Revision: 3038
Author: olavmrk
Date: Thu Mar 8 06:08:03 2012
Log: negotiate: A new module for Kerberos authentication
This is a module that implements support for Microsofts Kerberos
SPNEGO mechanism.
Thanks to Mathias Meisfjordskar for implementing this module!
http://code.google.com/p/simplesamlphp/source/detail?r=3038
Added:
/trunk/modules/negotiate
/trunk/modules/negotiate/dictionaries
/trunk/modules/negotiate/dictionaries/negotiate.definition.json
/trunk/modules/negotiate/dictionaries/negotiate.translation.json
/trunk/modules/negotiate/docs
/trunk/modules/negotiate/docs/negotiate.txt
/trunk/modules/negotiate/lib
/trunk/modules/negotiate/lib/Auth
/trunk/modules/negotiate/lib/Auth/Source
/trunk/modules/negotiate/lib/Auth/Source/Negotiate.php
/trunk/modules/negotiate/templates
/trunk/modules/negotiate/templates/disable.php
/trunk/modules/negotiate/templates/enable.php
/trunk/modules/negotiate/www
/trunk/modules/negotiate/www/backend.php
/trunk/modules/negotiate/www/disable.php
/trunk/modules/negotiate/www/enable.php
/trunk/modules/negotiate/www/retry.php
=======================================
--- /dev/null
+++ /trunk/modules/negotiate/dictionaries/negotiate.definition.json Thu
Mar 8 06:08:03 2012
@@ -0,0 +1,17 @@
+{
+ "disable_title": {
+ "en": "Turned off ability to use automatic login for this
browser"
+ },
+ "disable_info_pre": {
+ "en": "<p>By accessing this page, you have turned off the ability to use
automatic login for this browser.<\/p><p>To turn it on again, please visit
<a href='URL'>this page<\/a>.<\/p>"
+ },
+ "enable_title": {
+ "en": "Turned on the ability to use automatic login for this
browser"
+ },
+ "enable_info_pre": {
+ "en": "<p>By accessing this page, you have turned on the ability to use
automatic login for this browser.<\/p><p>To turn it off again, please visit
<a href='URL'>this page<\/a>.<\/p>"
+ },
+ "info_post": {
+ "en": "<h3>What is automatic login<\/h3><p>Automatic login allows you to
log in without being asked for username and password:<ul><li>if the
computer is in the Kerberos domain<\/li><li>using a supported browser (IE
and some others depending on installation options)<\/li><li>on a supported
operating system (Windows, Mac)<\/li><\/ul><\/p>"
+ }
+}
=======================================
--- /dev/null
+++ /trunk/modules/negotiate/dictionaries/negotiate.translation.json Thu
Mar 8 06:08:03 2012
@@ -0,0 +1,22 @@
+{
+ "disable_title": {
+ "no": "Skrudd av mulighet for automatisk innlogging for denne
nettleseren",
+ "nn": "Skrudd av moglegheit for automatisk innlogging for denne
nettlesaren"
+ },
+ "disable_info_pre": {
+ "no": "<p>Ved \u00e5 g\u00e5 til denne siden har du skrudd av muligheten
for automatisk innlogging for denne nettleseren.<\/p><p>For \u00e5 skru
p\u00e5 igjen muligheten, kan du g\u00e5 til <a href='URL'>denne
siden<\/a>.<\/p>",
+ "nn": "<p>Ved \u00e5 g\u00e5 til denne siden har du skrudd av moglegheit
for automatisk innlogging for denne nettlesaren.<\/p><p>For \u00e5 skru
p\u00e5 igjen moglegheita, kan du g\u00e5 til <a href='URL'>denne
siden<\/a>.<\/p>"
+ },
+ "enable_title": {
+ "no": "Skrudd p\u00e5 mulighet for automatisk innlogging for denne
nettleseren",
+ "nn": "Skrudd p\u00e5 moglegheit for automatisk innlogging for denne
nettlesaren"
+ },
+ "enable_info_pre": {
+ "no": "<p>Ved \u00e5 g\u00e5 til denne siden har du skrudd p\u00e5
muligheten for automatisk innlogging for denne nettleseren.<\/p><p>For
\u00e5 skru av igjen muligheten, kan du g\u00e5 til <a href='URL'>denne
siden<\/a>.<\/p>",
+ "nn": "<p>Ved \u00e5 g\u00e5 til denne siden har du skrudd p\u00e5
moglegheit for automatisk innlogging for denne nettlesaren.<\/p><p>For
\u00e5 skru av igjen moglegheita, kan du g\u00e5 til <a href='URL'>denne
siden<\/a>.<\/p>"
+ },
+ "info_post": {
+ "no": "<h3>Hva er automatisk innlogging<\/h3><\/p><p>Automatisk
innlogging gj\u00f8r at man kan logge inn uten \u00e5 bli bedt om
brukernavn og passord i noen tilfeller:<ul><li>hvis maskinen er i Kerberos
domenet<\/li><li>og hvis man bruker en st\u00f8ttet nettleser (IE og noen
flere avhengig av oppsett)<\/li><li>p\u00e5 et st\u00f8ttet operativsystem
(Windows, Mac)<\/li><\/ul><\/p>",
+ "nn": "<h3>Kva er automatisk innlogging<\/h3><\/p><p>Automatisk
innlogging gj\u00f8r at man kan logge inn uten \u00e5 bli bedt om
brukernavn og passord i noen tilfeller:<ul><li>hvis maskinen er i Kerberos
domenet<\/li><li>og hvis man bruker en st\u00f8ttet nettleser (IE og noen
flere avhengig av oppsett)<\/li><li>p\u00e5 et st\u00f8ttet operativsystem
(Windows, Mac)<\/li><\/ul><\/p>"
+ }
+}
=======================================
--- /dev/null
+++ /trunk/modules/negotiate/docs/negotiate.txt Thu Mar 8 06:08:03 2012
@@ -0,0 +1,279 @@
+Negotiate module
+================
+
+The Negotiate module implements Microsofts Kerberos SPNEGO mechanism.
+It is intended to only support Kerberos and not NTLM which RFC4559
+implements.
+
+`negotiate:Negotiate`
+: Authenticates users via HTTP authentication
+
+`negotiate:Negotiate`
+---------------------
+
+Negotiate implements the following mechanics:
+
+ * Initiate HTTP_AUTHN with the client
+ * Authorize user against a LDAP directory
+ * Collect metadata from LDAP directory
+ * Fall back to other SimpleSamlPhp module for any client/user that
+ fails to authenticate in the Negotiate module
+ * Check only clients from a certain subnet
+ * Supports enabling/disabling a client
+
+In effect this module aims to extend the Microsoft AD SSO session to
+the SAML IdP. (Or any other Kerberos domain) It doesn't work like this
+of course but for the user the client is automatically authenticated
+when an SP sends the client to the IdP. In reality Negotiate
+authenticates the user via SPNEGO and issues a separate SAML session.
+The Kerberos session against the Authentication Server is completely
+separate from the SAML session with the IdP. The only time the
+Kerberos session affects the SAML session is at authN at the IdP.
+
+The module is meant to supplement existing auth modules and not
+replace them. Users do not always log in on the IdP from a machine in
+the Windows domain (or another Kerberos domain) and from their own
+domain accounts. A fallback mechanism must be supplemented.
+
+The Kerberos TGS can be issued for a wide variety of accounts so an
+authoriation backend via LDAP is needed. If the search, with filters,
+fails, the fallback in invoked. This to prevent kiosk accounts and the
+likes to get faulty SAML sessions.
+
+The subnet is required to prevent excess attempts to authenticate via
+Kerberos for clients that always will fail. Worst case scenario the
+browser will prompt the user for u/p in a popup box that will always
+fail. Only when the user clicks cancel the proper login process will
+continue. This is handled through the body of the 401 message the
+client recieves with the Negotiate request. In the body a URL to the
+fallback mechanism is supplied and Javascript is used to redirect the
+client.
+
+All configuration is handled in authsources.php:
+
+ 'weblogin' => array(
+ 'negotiate:Negotiate',
+ 'keytab' => '/path/to/keytab-file',
+ 'fallback' => 'ldap',
+ 'hostname' => 'ldap.example.com',
+ 'base' => 'cn=people,dc=example,dc=com',
+ 'adminUser'
=> 'cn=idp-fallback,cn=services,dc=example,dc=com',
+ 'adminPassword' => 'VerySecretPassphraseHush'
+ ),
+ 'ldap' => array(
+ 'ldap:LDAP',
+ 'hostname' => 'ldap.example.com',
+ 'enable_tls' => TRUE,
+ 'dnpattern' => 'uid=%username%,cn=people,dc=example,dc=com',
+ 'search.enable' => FALSE
+ ),
+
+
+
+`php_krb5`
+++++++++++
+
+The processing involving the actual Kerberos ticket handling is done
+by php_krb5. The package is not yet labeled stable but has worked well
+during testing.
+
+NOTE! php_krb5 hardcodes the service name in the keytab file to 'HTTP'
+as of php_krb5-1.0rc2. To change this you need to edit the module code.
+Be wary of how much space is allocated to the string in
+negotiate_auth.c:101.
+
+Depending on you apache config you may need a rewrite rule to allow
+php_krb5 to read the HTTP_AUTHORIZATION header:
+
+ RewriteEngine on
+ RewriteCond %{HTTP:Authorization} !^$
+ RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
+
+
+Test the Kerberos setup with the following script:
+
+ <?php
+ if(!extension_loaded('krb5')) {
+ die('KRB5 Extension not installed');
+ }
+
+ if(!empty($_SERVER['HTTP_AUTHORIZATION'])) {
+ list($mech, $data) = explode(' ',
$_SERVER['HTTP_AUTHORIZATION']);
+ if(strtolower($mech) == 'basic') {
+ echo "Client sent basic";
+ die('Unsupported request');
+ } else if(strtolower($mech) != 'negotiate') {
+ echo "Couldn't find negotiate";
+ die('Unsupported request');
+ }
+ $auth = new KRB5NegotiateAuth('/path/to/keytab');
+ $reply = '';
+ if($reply = $auth->doAuthentication()) {
+ header('HTTP/1.1 200 Success');
+ echo 'Success - authenticated as ' .
$auth->getAuthenticatedUser() . '<br>';
+ } else {
+ echo 'Failed to authN.';
+ die();
+ }
+ } else {
+ header('HTTP/1.1 401 Unauthorized');
+ header('WWW-Authenticate: Negotiate',false);
+ echo 'Not authenticated. No HTTP_AUTHORIZATION available.';
+ echo 'Check headers sent by the browser and verify that ';
+ echo 'apache passes them to PHP';
+ }
+ ?>
+
+
+
+`LDAP`
+++++++
+
+LDAP is used to verify the user due to the lack of metadata in
+Kerberos. A domain can contain lots of kiosk users, non-personal
+accounts and the likes. The LDAP lookup will authorize and fetch
+attributes as defined by SimpleSamlPhp metadata.
+
+'hostname', 'enable_tls', 'debugLDAP', 'timeout' and 'base' are
+self-explanatory. Read the documentation of the LDAP auth module for
+more information. 'attr' is the attribute that will be used to look up
+user objects in the directory after extracting it from the Kerberos
+session. Default is 'uid'.
+
+For LDAP directories with restricted access to objects or attributes
+Negotiate implements 'adminUser' and 'adminPassword'. adminUser must
+be a DN to an object with access to search for all relevant user
+objects and to look up attributes needed by the SP.
+
+
+`Subnet filtering`
+++++++++++++++++++
+
+Subnet is meant to filter which clients you subject to the
+WWW-Authenticate request.
+
+Syntax is:
+
+ 'subnet' => array('127.0.0.0/16','192.168.0.0/16'),
+
+Browsers, especially IE, behave erratically when they encounter a
+WWW-Authenticate from the webserver. Included in RFC4559 Negotiate is
+NTLM authentication which IE seems prone to fall back to under various
+conditions. This triggers a popup login box which defeats the whole
+purpose of this module.
+
+TBD: Replace or supplement with LDAP lookups in the domain. Machines
+currently in the domain should be the only ones that are promted with
+WWW-Authenticate: Negotiate.
+
+
+`Enabling/disabling Negotiate from a web browser`
++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Included in Negotiate are semi-static web pages for enabling and
+disabling Negotiate for any given client. The pages simple set/deletes
+a cookie that Negotiate will look for when a client attempts AuthN.
+The help text in the JSON files should be locally overwritten to fully
+explain which clients are accepted by Negotiate.
+
+
+`Logout/Login loop and reauthenticating`
+++++++++++++++++++++++++++++++++++++++++
+
+Due to the automatic AuthN of certain clients and how SPs will
+automatically redirect clients to the IdP when clients try to access
+restricted content, a session variable has been put into Negotiate. This
+variable makes sure Negotiate doesn't reautenticate a recently logged
+out user. The consequence of this is that the user will be presented
+with the login mechanism of the fallback module specified in Negotiate
+config.
+
+SimpleSamlPhp offers no decent way of adding hooks or piggyback this
+information to the fallback module. In future releases one might add a
+box of information to the user explaining what's happening.
+
+One can add this bit of code to the template in the fallback AuthN
+module:
+
+// This should be placed in your www script
+$nego_session = FALSE;
+$nego_perm = FALSE;
+$nego_retry = NULL;
+if (array_key_exists('negotiate:authId', $state)) {
+ $nego = SimpleSAML_Auth_Source::getById($state['negotiate:authId']);
+ $mask = $nego->checkMask();
+ $disabled = $nego->spDisabledInMetadata($spMetadata);
+ $session_disabled = $session->getData('negotiate:disable', 'session');
+ if ($mask and !$disabled) {
+ if(array_key_exists('NEGOTIATE_AUTOLOGIN_DISABLE_PERMANENT',
$_COOKIE) &&
+ $_COOKIE['NEGOTIATE_AUTOLOGIN_DISABLE_PERMANENT'] == 'True') {
+ $nego_perm = TRUE;
+ } elseif ($session_disabled) {
+ $retryState = SimpleSAML_Auth_State::cloneState($state);
+ unset($retryState[SimpleSAML_Auth_State::ID]);
+ $nego_retry =
SimpleSAML_Auth_State::saveState($retryState, 'sspmod_negotiate_Auth_Source_Negotiate.StageId');
+ $nego_session = TRUE;
+ }
+ }
+}
+
+// This should reside in your template
+if($this->data['nego']['disable_perm']) {
+ echo '<span id="login-extra-info-uio.no" class="login-extra-info">'
+ . '<span class="login-extra-info-divider"></span>'
+ .
$this->t('{feide:login:login_uio_negotiate_disabled_permanent_info}')
+ . '</span>';
+} elseif($this->data['nego']['disable_session']) {
+ echo '<span id="login-extra-info-uio.no" class="login-extra-info">'
+ . '<span class="login-extra-info-divider"></span>'
+ .
$this->t('{feide:login:login_uio_negotiate_disabled_session_info}')
+ . '<br><a
href="'.SimpleSAML_Module::getModuleURL('negotiate/retry.php',
array('AuthState' => $this->data['nego']['retry_id'])).'">'
+ .
$this->t('{feide:login:login_uio_negotiate_disabled_session_info_link}')
+ . '</a>'
+ . '</span>';
+}
+
+The above may or may not work right out of the box for you but it is
+the gist of it. By looking at the state variable, cookie and checking
+for filters and the likes, only clients that are subjected to
+Negotiate should get the help text.
+
+Note that with Negotiate there is also a small script to allow the
+user to re-authenticate with Negotiate after being sent to the
+fallback mechanism due to the session cookie. In the example above you
+can see the construction of the URL. The cloning of the current state
+is necessary for retry.php to load a state without triggering a
+security check in SSP's state handling library. If you omit this and
+pass on the original state you will see a warning in the log like
+this:
+
+ Sep 27 13:47:36 simplesamlphp WARNING [b99e6131ee] Wrong stage in
state. Was 'foo', should
be 'sspmod_negotiate_Auth_Source_Negotiate.StageId'.
+
+It will work as loadState will take controll and call
+Negotiate->authenticate() but remaining code in retry.php will be
+discarded. Other side-effects may occur.
+
+
+`Clients`
++++++++++
+
+* Internet Explorer
+
+YMMV but generally you need to have your IdP defined in "Internet
+Options" -> "Security" -> "Local intranet" -> "Sites" -> "Advanced".
+You also need "Internet Options" -> "Advanced" -> "Security" -> Enable
+Integrated Windows Authentication" enabled.
+
+* Firefox
+
+Open "about:config". Locate "network.auth.use-sspi" and verify that
+this is true (on a Windows machine). Next locate
+"network.negotiate-auth.trusted-uris" and insert your IdP.
+
+* Safari
+
+TODO
+
+* Chrome
+
+TODO
=======================================
--- /dev/null
+++ /trunk/modules/negotiate/lib/Auth/Source/Negotiate.php Thu Mar 8
06:08:03 2012
@@ -0,0 +1,321 @@
+<?php
+
+/**
+ * The Negotiate module. Allows for password-less, secure login by
+ * Kerberos and Negotiate.
+ *
+ * @author Mathias Meisfjordskar, University of Oslo.
+ * <mathias.meisfjords...@usit.uio.no>
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+
+class sspmod_negotiate_Auth_Source_Negotiate extends
SimpleSAML_Auth_Source {
+
+ // Constants used in the module
+ const STAGEID = 'sspmod_negotiate_Auth_Source_Negotiate.StageId';
+
+ private $config;
+ protected $ldap = NULL;
+
+ /**
+ * Constructor for this authentication source.
+ *
+ * @param array $info Information about this authentication source.
+ * @param array $config Configuration.
+ */
+ public function __construct($info, $config) {
+ assert('is_array($info)');
+ assert('is_array($config)');
+
+ if(!extension_loaded('krb5'))
+ throw new Exception('KRB5 Extension not installed');
+
+ // Call the parent constructor first, as required by the
interface.
+ parent::__construct($info, $config);
+
+ $config = SimpleSAML_Configuration::loadFromArray($config);;
+
+ $this->backend = $config->getString('fallback');
+ $this->hostname = $config->getString('hostname');
+ $this->enableTLS = $config->getBoolean('enable_tls', FALSE);
+ $this->debugLDAP = $config->getBoolean('debugLDAP', FALSE);
+ $this->timeout = $config->getValue('timeout', 30);
+ $this->keytab = $config->getString('keytab');
+ $this->base = $config->getString('base');
+ $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);
+
+ }
+
+ /**
+ * 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
+ * proper Kerberos ticket, the login process is handed over to
+ * the 'fallback' module defined in the config.
+ *
+ * LDAP is used as a user metadata source.
+ *
+ * @param array &$state Information about the current
authentication.
+ */
+ public function authenticate(&$state) {
+ assert('is_array($state)');
+
+ // Set the default backend to config
+ $state['LogoutState'] = array(
+ 'negotiate:backend' => $this->backend,
+ );
+ $state['negotiate:authId'] = $this->authId;
+
+
+ // Check for disabled SPs. The disable flag is store in the SP
+ // metadata.
+ if (array_key_exists('SPMetadata', $state) and
$this->spDisabledInMetadata($state['SPMetadata']))
+ $this->fallBack($state);
+ // 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
+ $session = SimpleSAML_Session::getInstance();
+ $disabled = $session->getData('negotiate:disable', 'session');
+
+ if ($disabled or
+ (!empty($_COOKIE['NEGOTIATE_AUTOLOGIN_DISABLE_PERMANENT'])
and
+ $_COOKIE['NEGOTIATE_AUTOLOGIN_DISABLE_PERMANENT'] ==
'True')) {
+ SimpleSAML_Logger::debug('Negotiate - session disabled.
falling back');
+ $this->fallBack($state);
+ // Never executed
+ assert('FALSE');
+ }
+ $mask = $this->checkMask();
+ if (!$mask) {
+ $this->fallBack($state);
+ // Never executed
+ assert('FALSE');
+ }
+
+ SimpleSAML_Logger::debug('Negotiate - authenticate(): looking for
Negotate');
+ if (!empty($_SERVER['HTTP_AUTHORIZATION'])) {
+ SimpleSAML_Logger::debug('Negotiate - authenticate():
Negotate found');
+ $this->ldap = new SimpleSAML_Auth_LDAP($this->hostname,
$this->enableTLS, $this->debugLDAP, $this->timeout);
+
+ list($mech, $data) = explode(' ',
$_SERVER['HTTP_AUTHORIZATION'],2);
+ if(strtolower($mech) == 'basic')
+ SimpleSAML_Logger::debug('Negotiate - authenticate(): Basic found.
Skipping.');
+ else if(strtolower($mech) != 'negotiate')
+ SimpleSAML_Logger::debug('Negotiate - authenticate(): No "Negotiate"
found. Skipping.');
+
+ $auth = new KRB5NegotiateAuth($this->keytab);
+ // Atempt Kerberos authentication
+ try {
+ $reply = $auth->doAuthentication();
+ } catch (Exception $e) {
+ SimpleSAML_Logger::error('Negotiate - authenticate():
doAuthentication() exception: '. $e->getMessage());
+ $reply = NULL;
+ }
+
+ if($reply) {
+ // Success. Krb TGS recieved.
+ $user = $auth->getAuthenticatedUser();
+ SimpleSAML_Logger::info('Negotiate - authenticate(): '. $user . '
authenticated.');
+ $lookup = $this->lookupUserData($user);
+ if ($lookup) {
+ $state['Attributes'] = $lookup;
+ // Override the backend so logout will
know what to look for.
+ $state['LogoutState'] = array(
+ 'negotiate:backend' => NULL,
+ );
+ SimpleSAML_Logger::info('Negotiate - authenticate(): '. $user . '
authorized.');
+
SimpleSAML_Auth_Source::completeAuth($state);
+ // Never reached.
+ assert('FALSE');
+ }
+ } else {
+ // Some error in the recieved ticket. Expired?
+ SimpleSAML_Logger::info('Negotiate - authenticate(): Kerberos authN
failed. Skipping.');
+ }
+ } else {
+ // No auth token. Send it.
+ 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']);
+ $id = SimpleSAML_Auth_State::saveState($state,
self::STAGEID);
+ $params = array('AuthState' => $id);
+
+ $this->sendNegotiate($params);
+ exit;
+ }
+
+ SimpleSAML_Logger::info('Negotiate - authenticate(): Client failed
Negotiate. Falling back');
+ $this->fallBack($state);
+ /* The previous function never returns, so this code is never
+ executed */
+ assert('FALSE');
+ }
+
+ public function spDisabledInMetadata($spMetadata) {
+ if (array_key_exists('negotiate:disable', $spMetadata)) {
+ if ($spMetadata['negotiate:disable'] == TRUE) {
+ SimpleSAML_Logger::debug('Negotiate - SP
disabled. falling back');
+ return True;
+ // Never executed
+ assert('FALSE');
+ } else {
+ SimpleSAML_Logger::debug('Negotiate - SP disable flag found but set to
FALSE');
+ }
+ } else {
+ SimpleSAML_Logger::debug('Negotiate - SP disable flag
not found');
+ }
+ return False;
+ }
+
+ /**
+ * checkMask() looks up the subnet config option and verifies
+ * that the client is within that range.
+ *
+ * Will return TRUE if no subnet option is configured.
+ *
+ * @return boolean
+ */
+ public function checkMask() {
+ // No subnet means all clients are accepted.
+ if ($this->subnet === NULL)
+ return TRUE;
+ $ip = $_SERVER['REMOTE_ADDR'];
+ foreach ($this->subnet as $cidr) {
+ $ret = SimpleSAML_Utilities::ipCIDRcheck($cidr);
+ if ($ret) {
+ SimpleSAML_Logger::debug('Negotiate: Client "'.$ip.'" matched
subnet.');
+ return TRUE;
+ }
+ }
+ 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 = SimpleSAML_Module::getModuleURL('negotiate/backend.php',
$params);
+
+ header('HTTP/1.1 401 Unauthorized');
+ header('WWW-Authenticate: Negotiate',false);
+ echo '
+<html>
+<head>
+<script type="text/javascript">window.location
= '.json_encode(htmlspecialchars($url)).'</script>
+<title>Redirect to login</title>
+</head>
+<body>
+Your browser seems to have Javascript disabled. Please
+click <a href="'.htmlspecialchars($url).'">here</a>.
+</body>
+</html> ';
+
+ }
+
+ /**
+ * Passes control of the login process to a different module.
+ *
+ * @param string $state Information about the current authentication.
+ */
+ public static function fallBack(&$state) {
+ $authId = $state['LogoutState']['negotiate:backend'];
+
+ if ($authId === NULL) {
+ $msg = "This code should never be reached.";
+ throw new SimpleSAML_Error_AuthSource($msg);
+ }
+ $source = SimpleSAML_Auth_Source::getById($authId);
+
+ try {
+ $source->authenticate($state);
+ } catch (SimpleSAML_Error_Exception $e) {
+ SimpleSAML_Auth_State::throwException($state, $e);
+ } catch (Exception $e) {
+ $e = new SimpleSAML_Error_UnserializableException($e);
+ SimpleSAML_Auth_State::throwException($state, $e);
+ }
+ // fallBack never returns after loginCompleted()
+ SimpleSAML_Logger::debug('Negotiate: backend returned');
+ self::loginCompleted($state);
+ }
+
+ /**
+ * Strips away the realm of the Kerberos identifier, looks up
+ * what attributes to fetch from SP metadata and searches the
+ * directory.
+ *
+ * @param string $user The Kerberos user identifier
+ * @return string The DN to the user or NULL if not found
+ */
+ protected function lookupUserData($user) {
+ // Kerberos usernames include realm. Strip that away.
+ $pos = strpos($user, '@');
+ if ($pos === false)
+ return NULL;
+ $uid = substr($user, 0, $pos);
+
+ $this->adminBind();
+ try {
+ $dn = $this->ldap->searchfordn($this->base,
$this->attr, $uid);
+ return $this->ldap->getAttributes($dn);
+ } catch (SimpleSAML_Error_Exception $e) {
+ SimpleSAML_Logger::debug('Negotiate - ldap lookup
failed: '. $e);
+ return NULL;
+ }
+ }
+
+ /**
+ * Elevates the LDAP connection to allow restricted lookups if
+ * so configured. Does nothing if not.
+ */
+ protected function adminBind() {
+ if ($this->admin_user === NULL) {
+ // No admin user.
+ return;
+ }
+ SimpleSAML_Logger::debug('Negotiate - authenticate(): Binding as system
user ' . var_export($this->admin_user, TRUE));
+
+ if(!$this->ldap->bind($this->admin_user, $this->admin_pw)){
+ $msg = 'Unable to authenticate system user
(LDAP_INVALID_CREDENTIALS) ' . var_export($this->admin_user, TRUE);
+ SimpleSAML_Logger::error('Negotiate - authenticate(): '
. $msg);
+ throw new SimpleSAML_Error_AuthSource($msg);
+ }
+ }
+
+ /**
+ * Log out from this authentication source.
+ *
+ * This method either logs the user out from Negotiate or passes the
+ * logout call to the fallback module.
+ *
+ * @param array &$state Information about the current logout
operation.
+ */
+ 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::getInstance();
+ $session->setData('negotiate:disable', 'session', TRUE,
24*60*60);
+ parent::logout($state);
+ } else {
+ $source = SimpleSAML_Auth_Source::getById($authId);
+ $source->logout($state);
+ }
+ }
+
+}
+
=======================================
--- /dev/null
+++ /trunk/modules/negotiate/templates/disable.php Thu Mar 8 06:08:03 2012
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ *
+ *
+ * @author Mathias Meisfjordskar, University of Oslo.
+ * <mathias.meisfjords...@usit.uio.no>
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+$this->includeAtTemplateBase('includes/header.php');
+?>
+<h1><?php echo $this->t('{negotiate:negotiate:disable_title}'); ?></h1>
+<?php
+$url = SimpleSAML_Module::getModuleURL('negotiate/enable.php');
+?>
+<?php echo $this->t('{negotiate:negotiate:disable_info_pre}', array('URL'
=> htmlspecialchars($url))); ?>
+
+<?php echo $this->t('{negotiate:negotiate:info_post}'); ?>
+
+<?php $this->includeAtTemplateBase('includes/footer.php'); ?>
=======================================
--- /dev/null
+++ /trunk/modules/negotiate/templates/enable.php Thu Mar 8 06:08:03 2012
@@ -0,0 +1,22 @@
+<?php
+
+/**
+ *
+ *
+ * @author Mathias Meisfjordskar, University of Oslo.
+ * <mathias.meisfjords...@usit.uio.no>
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+$this->includeAtTemplateBase('includes/header.php');
+?>
+<h1><?php echo $this->t('{negotiate:negotiate:enable_title}'); ?></h1>
+
+<?php
+$url = SimpleSAML_Module::getModuleURL('negotiate/disable.php');
+?>
+<?php echo $this->t('{negotiate:negotiate:enable_info_pre}', array('URL'
=> htmlspecialchars($url))); ?>
+
+<?php echo $this->t('{negotiate:negotiate:info_post}'); ?>
+
+<?php $this->includeAtTemplateBase('includes/footer.php'); ?>
=======================================
--- /dev/null
+++ /trunk/modules/negotiate/www/backend.php Thu Mar 8 06:08:03 2012
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * Provide a URL for the module to statically link to.
+ *
+ * @author Mathias Meisfjordskar, University of Oslo.
+ * <mathias.meisfjords...@usit.uio.no>
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+
+$authStateId = $_REQUEST['AuthState'];
+$state = SimpleSAML_Auth_State::loadState($authStateId,
sspmod_negotiate_Auth_Source_Negotiate::STAGEID);
+SimpleSAML_Logger::debug('backend -
fallback: '.$state['LogoutState']['negotiate:backend']);
+
+sspmod_negotiate_Auth_Source_Negotiate::fallBack($state);
+
+exit;
=======================================
--- /dev/null
+++ /trunk/modules/negotiate/www/disable.php Thu Mar 8 06:08:03 2012
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ *
+ *
+ * @author Mathias Meisfjordskar, University of Oslo.
+ * <mathias.meisfjords...@usit.uio.no>
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+
+$globalConfig = SimpleSAML_Configuration::getInstance();
+setcookie('NEGOTIATE_AUTOLOGIN_DISABLE_PERMANENT', 'True',
mktime(0,0,0,1,1,2038), '/', SimpleSAML_Utilities::getSelfHost(), FALSE,
TRUE);
+$session = SimpleSAML_Session::getInstance();
+$session->setData('negotiate:disable', 'session', FALSE, 24*60*60);
+$t = new SimpleSAML_XHTML_Template($globalConfig, 'negotiate:disable.php');
+$t->show();
=======================================
--- /dev/null
+++ /trunk/modules/negotiate/www/enable.php Thu Mar 8 06:08:03 2012
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ *
+ *
+ * @author Mathias Meisfjordskar, University of Oslo.
+ * <mathias.meisfjords...@usit.uio.no>
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+
+$globalConfig = SimpleSAML_Configuration::getInstance();
+setcookie('NEGOTIATE_AUTOLOGIN_DISABLE_PERMANENT', 'False', time() -
3600, '/', SimpleSAML_Utilities::getSelfHost(), FALSE, TRUE);
+$session = SimpleSAML_Session::getInstance();
+$session->setData('negotiate:disable', 'session', FALSE, 24*60*60);
+$t = new SimpleSAML_XHTML_Template($globalConfig, 'negotiate:enable.php');
+$t->show();
=======================================
--- /dev/null
+++ /trunk/modules/negotiate/www/retry.php Thu Mar 8 06:08:03 2012
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ *
+ *
+ * @author Mathias Meisfjordskar, University of Oslo.
+ * <mathias.meisfjords...@usit.uio.no>
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+
+$authStateId = $_REQUEST['AuthState'];
+$state = SimpleSAML_Auth_State::loadState($authStateId,
sspmod_negotiate_Auth_Source_Negotiate::STAGEID);
+
+$metadata =
SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+$idpid =
$metadata->getMetaDataCurrentEntityID('saml20-idp-hosted', 'metaindex');
+$idpmeta = $metadata->getMetaData($idpid, 'saml20-idp-hosted');
+
+if (isset($idpmeta['auth'])) {
+ $source = SimpleSAML_Auth_Source::getById($idpmeta['auth']);
+ if ($source === NULL)
+ throw new SimpleSAML_Error_BadRequest('Invalid AuthId "' .
$idpmeta['auth'] . '" - not found.');
+
+ $session = SimpleSAML_Session::getInstance();
+ $session->setData('negotiate:disable', 'session', FALSE, 24*60*60);
+ SimpleSAML_Logger::debug('Negotiate(retry) - session enabled,
retrying.');
+ $source->authenticate($state);
+ assert('FALSE');
+} else {
+ SimpleSAML_Logger::error('Negotiate - retry - no "auth" parameter found
in IdP metadata.');
+ assert('FALSE');
+}
--
You received this message because you are subscribed to the Google Groups
"simpleSAMLphp commits" group.
To post to this group, send email to simplesamlphp-commits@googlegroups.com.
To unsubscribe from this group, send email to
simplesamlphp-commits+unsubscr...@googlegroups.com.
For more options, visit this group at
http://groups.google.com/group/simplesamlphp-commits?hl=en.