Skip to content
Snippets Groups Projects
Commit 6b8b1d10 authored by Jaime Perez Crespo's avatar Jaime Perez Crespo
Browse files

Reformat ssp_negotiate_Auth_Source_Negotiate.

parent 4b207d20
No related branches found
No related tags found
No related merge requests found
<?php <?php
/** /**
* The Negotiate module. Allows for password-less, secure login by * The Negotiate module. Allows for password-less, secure login by Kerberos and Negotiate.
* Kerberos and Negotiate.
* *
* @author Mathias Meisfjordskar, University of Oslo. * @author Mathias Meisfjordskar, University of Oslo <mathias.meisfjordskar@usit.uio.no>
* <mathias.meisfjordskar@usit.uio.no> * @package SimpleSAMLphp
* @package simpleSAMLphp
*/ */
class sspmod_negotiate_Auth_Source_Negotiate extends SimpleSAML_Auth_Source
class sspmod_negotiate_Auth_Source_Negotiate extends SimpleSAML_Auth_Source { {
// Constants used in the module // Constants used in the module
const STAGEID = 'sspmod_negotiate_Auth_Source_Negotiate.StageId'; const STAGEID = 'sspmod_negotiate_Auth_Source_Negotiate.StageId';
private $config; protected $ldap = null;
protected $ldap = NULL; protected $backend = '';
protected $backend = ''; protected $hostname = '';
protected $hostname = ''; protected $port = 389;
protected $port = 389; protected $referrals = true;
protected $referrals = true; protected $enableTLS = false;
protected $enableTLS = false; protected $debugLDAP = false;
protected $debugLDAP = false; protected $timeout = 30;
protected $timeout = 30; protected $keytab = '';
protected $keytab = ''; protected $base = array();
protected $base = array(); protected $attr = 'uid';
protected $attr = 'uid'; protected $subnet = null;
protected $subnet = null; protected $admin_user = null;
protected $admin_user = null; protected $admin_pw = null;
protected $admin_pw = null; protected $attributes = null;
protected $attributes = null;
/** /**
* Constructor for this authentication source. * Constructor for this authentication source.
* *
* @param array $info Information about this authentication source. * @param array $info Information about this authentication source.
* @param array $config Configuration. * @param array $config The configuration of the module
*/ *
public function __construct($info, $config) { * @throws Exception If the KRB5 extension is not installed or active.
assert('is_array($info)'); */
assert('is_array($config)'); public function __construct($info, $config)
{
if(!extension_loaded('krb5')) assert('is_array($info)');
throw new Exception('KRB5 Extension not installed'); assert('is_array($config)');
// Call the parent constructor first, as required by the interface. if (!extension_loaded('krb5')) {
parent::__construct($info, $config); throw new Exception('KRB5 Extension not installed');
}
$config = SimpleSAML_Configuration::loadFromArray($config);;
// call the parent constructor first, as required by the interface
$this->backend = $config->getString('fallback'); parent::__construct($info, $config);
$this->hostname = $config->getString('hostname');
$this->port = $config->getInteger('port', 389); $config = SimpleSAML_Configuration::loadFromArray($config);
$this->referrals = $config->getBoolean('referrals', TRUE);
$this->enableTLS = $config->getBoolean('enable_tls', FALSE); $this->backend = $config->getString('fallback');
$this->debugLDAP = $config->getBoolean('debugLDAP', FALSE); $this->hostname = $config->getString('hostname');
$this->timeout = $config->getInteger('timeout', 30); $this->port = $config->getInteger('port', 389);
$this->keytab = $config->getString('keytab'); $this->referrals = $config->getBoolean('referrals', true);
$this->base = $config->getArrayizeString('base'); $this->enableTLS = $config->getBoolean('enable_tls', false);
$this->attr = $config->getString('attr', 'uid'); $this->debugLDAP = $config->getBoolean('debugLDAP', false);
$this->subnet = $config->getArray('subnet', NULL); $this->timeout = $config->getInteger('timeout', 30);
$this->admin_user = $config->getString('adminUser', NULL); $this->keytab = $config->getString('keytab');
$this->admin_pw = $config->getString('adminPassword', NULL); $this->base = $config->getArrayizeString('base');
$this->attributes = $config->getArray('attributes', NULL); $this->attr = $config->getString('attr', 'uid');
$this->subnet = $config->getArray('subnet', null);
} $this->admin_user = $config->getString('adminUser', null);
$this->admin_pw = $config->getString('adminPassword', null);
/** $this->attributes = $config->getArray('attributes', null);
* The inner workings of the module. }
*
* Checks to see if client is in the defined subnets (if
* defined in config). Sends the client a 401 Negotiate and /**
* responds to the result. If the client fails to provide a * The inner workings of the module.
* proper Kerberos ticket, the login process is handed over to *
* the 'fallback' module defined in the config. * Checks to see if client is in the defined subnets (if defined in config). Sends the client a 401 Negotiate and
* * responds to the result. If the client fails to provide a proper Kerberos ticket, the login process is handed over
* LDAP is used as a user metadata source. * to the 'fallback' module defined in the config.
* *
* @param array &$state Information about the current authentication. * LDAP is used as a user metadata source.
*/ *
public function authenticate(&$state) { * @param array &$state Information about the current authentication.
assert('is_array($state)'); */
public function authenticate(&$state)
// Set the default backend to config {
$state['LogoutState'] = array( assert('is_array($state)');
'negotiate:backend' => $this->backend,
); // set the default backend to config
$state['negotiate:authId'] = $this->authId; $state['LogoutState'] = array(
'negotiate:backend' => $this->backend,
);
// Check for disabled SPs. The disable flag is store in the SP $state['negotiate:authId'] = $this->authId;
// metadata.
if (array_key_exists('SPMetadata', $state) && $this->spDisabledInMetadata($state['SPMetadata']))
$this->fallBack($state); // check for disabled SPs. The disable flag is store in the SP metadata
// Go straight to fallback if Negotiate is disabled or if you are if (array_key_exists('SPMetadata', $state) && $this->spDisabledInMetadata($state['SPMetadata'])) {
// sent back to the IdP directly from the SP after having logged out $this->fallBack($state);
$session = SimpleSAML_Session::getSessionFromRequest(); }
$disabled = $session->getData('negotiate:disable', 'session'); /* Go straight to fallback if Negotiate is disabled or if you are sent back to the IdP directly from the SP
after having logged out. */
if ($disabled || $session = SimpleSAML_Session::getSessionFromRequest();
(!empty($_COOKIE['NEGOTIATE_AUTOLOGIN_DISABLE_PERMANENT']) && $disabled = $session->getData('negotiate:disable', 'session');
$_COOKIE['NEGOTIATE_AUTOLOGIN_DISABLE_PERMANENT'] == 'True')) {
SimpleSAML_Logger::debug('Negotiate - session disabled. falling back'); if ($disabled ||
$this->fallBack($state); (!empty($_COOKIE['NEGOTIATE_AUTOLOGIN_DISABLE_PERMANENT']) &&
// Never executed $_COOKIE['NEGOTIATE_AUTOLOGIN_DISABLE_PERMANENT'] == 'True')
assert('FALSE'); ) {
} SimpleSAML_Logger::debug('Negotiate - session disabled. falling back');
$mask = $this->checkMask(); $this->fallBack($state);
if (!$mask) { // never executed
$this->fallBack($state); assert('FALSE');
// Never executed }
assert('FALSE'); $mask = $this->checkMask();
} if (!$mask) {
$this->fallBack($state);
SimpleSAML_Logger::debug('Negotiate - authenticate(): looking for Negotate'); // never executed
if (!empty($_SERVER['HTTP_AUTHORIZATION'])) { assert('FALSE');
SimpleSAML_Logger::debug('Negotiate - authenticate(): Negotate found'); }
$this->ldap = new SimpleSAML_Auth_LDAP($this->hostname, $this->enableTLS, $this->debugLDAP, $this->timeout, $this->port, $this->referrals);
SimpleSAML_Logger::debug('Negotiate - authenticate(): looking for Negotate');
list($mech, $data) = explode(' ', $_SERVER['HTTP_AUTHORIZATION'],2); if (!empty($_SERVER['HTTP_AUTHORIZATION'])) {
if(strtolower($mech) == 'basic') SimpleSAML_Logger::debug('Negotiate - authenticate(): Negotate found');
SimpleSAML_Logger::debug('Negotiate - authenticate(): Basic found. Skipping.'); $this->ldap = new SimpleSAML_Auth_LDAP(
else if(strtolower($mech) != 'negotiate') $this->hostname,
SimpleSAML_Logger::debug('Negotiate - authenticate(): No "Negotiate" found. Skipping.'); $this->enableTLS,
$this->debugLDAP,
$auth = new KRB5NegotiateAuth($this->keytab); $this->timeout,
// Atempt Kerberos authentication $this->port,
try { $this->referrals
$reply = $auth->doAuthentication(); );
} catch (Exception $e) {
SimpleSAML_Logger::error('Negotiate - authenticate(): doAuthentication() exception: '. $e->getMessage()); list($mech, $data) = explode(' ', $_SERVER['HTTP_AUTHORIZATION'], 2);
$reply = NULL; if (strtolower($mech) == 'basic') {
} SimpleSAML_Logger::debug('Negotiate - authenticate(): Basic found. Skipping.');
} else {
if($reply) { if (strtolower($mech) != 'negotiate') {
// Success. Krb TGS recieved. SimpleSAML_Logger::debug('Negotiate - authenticate(): No "Negotiate" found. Skipping.');
$user = $auth->getAuthenticatedUser(); }
SimpleSAML_Logger::info('Negotiate - authenticate(): '. $user . ' authenticated.'); }
$lookup = $this->lookupUserData($user);
if ($lookup) { $auth = new KRB5NegotiateAuth($this->keytab);
$state['Attributes'] = $lookup; // attempt Kerberos authentication
// Override the backend so logout will know what to look for. try {
$state['LogoutState'] = array( $reply = $auth->doAuthentication();
'negotiate:backend' => NULL, } catch (Exception $e) {
); SimpleSAML_Logger::error('Negotiate - authenticate(): doAuthentication() exception: '.$e->getMessage());
SimpleSAML_Logger::info('Negotiate - authenticate(): '. $user . ' authorized.'); $reply = null;
SimpleSAML_Auth_Source::completeAuth($state); }
// Never reached.
assert('FALSE'); if ($reply) {
} // success! krb TGS received
} else { $user = $auth->getAuthenticatedUser();
// Some error in the recieved ticket. Expired? SimpleSAML_Logger::info('Negotiate - authenticate(): '.$user.' authenticated.');
SimpleSAML_Logger::info('Negotiate - authenticate(): Kerberos authN failed. Skipping.'); $lookup = $this->lookupUserData($user);
} if ($lookup) {
} else { $state['Attributes'] = $lookup;
// No auth token. Send it. // Override the backend so logout will know what to look for.
SimpleSAML_Logger::debug('Negotiate - authenticate(): Sending Negotiate.'); $state['LogoutState'] = array(
// Save the $state array, so that we can restore if after a redirect 'negotiate:backend' => null,
SimpleSAML_Logger::debug('Negotiate - fallback: '.$state['LogoutState']['negotiate:backend']); );
$id = SimpleSAML_Auth_State::saveState($state, self::STAGEID); SimpleSAML_Logger::info('Negotiate - authenticate(): '.$user.' authorized.');
$params = array('AuthState' => $id); SimpleSAML_Auth_Source::completeAuth($state);
// Never reached.
$this->sendNegotiate($params); assert('FALSE');
exit; }
} } else {
// Some error in the recieved ticket. Expired?
SimpleSAML_Logger::info('Negotiate - authenticate(): Client failed Negotiate. Falling back'); SimpleSAML_Logger::info('Negotiate - authenticate(): Kerberos authN failed. Skipping.');
$this->fallBack($state); }
/* The previous function never returns, so this code is never } else {
executed */ // No auth token. Send it.
assert('FALSE'); SimpleSAML_Logger::debug('Negotiate - authenticate(): Sending Negotiate.');
} // Save the $state array, so that we can restore if after a redirect
SimpleSAML_Logger::debug('Negotiate - fallback: '.$state['LogoutState']['negotiate:backend']);
public function spDisabledInMetadata($spMetadata) { $id = SimpleSAML_Auth_State::saveState($state, self::STAGEID);
if (array_key_exists('negotiate:disable', $spMetadata)) { $params = array('AuthState' => $id);
if ($spMetadata['negotiate:disable'] == TRUE) {
SimpleSAML_Logger::debug('Negotiate - SP disabled. falling back'); $this->sendNegotiate($params);
return true; exit;
} else { }
SimpleSAML_Logger::debug('Negotiate - SP disable flag found but set to FALSE');
} SimpleSAML_Logger::info('Negotiate - authenticate(): Client failed Negotiate. Falling back');
} else { $this->fallBack($state);
SimpleSAML_Logger::debug('Negotiate - SP disable flag not found'); /* The previous function never returns, so this code is never
} executed */
return False; assert('FALSE');
} }
/**
* checkMask() looks up the subnet config option and verifies public function spDisabledInMetadata($spMetadata)
* that the client is within that range. {
* if (array_key_exists('negotiate:disable', $spMetadata)) {
* Will return TRUE if no subnet option is configured. if ($spMetadata['negotiate:disable'] == true) {
* SimpleSAML_Logger::debug('Negotiate - SP disabled. falling back');
* @return boolean return true;
*/ } else {
public function checkMask() { SimpleSAML_Logger::debug('Negotiate - SP disable flag found but set to FALSE');
// No subnet means all clients are accepted. }
if ($this->subnet === NULL) } else {
return TRUE; SimpleSAML_Logger::debug('Negotiate - SP disable flag not found');
$ip = $_SERVER['REMOTE_ADDR']; }
foreach ($this->subnet as $cidr) { return false;
$ret = SimpleSAML\Utils\Net::ipCIDRcheck($cidr); }
if ($ret) {
SimpleSAML_Logger::debug('Negotiate: Client "'.$ip.'" matched subnet.');
return TRUE; /**
} * checkMask() looks up the subnet config option and verifies
} * that the client is within that range.
SimpleSAML_Logger::debug('Negotiate: Client "'.$ip.'" did not match subnet.'); *
return FALSE; * Will return TRUE if no subnet option is configured.
} *
* @return boolean
/** */
* Send the actual headers and body of the 401. Embedded in public function checkMask()
* the body is a post that is triggered by JS if the client {
* wants to show the 401 message. // No subnet means all clients are accepted.
* if ($this->subnet === null) {
* @param array $params additional parameters to the URL in return true;
* the URL in the body }
*/ $ip = $_SERVER['REMOTE_ADDR'];
protected function sendNegotiate($params) { foreach ($this->subnet as $cidr) {
$url = SimpleSAML_Module::getModuleURL('negotiate/backend.php', $params); $ret = SimpleSAML\Utils\Net::ipCIDRcheck($cidr);
if ($ret) {
header('HTTP/1.1 401 Unauthorized'); SimpleSAML_Logger::debug('Negotiate: Client "'.$ip.'" matched subnet.');
header('WWW-Authenticate: Negotiate',false); return true;
echo ' }
}
SimpleSAML_Logger::debug('Negotiate: Client "'.$ip.'" did not match subnet.');
return false;
}
/**
* Send the actual headers and body of the 401. Embedded in the body is a post that is triggered by JS if the client
* wants to show the 401 message.
*
* @param array $params additional parameters to the URL in the URL in the body.
*/
protected function sendNegotiate($params)
{
$url = htmlspecialchars(SimpleSAML_Module::getModuleURL('negotiate/backend.php', $params));
$json_url = json_encode($url);
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Negotiate', false);
echo <<<EOF
<html> <html>
<head> <head>
<script type="text/javascript">window.location = '.json_encode(htmlspecialchars($url)).'</script> <script type="text/javascript">window.location = $json_url</script>
<title>Redirect to login</title> <title>Redirect to login</title>
</head> </head>
<body> <body>
Your browser seems to have Javascript disabled. Please <p>Your browser seems to have Javascript disabled. Please click <a href="$url">here</a>.</p>
click <a href="'.htmlspecialchars($url).'">here</a>.
</body> </body>
</html> '; </html>
EOF;
} }
/**
* Passes control of the login process to a different module. /**
* * Passes control of the login process to a different module.
* @param string $state Information about the current authentication. *
*/ * @param string $state Information about the current authentication.
public static function fallBack(&$state) { *
$authId = $state['LogoutState']['negotiate:backend']; * @throws SimpleSAML_Error_Error If couldn't determine the auth source.
* @throws SimpleSAML_Error_Exception
if ($authId === NULL) { * @throws Exception
$msg = "This code should never be reached."; */
throw new SimpleSAML_Error_AuthSource($msg); public static function fallBack(&$state)
} {
$source = SimpleSAML_Auth_Source::getById($authId); $authId = $state['LogoutState']['negotiate:backend'];
try { if ($authId === null) {
$source->authenticate($state); throw new SimpleSAML_Error_Error(500, "Unable to determine auth source.");
} catch (SimpleSAML_Error_Exception $e) { }
SimpleSAML_Auth_State::throwException($state, $e); $source = SimpleSAML_Auth_Source::getById($authId);
} catch (Exception $e) {
$e = new SimpleSAML_Error_UnserializableException($e); try {
SimpleSAML_Auth_State::throwException($state, $e); $source->authenticate($state);
} } catch (SimpleSAML_Error_Exception $e) {
// fallBack never returns after loginCompleted() SimpleSAML_Auth_State::throwException($state, $e);
SimpleSAML_Logger::debug('Negotiate: backend returned'); } catch (Exception $e) {
self::loginCompleted($state); $e = new SimpleSAML_Error_UnserializableException($e);
} SimpleSAML_Auth_State::throwException($state, $e);
}
/** // fallBack never returns after loginCompleted()
* Strips away the realm of the Kerberos identifier, looks up SimpleSAML_Logger::debug('Negotiate: backend returned');
* what attributes to fetch from SP metadata and searches the self::loginCompleted($state);
* directory. }
*
* @param string $user The Kerberos user identifier
* @return string The DN to the user or NULL if not found /**
*/ * Strips away the realm of the Kerberos identifier, looks up what attributes to fetch from SP metadata and
protected function lookupUserData($user) { * searches the directory.
// Kerberos usernames include realm. Strip that away. *
$pos = strpos($user, '@'); * @param string $user The Kerberos user identifier.
if ($pos === false) *
return NULL; * @return string The DN to the user or NULL if not found.
$uid = substr($user, 0, $pos); */
protected function lookupUserData($user)
$this->adminBind(); {
try { // Kerberos user names include realm. Strip that away.
$dn = $this->ldap->searchfordn($this->base, $this->attr, $uid); $pos = strpos($user, '@');
return $this->ldap->getAttributes($dn, $this->attributes); if ($pos === false) {
} catch (SimpleSAML_Error_Exception $e) { return null;
SimpleSAML_Logger::debug('Negotiate - ldap lookup failed: '. $e); }
return NULL; $uid = substr($user, 0, $pos);
}
} $this->adminBind();
try {
/** $dn = $this->ldap->searchfordn($this->base, $this->attr, $uid);
* Elevates the LDAP connection to allow restricted lookups if return $this->ldap->getAttributes($dn, $this->attributes);
* so configured. Does nothing if not. } catch (SimpleSAML_Error_Exception $e) {
*/ SimpleSAML_Logger::debug('Negotiate - ldap lookup failed: '.$e);
protected function adminBind() { return null;
if ($this->admin_user === NULL) { }
// No admin user. }
return;
}
SimpleSAML_Logger::debug('Negotiate - authenticate(): Binding as system user ' . var_export($this->admin_user, TRUE)); /**
* Elevates the LDAP connection to allow restricted lookups if
if(!$this->ldap->bind($this->admin_user, $this->admin_pw)){ * so configured. Does nothing if not.
$msg = 'Unable to authenticate system user (LDAP_INVALID_CREDENTIALS) ' . var_export($this->admin_user, TRUE); */
SimpleSAML_Logger::error('Negotiate - authenticate(): ' . $msg); protected function adminBind()
throw new SimpleSAML_Error_AuthSource($msg); {
} if ($this->admin_user === null) {
} // no admin user
return;
/** }
* Log out from this authentication source. SimpleSAML_Logger::debug(
* 'Negotiate - authenticate(): Binding as system user '.var_export($this->admin_user, true)
* This method either logs the user out from Negotiate or passes the );
* logout call to the fallback module.
* if (!$this->ldap->bind($this->admin_user, $this->admin_pw)) {
* @param array &$state Information about the current logout operation. $msg = 'Unable to authenticate system user (LDAP_INVALID_CREDENTIALS) '.var_export($this->admin_user, true);
*/ SimpleSAML_Logger::error('Negotiate - authenticate(): '.$msg);
public function logout(&$state) { throw new SimpleSAML_Error_AuthSource($msg);
assert('is_array($state)'); }
/* Get the source that was used to authenticate */ }
$authId = $state['negotiate:backend'];
SimpleSAML_Logger::debug('Negotiate - logout has the following authId: "'.$authId.'"');
/**
if ($authId === NULL) { * Log out from this authentication source.
$session = SimpleSAML_Session::getSessionFromRequest(); *
$session->setData('negotiate:disable', 'session', TRUE, 24*60*60); * This method either logs the user out from Negotiate or passes the
parent::logout($state); * logout call to the fallback module.
} else { *
$source = SimpleSAML_Auth_Source::getById($authId); * @param array &$state Information about the current logout operation.
$source->logout($state); */
} public function logout(&$state)
} {
assert('is_array($state)');
// get the source that was used to authenticate
$authId = $state['negotiate:backend'];
SimpleSAML_Logger::debug('Negotiate - logout has the following authId: "'.$authId.'"');
if ($authId === null) {
$session = SimpleSAML_Session::getSessionFromRequest();
$session->setData('negotiate:disable', 'session', true, 24 * 60 * 60);
parent::logout($state);
} else {
$source = SimpleSAML_Auth_Source::getById($authId);
$source->logout($state);
}
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment