470 lines
17 KiB
PHP
Executable File
470 lines
17 KiB
PHP
Executable File
<?php
|
|
require_once('lassospkit_config.inc.php');
|
|
require_once('lassospkit_debug.inc.php');
|
|
require_once('lassospkit_lib.inc.php');
|
|
require_once('lassospkit_generic_session.inc.php');
|
|
require_once('lassospkit_utils_session.inc.php');
|
|
|
|
/**
|
|
* TODO: initServer();
|
|
*/
|
|
class LassoSPKitSAMLCommon {
|
|
var $session;
|
|
var $server;
|
|
var $profile;
|
|
|
|
/** Error handling */
|
|
var $reset_status = 0;
|
|
var $human_status = "";
|
|
var $ret = 0;
|
|
var $ret_str = "";
|
|
var $exception = null;
|
|
var $relayState = null;
|
|
|
|
public function __construct(LassoSPKitGenericSession $session) {
|
|
$this->session = $session;
|
|
}
|
|
/* Accessors */
|
|
/* Status is a human readable and translatable string. */
|
|
public function setStatus($str) {
|
|
$this->human_status = gettext($str);
|
|
}
|
|
public function getStatus() {
|
|
return $this->human_status;
|
|
}
|
|
/* Ret is the return code from the last error returning
|
|
lasso function. */
|
|
public function setRet($ret, $prefix = "") {
|
|
$this->ret = $ret;
|
|
if ($ret != 0) {
|
|
$this->ret_str = strError($ret);
|
|
LassoSPKitUtilsSession::setLastError($prefix . $this->ret_str);
|
|
} else {
|
|
$this->ret_str = "";
|
|
}
|
|
}
|
|
public function getRet() {
|
|
return $this->ret . ":" . $this->ret_str;
|
|
}
|
|
/** Create the server object by retrieving the configuration from
|
|
the data object. */
|
|
function initServer($base) {
|
|
if ($this->server) {
|
|
return 1;
|
|
}
|
|
$spmeta = $base . "/" . SP_METADATA;
|
|
$idpmeta = $base . "/../" . IDP_METADATA;
|
|
$pkey = $base . "/" . PRIVATE_KEY;
|
|
$ok = $this->checkFile($spmeta);
|
|
$ok = $ok && $this->checkFile($idpmeta);
|
|
$ok = $ok && $this->checkFile($pkey);
|
|
$ok = $ok
|
|
&& $server = new LassoServer($spmeta, $pkey, NULL, NULL);
|
|
$ok = $ok
|
|
&& ! $ret = $server->addProvider(LASSO_PROVIDER_ROLE_IDP
|
|
,$idpmeta,null, null);
|
|
if (! $ok) {
|
|
lassospkit_errlog("Erreur création serveur: Ret=$ret Msg=" . strError($ret));
|
|
return 0;
|
|
} else {
|
|
$this->server = $server;
|
|
return $server;
|
|
}
|
|
}
|
|
public static function checkFile($file) {
|
|
if (! file_exists($file)) {
|
|
lassospkit_errlog("File " . $file . " is absent, can't construct server object");
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
/*** Helper functions ***/
|
|
public function ssoNameIdPolicyConfig($blob) {
|
|
throw new Exception("Must be overloaded!!");
|
|
}
|
|
/** Helper function to do redirects. */
|
|
public function doRedirect(LassoProfile $profile) {
|
|
$this->session->doRedirect($profile->msgUrl);
|
|
}
|
|
/** Return a normal HTTP response, for SOAP Response binding */
|
|
public function doResponse(LassoProfile $profile) {
|
|
$this->session->doResponse('text/xml', $profile->msgBody);
|
|
}
|
|
/** Read a soap message from stdin */
|
|
public function receiveSOAPMessage() {
|
|
$contents = @file_get_contents("php://input");
|
|
if ($contents === FALSE) {
|
|
lassospkit_errlog("Problem Receiving a SOAP message2");
|
|
}
|
|
return $contents;
|
|
}
|
|
/** Retrieve the response message associated to an artifact string,
|
|
method is the method used to transmit the artifact.
|
|
This method makes a soap call to resolve the artifact, it is synchronous
|
|
so can potentially take times.
|
|
*/
|
|
public function artifactResolve(LassoProfile $profile, $query, $method) {
|
|
$retIR = $profile->initRequest($query, $method);
|
|
$retBRM = $profile->buildRequestMsg();
|
|
$content = @LassoSPKitHelper::SoapCallWithProfile($profile);
|
|
if ($content === FALSE) {
|
|
lassospkit_errlog("artifactResolve: soapCall result empty");
|
|
$content = "";
|
|
}
|
|
$retPRM = $profile->processResponseMsg($content);
|
|
if ($retIR || $retBRM || $retPRM) {
|
|
lassospkit_errlog("artifactResolve: retIR: $retIR retBRM: $retBRM retPRM: $retPRM");
|
|
if ($retIR) {
|
|
return $retIR;
|
|
}
|
|
if ($retBRM) {
|
|
return $retBRM;
|
|
}
|
|
if ($retPRM) {
|
|
return $retPRM;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
/** Finish a request with a redirect transport */
|
|
public function finishRedirectRequest(LassoProfile $profile) {
|
|
$this->doRedirect($profile);
|
|
}
|
|
/** Finish a request with a SOAP transport */
|
|
public function finishSOAPRequest(LassoProfile $profile, &$response) {
|
|
$response = LassoSPKitHelper::SoapCallWithProfile($profile);
|
|
}
|
|
|
|
public function finishResponse(LassoProfile $profile) {
|
|
$method = LASSO_HTTP_METHOD_SOAP; # $profile->http_request_method;
|
|
$ret = $profile->buildResponseMsg();
|
|
switch ($method) {
|
|
case LASSO_HTTP_METHOD_REDIRECT:
|
|
$this->doRedirect($profile);
|
|
break;
|
|
case LASSO_HTTP_METHOD_SOAP:
|
|
$this->doResponse($profile);
|
|
break;
|
|
default:
|
|
LassoSPKitHelper::notImplemented();
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
/** Web SSO protocol */
|
|
|
|
/** Start a WebSSO interaction. Initiate the redirect using
|
|
the doRedirect method of the session object.
|
|
Calls ssoNameIdPolicyConfig on the session object to initialize.
|
|
*/
|
|
public function ssoCommon(&$login,
|
|
$remoteID,
|
|
$method,
|
|
$isConsentObtained,
|
|
$forceAuthn,
|
|
$isPassive,
|
|
$relayState,
|
|
$blob) {
|
|
$login = new LassoLogin($this->server);
|
|
if ($relayState && is_string($relayState)) {
|
|
$request->msgRelayState = $relayState;
|
|
# Temporary workaround
|
|
LassoSPKitUtilsSession::setRelayState('sso', $relayState);
|
|
}
|
|
$retFF = $this->findFederation($login);
|
|
$retIAR = $login->initAuthnRequest($remoteID,$method);
|
|
$request = @$login->request;
|
|
$nameidpolicy = @$request->NameIDPolicy;
|
|
if ($nameidpolicy) {
|
|
$this->ssoNameIdPolicyConfig($login, $blob);
|
|
$request->consent = $isConsentObtained;
|
|
$request->ForceAuthn = $forceAuthn;
|
|
$request->IsPassive = $isPassive;
|
|
} else {
|
|
throw new Exception("SSO: Pas d'object NameIDPolicy");
|
|
}
|
|
$retBAR = $login->buildAuthnRequestMsg();
|
|
switch($method) {
|
|
case LASSO_HTTP_METHOD_REDIRECT:
|
|
$this->doRedirect($login);
|
|
break;
|
|
default:
|
|
LassoSPKitHelper::notImplemented();
|
|
}
|
|
}
|
|
/** Method to consume response to auth requests. */
|
|
function ssoConsumer($method, $message) {
|
|
$login = new LassoLogin($this->server);
|
|
$retPRM = 0;
|
|
$retAR = 0;
|
|
$retFF = 0;
|
|
switch ($method) {
|
|
case LASSO_HTTP_METHOD_ARTIFACT_GET:
|
|
case LASSO_HTTP_METHOD_ARTIFACT_POST:
|
|
$retAR = $this->artifactResolve($login,
|
|
$message,
|
|
$method);
|
|
break;
|
|
case LASSO_HTTP_METHOD_POST:
|
|
$retPRM = $login->processResponseMsg($message);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if ($retAR == LASSO_LOGIN_ERROR_FEDERATION_NOT_FOUND
|
|
|| $retPRM == LASSO_LOGIN_ERROR_FEDERATION_NOT_FOUND) {
|
|
$this->setMessage("Federation not found");
|
|
}
|
|
if ($retAR == LASSO_LOGIN_ERROR_STATUS_NOT_SUCCESS ||
|
|
$retPRM == LASSO_LOGIN_ERROR_STATUS_NOT_SUCCESS) {
|
|
$this->setMessage("Request denied");
|
|
}
|
|
if ($login->msgRelayState) {
|
|
$this->relayState = $login->msgRelayState;
|
|
}
|
|
# temporary workaround
|
|
$this->relayState = LassoSPKitUtilsSession::getRelayState('sso');
|
|
if ($retAR || $retPRM) {
|
|
lassospkit_errlog("ssoConsumer, retAR: $retAR retPRM: $retPRM");
|
|
} else {
|
|
$retFF = $this->findFederation($login);
|
|
$retASSO = $login->acceptSso();
|
|
if ($retFF || $retASSO) {
|
|
lassospkit_errlog("ssoConsumer, retASSO: $retASSO retFF: $retFF");
|
|
}
|
|
}
|
|
if ($login->response->assertion) {
|
|
$attributes = LassoSPKitHelper::assertionExtractAttributes($login->response->assertion[0]);
|
|
$utils_session = LassoSPKitUtilsSession::getSingleton();
|
|
$utils_session->setAssertionAttributes($attributes);
|
|
}
|
|
$this->saveFederation($login);
|
|
$this->checkXmlErrors("AssertionConsumer");
|
|
if ($retAR) {
|
|
return $retAR;
|
|
}
|
|
if ($retPRM) {
|
|
return $retPRM;
|
|
}
|
|
#if ($retFF) {
|
|
# return $retFF;
|
|
#}
|
|
if ($retASSO) {
|
|
return $retASSO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/** Web SLO methods */
|
|
/* SP initiated */
|
|
public function initiateSLO($method = LASSO_HTTP_METHOD_SOAP, $remoteID = null, $relayState = null)
|
|
{
|
|
if ($method == null) {
|
|
$method = LASSO_HTTP_METHOD_SOAP;
|
|
}
|
|
$logout = new LassoLogout($this->server);
|
|
if ($relayState && is_string($relayState)) {
|
|
$logout->msgRelayState = $relayState;
|
|
}
|
|
if ($retFF = $this->findFederation($logout)) {
|
|
return $retFF;
|
|
}
|
|
$retIR = $logout->initRequest($remoteID, $method);
|
|
$retBRM = $logout->buildRequestMsg();
|
|
switch ($method) {
|
|
case LASSO_HTTP_METHOD_REDIRECT:
|
|
$this->finishRedirectRequest($logout);
|
|
break;
|
|
case LASSO_HTTP_METHOD_SOAP:
|
|
$this->finishSOAPRequest($logout, $response);
|
|
$this->processResponseSLO($method, $response, $logout);
|
|
break;
|
|
case LASSO_HTTP_METHOD_ARTIFACT_GET:
|
|
case LASSO_HTTP_METHOD_ARTIFACT_POST:
|
|
case LASSO_HTTP_METHOD_POST:
|
|
default:
|
|
LassoSPKitHelper::notImplemented();
|
|
}
|
|
$this->checkXmlErrors("Logout");
|
|
}
|
|
public function processRedirectResponseSLO() {
|
|
return $this->processResponseSLO(LASSO_HTTP_METHOD_REDIRECT, $_SERVER['QUERY_STRING']);
|
|
}
|
|
public function processResponseSLO($http_method, $message, &$logout = null) {
|
|
$retFF = 0;
|
|
if ($logout == null) {
|
|
$logout = new LassoLogout($this->server);
|
|
$retFF = $this->findFederation($logout);
|
|
}
|
|
$retPRM = $logout->processResponseMsg($message);
|
|
switch ($retPRM) {
|
|
case 0:
|
|
default:
|
|
case LASSO_DS_ERROR_INVALID_SIGNATURE:
|
|
break;
|
|
case LASSO_LOGOUT_ERROR_REQUEST_DENIED:
|
|
$this->setMessage("Logout request denied by IdP");
|
|
break;
|
|
case LASSO_LOGOUT_ERROR_UNKNOWN_PRINCIPAL:
|
|
$this->setMessage("IdP said 'unknown principal'");
|
|
break;
|
|
case LASSO_PROFILE_ERROR_INVALID_QUERY:
|
|
$this->setMessage("IdP said 'logout request is invalid'");
|
|
break;
|
|
}
|
|
$retSF = $this->saveFederation($logout);
|
|
$this->session->logout();
|
|
if ($retFF || $retPRM || $retSF) {
|
|
lassospkit_errlog("processResponseSLO: retFF: $retFF retPRM: $retPRM retSF: $retSF");
|
|
}
|
|
if ($retFF) {
|
|
return $retFF;
|
|
}
|
|
if ($retPRM) {
|
|
return $retPRM;
|
|
}
|
|
return 0;
|
|
}
|
|
/** IDP initiated SLO **/
|
|
public function processRedirectRequestSLO() {
|
|
return $this->processRequestSLO(LASSO_HTTP_METHOD_REDIRECT,
|
|
$_SERVER['QUERY_STRING']);
|
|
}
|
|
/** Read the SOAP request in the POSTed HTTP message,
|
|
and process it as a SLO request. */
|
|
public function processSOAPRequestSLO() {
|
|
$contents = $this->receiveSoapMessage();
|
|
return $this->processRequestSLO(LASSO_HTTP_METHOD_SOAP,
|
|
$contents);
|
|
}
|
|
/** Use $message as a query for the given http_method,
|
|
and treat it as a SLO request. */
|
|
public function processRequestSLO($method, $message) {
|
|
$logout = new LassoLogout($this->server);
|
|
$retPRM = 0;
|
|
$retAR = 0;
|
|
$retFF = 0;
|
|
switch ($method) {
|
|
case LASSO_HTTP_METHOD_ARTIFACT_GET:
|
|
case LASSO_HTTP_METHOD_ARTIFACT_POST:
|
|
$retAR = $this->artifactResolve($login,
|
|
$message,
|
|
$method);
|
|
break;
|
|
case LASSO_HTTP_METHOD_POST:
|
|
case LASSO_HTTP_METHOD_REDIRECT:
|
|
case LASSO_HTTP_METHOD_SOAP:
|
|
$retPRM = $logout->processRequestMsg($message);
|
|
break;
|
|
}
|
|
$retFF = $this->findFederation($logout);
|
|
$retVR = $logout->validateRequest();
|
|
$retBR = $this->finishResponse($logout);
|
|
$this->session->logout();
|
|
if ($retFF || $retAR || $retPRM || $retVR || $retBR) {
|
|
lassospkit_errlog("Error during SLO request handling for nameId: " . $logout->nameID . " retAR: $retAR retPRM: $retPRM retFF: $retFF retVR: $retVR retBR: $retBR");
|
|
}
|
|
lassospkit_errlog("Error during SLO request handling for nameId: " . $logout->nameID . " retAR: $retAR retPRM: $retPRM retFF: $retFF retVR: $retVR retBR: $retBR");
|
|
if ($retAR) {
|
|
return $retAR;
|
|
}
|
|
if ($retPRM) {
|
|
return $retPRM;
|
|
}
|
|
if ($retFF) {
|
|
return $retFF;
|
|
}
|
|
if ($retVR) {
|
|
return $retVR;
|
|
}
|
|
if ($retBR) {
|
|
return $retBR;
|
|
}
|
|
return 0;
|
|
}
|
|
/** If result of profile is a modification of identity or session objects,
|
|
save them in the federations database. */
|
|
public function saveFederation(LassoProfile $profile) {
|
|
LassoSPKitHelper::saveFederation($profile, $this->session);
|
|
return 0;
|
|
}
|
|
/** Find a federation using as many hints as possible (maybe the nameid given in the request,
|
|
or the nameid present in the current session) */
|
|
public function findFederation(LassoProfile $profile) {
|
|
if (LassoSPKitHelper::findFederation($profile, $this->session))
|
|
return 0;
|
|
return LASSO_PROFILE_ERROR_FEDERATION_NOT_FOUND;
|
|
}
|
|
/** Federation termination.
|
|
Supress the current federation. That is the records associated
|
|
to the nameid and userid in the current session.
|
|
**/
|
|
public function initiateFTNotification($method = LASSO_HTTP_METHOD_SOAP, $remoteID = null) {
|
|
$this->session->changeFederation(null, null);
|
|
}
|
|
/** Store the given profile in the current session to restore it in the
|
|
response endpoint handler. */
|
|
function keepProfile(LassoProfile $profile) {
|
|
$fed = @unserialize(LassoSPKitUtilsSession::getFederation());
|
|
if ($fed == null) {
|
|
$fed = array();
|
|
}
|
|
$fed['profile'] = $profile->dump();
|
|
LassoSPKitUtilsSession::setFederation(serialize($fed));
|
|
}
|
|
/** Get the stored profile */
|
|
function restoreProfile() {
|
|
$fed = LassoSPKitUtilsSession::getFederation();
|
|
if ($fed == null)
|
|
return null;
|
|
$fed = @unserialize($fed);
|
|
if ($fed == null)
|
|
return null;
|
|
if (isset($fed['profile'])) {
|
|
$profile = @$fed['profile'];
|
|
unset($fed['profile']);
|
|
} else {
|
|
return null;
|
|
}
|
|
LassoSPKitUtilsSession::setFederation(serialize($fed));
|
|
return $profile;
|
|
}
|
|
function setMessage($message) {
|
|
LassoSPKitUtilsSession::setLastError($message);
|
|
}
|
|
function getRelayState() {
|
|
return $this->relayState;
|
|
}
|
|
/** Check the stack of xml errors and report
|
|
them to syslog */
|
|
function checkXmlErrors($prefix = "") {
|
|
if (! function_exists('libxml_get_errors')) {
|
|
return;
|
|
}
|
|
foreach (libxml_get_errors() as $error) {
|
|
$return = "$prefix, LibXMLError, ";
|
|
switch ($error->level) {
|
|
case LIBXML_ERR_WARNING:
|
|
$return .= "Warning $error->code: ";
|
|
break;
|
|
case LIBXML_ERR_ERROR:
|
|
$return .= "Error $error->code: ";
|
|
break;
|
|
case LIBXML_ERR_FATAL:
|
|
$return .= "Fatal Error $error->code: ";
|
|
break;
|
|
}
|
|
|
|
$return .= trim($error->message) .
|
|
" Line: $error->line" .
|
|
" Column: $error->column";
|
|
|
|
if ($error->file) {
|
|
$return .= " File: $error->file";
|
|
}
|
|
lassospkit_errlog($return);
|
|
}
|
|
libxml_clear_errors();
|
|
}
|
|
}
|