Skip to content

Commit

Permalink
MDL-31968 Make NTLM REMOTE_USER format configurable by the admin
Browse files Browse the repository at this point in the history
Signed-off-by: Iñaki Arenaza <iarenaza@mondragon.edu>
  • Loading branch information
iarenaza committed Oct 2, 2012
1 parent 5d6285c commit 34b10e2
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 2 deletions.
79 changes: 77 additions & 2 deletions auth/ldap/auth.php
Expand Up @@ -41,6 +41,18 @@
define('AUTH_GID_NOGROUP', -2);
}

// Regular expressions for a valid NTLM username and domain name.
if (!defined('AUTH_NTLM_VALID_USERNAME')) {
define('AUTH_NTLM_VALID_USERNAME', '[^/\\\\\\\\\[\]:;|=,+*?<>@"]+');
}
if (!defined('AUTH_NTLM_VALID_DOMAINNAME')) {
define('AUTH_NTLM_VALID_DOMAINNAME', '[^\\\\\\\\\/:*?"<>|]+');
}
// Default format for remote users if using NTLM SSO
if (!defined('AUTH_NTLM_DEFAULT_FORMAT')) {
define('AUTH_NTLM_DEFAULT_FORMAT', '%domain%\\%username%');
}

require_once($CFG->libdir.'/authlib.php');
require_once($CFG->libdir.'/ldaplib.php');

Expand Down Expand Up @@ -1570,8 +1582,11 @@ function ntlmsso_magic($sesskey) {

switch ($this->config->ntlmsso_type) {
case 'ntlm':
// Format is DOMAIN\username
$username = substr(strrchr($username, '\\'), 1);
// The format is now configurable, so try to extract the username
$username = $this->get_ntlm_remote_user($username);
if (empty($username)) {
return false;
}
break;
case 'kerberos':
// Format is username@DOMAIN
Expand Down Expand Up @@ -1779,6 +1794,9 @@ function process_config($config) {
if (!isset($config->ntlmsso_type)) {
$config->ntlmsso_type = 'ntlm';
}
if (!isset($config->ntlmsso_remoteuserformat)) {
$config->ntlmsso_remoteuserformat = '';
}

// Try to remove duplicates before storing the contexts (to avoid problems in sync_users()).
$config->contexts = explode(';', $config->contexts);
Expand Down Expand Up @@ -1818,6 +1836,7 @@ function process_config($config) {
set_config('ntlmsso_subnet', trim($config->ntlmsso_subnet), $this->pluginconfig);
set_config('ntlmsso_ie_fastpath', (int)$config->ntlmsso_ie_fastpath, $this->pluginconfig);
set_config('ntlmsso_type', $config->ntlmsso_type, 'auth/ldap');
set_config('ntlmsso_remoteuserformat', trim($config->ntlmsso_remoteuserformat), 'auth/ldap');

return true;
}
Expand Down Expand Up @@ -2018,4 +2037,60 @@ function ldap_find_userdn($ldapconnection, $extusername) {
$this->config->user_attribute, $this->config->search_sub);
}


/**
* A chance to validate form data, and last chance to do stuff
* before it is inserted in config_plugin
*
* @param object object with submitted configuration settings (without system magic quotes)
* @param array $err array of error messages (passed by reference)
*/
function validate_form($form, &$err) {
if ($form->ntlmsso_type == 'ntlm') {
$format = trim($form->ntlmsso_remoteuserformat);
if (!empty($format) && !preg_match('/%username%/i', $format)) {
$err['ntlmsso_remoteuserformat'] = get_string('auth_ntlmsso_missing_username', 'auth_ldap');
}
}
}


/**
* When using NTLM SSO, the format of the remote username we get in
* $_SERVER['REMOTE_USER'] may vary, depending on where from and how the web
* server gets the data. So we let the admin configure the format using two
* place holders (%domain% and %username%). This function tries to extract
* the username (stripping the domain part and any separators if they are
* present) from the value present in $_SERVER['REMOTE_USER'], using the
* configured format.
*
* @param string $remoteuser The value from $_SERVER['REMOTE_USER'] (converted to UTF-8)
*
* @return string The remote username (without domain part or
* separators). Empty string if we can't extract the username.
*/
protected function get_ntlm_remote_user($remoteuser) {
if (empty($this->config->ntlmsso_remoteuserformat)) {
$format = AUTH_NTLM_DEFAULT_FORMAT;
} else {
$format = $this->config->ntlmsso_remoteuserformat;
}

$format = preg_quote($format);
$formatregex = preg_replace(array('#%domain%#', '#%username%#'),
array('('.AUTH_NTLM_VALID_DOMAINNAME.')', '('.AUTH_NTLM_VALID_USERNAME.')'),
$format);
if (preg_match('#^'.$formatregex.'$#', $remoteuser, $matches)) {
$user = end($matches);
return $user;
}

/* We are unable to extract the username with the configured format. Probably
* the format specified is wrong, so log a warning for the admin and return
* an empty username.
*/
error_log($this->errorlogtag.get_string ('auth_ntlmsso_maybeinvalidformat', 'auth_ldap'));
return '';
}

} // End of the class
15 changes: 15 additions & 0 deletions auth/ldap/config.html
Expand Up @@ -94,6 +94,9 @@
if (!isset($config->ntlmsso_type)) {
$config->ntlmsso_type = 'ntlm';
}
if (!isset($config->ntlmsso_remoteuserformat)) {
$config->ntlmsso_remoteuserformat = '';
}

$yesno = array(get_string('no'), get_string('yes'));

Expand Down Expand Up @@ -539,6 +542,18 @@ <h4><?php print_string('auth_ntlmsso', 'auth_ldap') ?></h4>
<?php print_string('auth_ntlmsso_type','auth_ldap') ?>
</td>
</tr>
<tr valign="top">
<td align="right">
<label for="ntlmsso_remoteuserformat"><?php print_string('auth_ntlmsso_remoteuserformat_key', 'auth_ldap') ?></label>
</td>
<td>
<input name="ntlmsso_remoteuserformat" id="ntlmsso_remoteuserformat" type="text" size="30" value="<?php echo $config->ntlmsso_remoteuserformat?>" />
<?php if (isset($err['ntlmsso_remoteuserformat'])) { echo $OUTPUT->error_text($err['ntlmsso_remoteuserformat']); } ?>
</td>
<td>
<?php print_string('auth_ntlmsso_remoteuserformat', 'auth_ldap') ?>
</td>
</tr>
<?php
$help = get_string('auth_ldapextrafields', 'auth_ldap');
$help .= get_string('auth_updatelocal_expl', 'auth');
Expand Down
4 changes: 4 additions & 0 deletions auth/ldap/lang/en/auth_ldap.php
Expand Up @@ -101,6 +101,10 @@
$string['auth_ntlmsso_enabled_key'] = 'Enable';
$string['auth_ntlmsso_ie_fastpath'] = 'Set to yes to enable the NTLM SSO fast path (bypasses certain steps and only works if the client\'s browser is MS Internet Explorer).';
$string['auth_ntlmsso_ie_fastpath_key'] = 'MS IE fast path?';
$string['auth_ntlmsso_maybeinvalidformat'] = 'Unable to extract the username from the REMOTE_USER header. Is the configured format right?';
$string['auth_ntlmsso_missing_username'] = 'You need to specify at least %username% in the remote username format';
$string['auth_ntlmsso_remoteuserformat_key'] = 'Remote username format';
$string['auth_ntlmsso_remoteuserformat'] = 'If you have chosen \'NTLM\' in \'Authentication type\', you can specify the remote username format here. If you leave this empty, the default DOMAIN\\username format will be used. You can use the optional <b>%domain%</b> placeholder to specify where the domain name appears, and the mandatory <b>%username%</b> placeholder to specify where the username appears. <br /><br />Some widely used formats are <tt>%domain%\\%username%</tt> (MS Windows default), <tt>%domain%/%username%</tt>, <tt>%domain%+%username%</tt> and just <tt>%username%</tt> (if there is no domain part).';
$string['auth_ntlmsso_subnet'] = 'If set, it will only attempt SSO with clients in this subnet. Format: xxx.xxx.xxx.xxx/bitmask. Separate multiple subnets with \',\' (comma).';
$string['auth_ntlmsso_subnet_key'] = 'Subnet';
$string['auth_ntlmsso_type_key'] = 'Authentication type';
Expand Down

0 comments on commit 34b10e2

Please sign in to comment.