This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
spkitlasso/include/lassospkit_saml_common.inc.php

478 lines
17 KiB
PHP

<?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;
var $nameID = 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) {
$ret = 0;
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) {
$code = null;
$retIR = $profile->initRequest($query, $method);
$retBRM = $profile->buildRequestMsg();
$content = @LassoSPKitHelper::SoapCallWithProfile($profile, $code);
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, &$code) {
$response = LassoSPKitHelper::SoapCallWithProfile($profile, $code);
lassospkit_errlog("code: $code");
}
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)
{
$code = 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, $code);
$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);
if ($profile->nameIdentifier) {
$this->nameID = LassoSPKitHelper::profileGetNameID($profile);
}
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();
}
}