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.

Reply via email to