ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Negotiate.php
Go to the documentation of this file.
1<?php
2
3
11{
12
13 // Constants used in the module
14 const STAGEID = 'sspmod_negotiate_Auth_Source_Negotiate.StageId';
15
16 protected $ldap = null;
17 protected $backend = '';
18 protected $hostname = '';
19 protected $port = 389;
20 protected $referrals = true;
21 protected $enableTLS = false;
22 protected $debugLDAP = false;
23 protected $timeout = 30;
24 protected $keytab = '';
25 protected $base = array();
26 protected $attr = 'uid';
27 protected $subnet = null;
28 protected $admin_user = null;
29 protected $admin_pw = null;
30 protected $attributes = null;
31
32
41 public function __construct($info, $config)
42 {
43 assert(is_array($info));
44 assert(is_array($config));
45
46 if (!extension_loaded('krb5')) {
47 throw new Exception('KRB5 Extension not installed');
48 }
49
50 // call the parent constructor first, as required by the interface
51 parent::__construct($info, $config);
52
54
55 $this->backend = $config->getString('fallback');
56 $this->hostname = $config->getString('hostname');
57 $this->port = $config->getInteger('port', 389);
58 $this->referrals = $config->getBoolean('referrals', true);
59 $this->enableTLS = $config->getBoolean('enable_tls', false);
60 $this->debugLDAP = $config->getBoolean('debugLDAP', false);
61 $this->timeout = $config->getInteger('timeout', 30);
62 $this->keytab = $config->getString('keytab');
63 $this->base = $config->getArrayizeString('base');
64 $this->attr = $config->getString('attr', 'uid');
65 $this->subnet = $config->getArray('subnet', null);
66 $this->admin_user = $config->getString('adminUser', null);
67 $this->admin_pw = $config->getString('adminPassword', null);
68 $this->attributes = $config->getArray('attributes', null);
69 }
70
71
83 public function authenticate(&$state)
84 {
85 assert(is_array($state));
86
87 // set the default backend to config
88 $state['LogoutState'] = array(
89 'negotiate:backend' => $this->backend,
90 );
91 $state['negotiate:authId'] = $this->authId;
92
93
94 // check for disabled SPs. The disable flag is store in the SP metadata
95 if (array_key_exists('SPMetadata', $state) && $this->spDisabledInMetadata($state['SPMetadata'])) {
96 $this->fallBack($state);
97 }
98 /* Go straight to fallback if Negotiate is disabled or if you are sent back to the IdP directly from the SP
99 after having logged out. */
101 $disabled = $session->getData('negotiate:disable', 'session');
102
103 if ($disabled ||
104 (!empty($_COOKIE['NEGOTIATE_AUTOLOGIN_DISABLE_PERMANENT']) &&
105 $_COOKIE['NEGOTIATE_AUTOLOGIN_DISABLE_PERMANENT'] == 'True')
106 ) {
107 SimpleSAML\Logger::debug('Negotiate - session disabled. falling back');
108 $this->fallBack($state);
109 // never executed
110 assert(false);
111 }
112 $mask = $this->checkMask();
113 if (!$mask) {
114 $this->fallBack($state);
115 // never executed
116 assert(false);
117 }
118
119 SimpleSAML\Logger::debug('Negotiate - authenticate(): looking for Negotiate');
120 if (!empty($_SERVER['HTTP_AUTHORIZATION'])) {
121 SimpleSAML\Logger::debug('Negotiate - authenticate(): Negotiate found');
122 $this->ldap = new SimpleSAML_Auth_LDAP(
123 $this->hostname,
124 $this->enableTLS,
125 $this->debugLDAP,
126 $this->timeout,
127 $this->port,
128 $this->referrals
129 );
130
131 list($mech,) = explode(' ', $_SERVER['HTTP_AUTHORIZATION'], 2);
132 if (strtolower($mech) == 'basic') {
133 SimpleSAML\Logger::debug('Negotiate - authenticate(): Basic found. Skipping.');
134 } else {
135 if (strtolower($mech) != 'negotiate') {
136 SimpleSAML\Logger::debug('Negotiate - authenticate(): No "Negotiate" found. Skipping.');
137 }
138 }
139
140 $auth = new KRB5NegotiateAuth($this->keytab);
141 // attempt Kerberos authentication
142 try {
143 $reply = $auth->doAuthentication();
144 } catch (Exception $e) {
145 SimpleSAML\Logger::error('Negotiate - authenticate(): doAuthentication() exception: '.$e->getMessage());
146 $reply = null;
147 }
148
149 if ($reply) {
150 // success! krb TGS received
151 $user = $auth->getAuthenticatedUser();
152 SimpleSAML\Logger::info('Negotiate - authenticate(): '.$user.' authenticated.');
153 $lookup = $this->lookupUserData($user);
154 if ($lookup !== null) {
155 $state['Attributes'] = $lookup;
156 // Override the backend so logout will know what to look for
157 $state['LogoutState'] = array(
158 'negotiate:backend' => null,
159 );
160 SimpleSAML\Logger::info('Negotiate - authenticate(): '.$user.' authorized.');
162 // Never reached.
163 assert(false);
164 }
165 } else {
166 // Some error in the received ticket. Expired?
167 SimpleSAML\Logger::info('Negotiate - authenticate(): Kerberos authN failed. Skipping.');
168 }
169 } else {
170 // No auth token. Send it.
171 SimpleSAML\Logger::debug('Negotiate - authenticate(): Sending Negotiate.');
172 // Save the $state array, so that we can restore if after a redirect
173 SimpleSAML\Logger::debug('Negotiate - fallback: '.$state['LogoutState']['negotiate:backend']);
175 $params = array('AuthState' => $id);
176
177 $this->sendNegotiate($params);
178 exit;
179 }
180
181 SimpleSAML\Logger::info('Negotiate - authenticate(): Client failed Negotiate. Falling back');
182 $this->fallBack($state);
183 /* The previous function never returns, so this code is never
184 executed */
185 assert(false);
186 }
187
188
190 {
191 if (array_key_exists('negotiate:disable', $spMetadata)) {
192 if ($spMetadata['negotiate:disable'] == true) {
193 SimpleSAML\Logger::debug('Negotiate - SP disabled. falling back');
194 return true;
195 } else {
196 SimpleSAML\Logger::debug('Negotiate - SP disable flag found but set to FALSE');
197 }
198 } else {
199 SimpleSAML\Logger::debug('Negotiate - SP disable flag not found');
200 }
201 return false;
202 }
203
204
213 public function checkMask()
214 {
215 // No subnet means all clients are accepted.
216 if ($this->subnet === null) {
217 return true;
218 }
219 $ip = $_SERVER['REMOTE_ADDR'];
220 foreach ($this->subnet as $cidr) {
222 if ($ret) {
223 SimpleSAML\Logger::debug('Negotiate: Client "'.$ip.'" matched subnet.');
224 return true;
225 }
226 }
227 SimpleSAML\Logger::debug('Negotiate: Client "'.$ip.'" did not match subnet.');
228 return false;
229 }
230
231
238 protected function sendNegotiate($params)
239 {
240 $url = htmlspecialchars(SimpleSAML\Module::getModuleURL('negotiate/backend.php', $params));
241 $json_url = json_encode($url);
242
243 header('HTTP/1.1 401 Unauthorized');
244 header('WWW-Authenticate: Negotiate', false);
245 echo <<<EOF
246<html>
247 <head>
248 <script type="text/javascript">window.location = $json_url</script>
249 <title>Redirect to login</title>
250 </head>
251<body>
252 <p>Your browser seems to have Javascript disabled. Please click <a href="$url">here</a>.</p>
253</body>
254</html>
255EOF;
256 }
257
258
268 public static function fallBack(&$state)
269 {
270 $authId = $state['LogoutState']['negotiate:backend'];
271
272 if ($authId === null) {
273 throw new SimpleSAML_Error_Error(array(500, "Unable to determine auth source."));
274 }
276
277 try {
278 $source->authenticate($state);
279 } catch (SimpleSAML_Error_Exception $e) {
281 } catch (Exception $e) {
284 }
285 // fallBack never returns after loginCompleted()
286 SimpleSAML\Logger::debug('Negotiate: backend returned');
288 }
289
290
299 protected function lookupUserData($user)
300 {
301 // Kerberos user names include realm. Strip that away.
302 $pos = strpos($user, '@');
303 if ($pos === false) {
304 return null;
305 }
306 $uid = substr($user, 0, $pos);
307
308 $this->adminBind();
309 try {
310 $dn = $this->ldap->searchfordn($this->base, $this->attr, $uid);
311 return $this->ldap->getAttributes($dn, $this->attributes);
312 } catch (SimpleSAML_Error_Exception $e) {
313 SimpleSAML\Logger::debug('Negotiate - ldap lookup failed: '.$e);
314 return null;
315 }
316 }
317
318
323 protected function adminBind()
324 {
325 if ($this->admin_user === null) {
326 // no admin user
327 return;
328 }
330 'Negotiate - authenticate(): Binding as system user '.var_export($this->admin_user, true)
331 );
332
333 if (!$this->ldap->bind($this->admin_user, $this->admin_pw)) {
334 $msg = 'Unable to authenticate system user (LDAP_INVALID_CREDENTIALS) '.var_export($this->admin_user, true);
335 SimpleSAML\Logger::error('Negotiate - authenticate(): '.$msg);
336 throw new SimpleSAML_Error_AuthSource('negotiate', $msg);
337 }
338 }
339
340
349 public function logout(&$state)
350 {
351 assert(is_array($state));
352 // get the source that was used to authenticate
353 $authId = $state['negotiate:backend'];
354 SimpleSAML\Logger::debug('Negotiate - logout has the following authId: "'.$authId.'"');
355
356 if ($authId === null) {
358 $session->setData('negotiate:disable', 'session', true, 24 * 60 * 60);
359 parent::logout($state);
360 } else {
362 $source->logout($state);
363 }
364 }
365}
base()
Definition: base.php:2
html()
const EOF
How fgetc() reports an End Of File.
Definition: JSMin_lib.php:92
$_COOKIE['client_id']
Definition: server.php:9
$source
Definition: linkback.php:22
if(!array_key_exists('stateid', $_REQUEST)) $state
Handle linkback() response from LinkedIn.
Definition: linkback.php:10
exit
Definition: backend.php:16
An exception for terminatinating execution or to throw for unit testing.
static info($string)
Definition: Logger.php:199
static error($string)
Definition: Logger.php:166
static debug($string)
Definition: Logger.php:211
static ipCIDRcheck($cidr, $ip=null)
Check whether an IP address is part of a CIDR.
Definition: Net.php:26
static loginCompleted($state)
Called when a login operation has finished.
Definition: Source.php:212
static getById($authId, $type=null)
Retrieve authentication source.
Definition: Source.php:340
static completeAuth(&$state)
Complete authentication.
Definition: Source.php:136
static throwException($state, SimpleSAML_Error_Exception $exception)
Throw exception to the state exception handler.
Definition: State.php:343
static saveState(&$state, $stage, $rawId=false)
Save the state.
Definition: State.php:194
static loadFromArray($config, $location='[ARRAY]', $instance=null)
Loads a configuration from the given array.
static getSessionFromRequest()
Retrieves the current session.
Definition: Session.php:241
logout(&$state)
Log out from this authentication source.
Definition: Negotiate.php:349
sendNegotiate($params)
Send the actual headers and body of the 401.
Definition: Negotiate.php:238
static fallBack(&$state)
Passes control of the login process to a different module.
Definition: Negotiate.php:268
authenticate(&$state)
The inner workings of the module.
Definition: Negotiate.php:83
lookupUserData($user)
Strips away the realm of the Kerberos identifier, looks up what attributes to fetch from SP metadata ...
Definition: Negotiate.php:299
checkMask()
checkMask() looks up the subnet config option and verifies that the client is within that range.
Definition: Negotiate.php:213
adminBind()
Elevates the LDAP connection to allow restricted lookups if so configured.
Definition: Negotiate.php:323
__construct($info, $config)
Constructor for this authentication source.
Definition: Negotiate.php:41
$mask
Definition: example_042.php:90
if(!array_key_exists('StateId', $_REQUEST)) $id
$auth
Definition: fileserver.php:48
$config
Definition: bootstrap.php:15
$info
Definition: index.php:5
$spMetadata
$user
Definition: migrateto20.php:57
Attribute-related utility methods.
$ret
Definition: parser.php:6
$session
$url
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']