001/* 002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 003 * 004 * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved 005 * 006 * The contents of this file are subject to the terms 007 * of the Common Development and Distribution License 008 * (the License). You may not use this file except in 009 * compliance with the License. 010 * 011 * You can obtain a copy of the License at 012 * https://opensso.dev.java.net/public/CDDLv1.0.html or 013 * opensso/legal/CDDLv1.0.txt 014 * See the License for the specific language governing 015 * permission and limitations under the License. 016 * 017 * When distributing Covered Code, include this CDDL 018 * Header Notice in each file and include the License file 019 * at opensso/legal/CDDLv1.0.txt. 020 * If applicable, add the following below the CDDL Header, 021 * with the fields enclosed by brackets [] replaced by 022 * your own identifying information: 023 * "Portions Copyrighted [year] [name of copyright owner]" 024 * 025 * $Id: SPACSUtils.java,v 1.48 2009/11/20 21:41:16 exu Exp $ 026 * 027 * Portions Copyrighted 2010-2015 ForgeRock AS. 028 */ 029package com.sun.identity.saml2.profile; 030 031import com.sun.identity.common.SystemConfigurationUtil; 032import com.sun.identity.liberty.ws.soapbinding.Message; 033import com.sun.identity.liberty.ws.soapbinding.SOAPBindingException; 034import com.sun.identity.liberty.ws.soapbinding.SOAPFaultException; 035import com.sun.identity.plugin.datastore.DataStoreProviderException; 036import com.sun.identity.plugin.monitoring.FedMonAgent; 037import com.sun.identity.plugin.monitoring.FedMonSAML2Svc; 038import com.sun.identity.plugin.monitoring.MonitorManager; 039import com.sun.identity.plugin.session.SessionException; 040import com.sun.identity.plugin.session.SessionManager; 041import com.sun.identity.plugin.session.SessionProvider; 042import com.sun.identity.saml.common.SAMLConstants; 043import com.sun.identity.saml.common.SAMLUtils; 044import com.sun.identity.saml.xmlsig.KeyProvider; 045import com.sun.identity.saml2.assertion.Advice; 046import com.sun.identity.saml2.assertion.Assertion; 047import com.sun.identity.saml2.assertion.AssertionFactory; 048import com.sun.identity.saml2.assertion.Attribute; 049import com.sun.identity.saml2.assertion.AttributeStatement; 050import com.sun.identity.saml2.assertion.EncryptedAttribute; 051import com.sun.identity.saml2.assertion.EncryptedID; 052import com.sun.identity.saml2.assertion.Issuer; 053import com.sun.identity.saml2.assertion.NameID; 054import com.sun.identity.saml2.assertion.Subject; 055import com.sun.identity.saml2.common.AccountUtils; 056import com.sun.identity.saml2.common.NameIDInfo; 057import com.sun.identity.saml2.common.NameIDInfoKey; 058import com.sun.identity.saml2.common.SAML2Constants; 059import com.sun.identity.saml2.common.SAML2Exception; 060import com.sun.identity.saml2.common.SAML2FailoverUtils; 061import com.sun.identity.saml2.common.SAML2SDKUtils; 062import com.sun.identity.saml2.common.SAML2Utils; 063import com.sun.identity.saml2.common.SOAPCommunicator; 064import com.sun.identity.saml2.ecp.ECPFactory; 065import com.sun.identity.saml2.ecp.ECPRelayState; 066import com.sun.identity.saml2.jaxb.entityconfig.IDPSSOConfigElement; 067import com.sun.identity.saml2.jaxb.entityconfig.SPSSOConfigElement; 068import com.sun.identity.saml2.jaxb.metadata.AffiliationDescriptorType; 069import com.sun.identity.saml2.jaxb.metadata.ArtifactResolutionServiceElement; 070import com.sun.identity.saml2.jaxb.metadata.IDPSSODescriptorElement; 071import com.sun.identity.saml2.jaxb.metadata.SPSSODescriptorElement; 072import com.sun.identity.saml2.key.KeyUtil; 073import com.sun.identity.saml2.logging.LogUtil; 074import com.sun.identity.saml2.meta.SAML2MetaException; 075import com.sun.identity.saml2.meta.SAML2MetaManager; 076import com.sun.identity.saml2.meta.SAML2MetaUtils; 077import com.sun.identity.saml2.plugins.SAML2PluginsUtils; 078import com.sun.identity.saml2.plugins.SAML2ServiceProviderAdapter; 079import com.sun.identity.saml2.plugins.SPAccountMapper; 080import com.sun.identity.saml2.plugins.SPAttributeMapper; 081import com.sun.identity.saml2.protocol.Artifact; 082import com.sun.identity.saml2.protocol.ArtifactResolve; 083import com.sun.identity.saml2.protocol.ArtifactResponse; 084import com.sun.identity.saml2.protocol.AuthnRequest; 085import com.sun.identity.saml2.protocol.ProtocolFactory; 086import com.sun.identity.saml2.protocol.Response; 087import com.sun.identity.saml2.protocol.Status; 088import com.sun.identity.shared.encode.Base64; 089import com.sun.identity.shared.encode.URLEncDec; 090import com.sun.identity.shared.xml.XMLUtils; 091import java.io.ByteArrayInputStream; 092import java.io.IOException; 093import java.io.PrintWriter; 094import java.security.PrivateKey; 095import java.security.cert.X509Certificate; 096import java.util.ArrayList; 097import java.util.Date; 098import java.util.HashMap; 099import java.util.HashSet; 100import java.util.Iterator; 101import java.util.List; 102import java.util.Map; 103import java.util.Set; 104import java.util.logging.Level; 105import javax.servlet.ServletException; 106import javax.servlet.http.HttpServletRequest; 107import javax.servlet.http.HttpServletResponse; 108import javax.xml.soap.SOAPConnection; 109import javax.xml.soap.SOAPException; 110import javax.xml.soap.SOAPMessage; 111import org.forgerock.openam.federation.saml2.SAML2TokenRepositoryException; 112import org.forgerock.openam.saml2.audit.SAML2Auditor; 113import org.forgerock.openam.saml2.audit.SAML2EventLogger; 114import org.forgerock.openam.utils.ClientUtils; 115import org.forgerock.openam.utils.CollectionUtils; 116import org.forgerock.openam.utils.StringUtils; 117import org.w3c.dom.Document; 118import org.w3c.dom.Element; 119 120/** 121 * This class is used by a service provider (SP) to process the response from 122 * an identity provider for the SP's Assertion Consumer Service. 123 * 124 * @supported.api 125 */ 126public class SPACSUtils { 127 128 private static FedMonAgent agent = MonitorManager.getAgent(); 129 private static FedMonSAML2Svc saml2Svc = MonitorManager.getSAML2Svc(); 130 131 private SPACSUtils() {} 132 133 /** 134 * Retrieves <code>SAML</code> <code>Response</code> from http request. 135 * It handles three cases: 136 * <pre> 137 * 1. using http method get using request parameter "resID". 138 * This is the case after local login is done. 139 * 2. using http method get using request parameter "SAMLart". 140 * This is the case for artifact profile. 141 * 3. using http method post. This is the case for post profile. 142 * </pre> 143 * 144 * @param request http servlet request 145 * @param response http servlet response 146 * @param orgName realm or organization name the service provider resides in 147 * @param hostEntityId Entity ID of the hosted service provider 148 * @param metaManager <code>SAML2MetaManager</code> instance. 149 * @return <code>ResponseInfo</code> instance. 150 * @throws SAML2Exception,IOException if it fails in the process. 151 */ 152 public static ResponseInfo getResponse( 153 HttpServletRequest request, 154 HttpServletResponse response, 155 String orgName, 156 String hostEntityId, 157 SAML2MetaManager metaManager) 158 throws SAML2Exception,IOException 159 { 160 ResponseInfo respInfo = null; 161 162 String method = request.getMethod(); 163 if (method.equals("GET")) { 164 if (!SAML2Utils.isSPProfileBindingSupported( 165 orgName, hostEntityId, SAML2Constants.ACS_SERVICE, 166 SAML2Constants.HTTP_ARTIFACT)) 167 { 168 SAMLUtils.sendError(request, response, 169 response.SC_BAD_REQUEST, 170 "unsupportedBinding", 171 SAML2Utils.bundle.getString("unsupportedBinding")); 172 throw new SAML2Exception( 173 SAML2Utils.bundle.getString("unsupportedBinding")); 174 } 175 respInfo = getResponseFromGet(request, response, orgName, 176 hostEntityId, metaManager); 177 } else if (method.equals("POST")) { 178 String pathInfo = request.getPathInfo(); 179 if ((pathInfo != null) && (pathInfo.startsWith("/ECP"))) { 180 if (!SAML2Utils.isSPProfileBindingSupported( 181 orgName, hostEntityId, SAML2Constants.ACS_SERVICE, 182 SAML2Constants.PAOS)) 183 { 184 SAMLUtils.sendError(request, response, 185 response.SC_BAD_REQUEST, 186 "unsupportedBinding", 187 SAML2Utils.bundle.getString("unsupportedBinding")); 188 throw new SAML2Exception( 189 SAML2Utils.bundle.getString("unsupportedBinding")); 190 } 191 respInfo = getResponseFromPostECP(request, response, orgName, 192 hostEntityId, metaManager); 193 } else { 194 if (!SAML2Utils.isSPProfileBindingSupported( 195 orgName, hostEntityId, SAML2Constants.ACS_SERVICE, 196 SAML2Constants.HTTP_POST)) 197 { 198 SAMLUtils.sendError(request, response, 199 response.SC_BAD_REQUEST, 200 "unsupportedBinding", 201 SAML2Utils.bundle.getString("unsupportedBinding")); 202 throw new SAML2Exception( 203 SAML2Utils.bundle.getString("unsupportedBinding")); 204 } 205 respInfo = getResponseFromPost(request, response, orgName, 206 hostEntityId, metaManager); 207 } 208 } else { 209 // not supported 210 SAMLUtils.sendError(request, response, 211 response.SC_METHOD_NOT_ALLOWED, 212 "notSupportedHTTPMethod", 213 SAML2Utils.bundle.getString("notSupportedHTTPMethod")); 214 throw new SAML2Exception( 215 SAML2Utils.bundle.getString("notSupportedHTTPMethod")); 216 } 217 if (SAML2Utils.debug.messageEnabled()) { 218 SAML2Utils.debug.message("SPACSUtils.getResponse: got response=" 219 + respInfo.getResponse().toXMLString(true, true)); 220 } 221 return respInfo; 222 } 223 224 /** 225 * Retrieves <code>SAML Response</code> from http Get. 226 * It first uses parameter resID to retrieve <code>Response</code>. This is 227 * the case after local login; 228 * If resID is not defined, it then uses <code>SAMLart</code> http 229 * parameter to retrieve <code>Response</code>. 230 */ 231 private static ResponseInfo getResponseFromGet( 232 HttpServletRequest request, 233 HttpServletResponse response, 234 String orgName, 235 String hostEntityId, 236 SAML2MetaManager metaManager) 237 throws SAML2Exception,IOException 238 { 239 ResponseInfo respInfo = null; 240 String resID = request.getParameter("resID"); 241 if (resID != null && resID.length() != 0) { 242 if (SAML2Utils.debug.messageEnabled()) { 243 SAML2Utils.debug.message("SPACSUtils.getResponseFromGet: resID=" 244 + resID); 245 } 246 synchronized (SPCache.responseHash) { 247 respInfo = (ResponseInfo) SPCache.responseHash.remove(resID); 248 } 249 if (respInfo == null) { 250 if (SAML2Utils.debug.messageEnabled()) { 251 SAML2Utils.debug.message("SPACSUtils.getResponseFromGet: " 252 + "couldn't find Response from resID."); 253 } 254 String[] data = {resID}; 255 LogUtil.error(Level.INFO, 256 LogUtil.RESPONSE_NOT_FOUND_FROM_CACHE, 257 data, 258 null); 259 SAMLUtils.sendError(request, response, 260 response.SC_INTERNAL_SERVER_ERROR, "SSOFailed", 261 SAML2Utils.bundle.getString("SSOFailed")); 262 throw new SAML2Exception( 263 SAML2Utils.bundle.getString("SSOFailed")); 264 } 265 return respInfo; 266 } 267 268 String samlArt = request.getParameter(SAML2Constants.SAML_ART); 269 if (samlArt == null || samlArt.trim().length() == 0) { 270 SAML2Utils.debug.error("SPACSUtils.getResponseFromGet: Artifact " 271 + "string is empty."); 272 LogUtil.error(Level.INFO, 273 LogUtil.MISSING_ARTIFACT, 274 null, 275 null); 276 SAMLUtils.sendError(request, response, response.SC_BAD_REQUEST, 277 "missingArtifact", 278 SAML2Utils.bundle.getString("missingArtifact")); 279 throw new SAML2Exception( 280 SAML2Utils.bundle.getString("missingArtifact")); 281 } 282 283 return new ResponseInfo(getResponseFromArtifact(samlArt, hostEntityId, 284 request, response, orgName, metaManager), 285 SAML2Constants.HTTP_ARTIFACT, null); 286 } 287 288 // Retrieves response using artifact profile. 289 private static Response getResponseFromArtifact(String samlArt, 290 String hostEntityId, HttpServletRequest request, 291 HttpServletResponse response, String orgName, 292 SAML2MetaManager sm) throws SAML2Exception,IOException 293 { 294 295 // Try to get source ID and endpointIndex, and then 296 // decide which IDP and which artifact resolution service 297 if (SAML2Utils.debug.messageEnabled()) { 298 SAML2Utils.debug.message("SPACSUtils.getResponseFromArtifact: " + 299 "samlArt = " + samlArt); 300 } 301 302 Artifact art = null; 303 try { 304 art = ProtocolFactory.getInstance().createArtifact(samlArt.trim()); 305 String[] data = {samlArt.trim()}; 306 LogUtil.access(Level.INFO, 307 LogUtil.RECEIVED_ARTIFACT, 308 data, 309 null); 310 } catch (SAML2Exception se) { 311 SAML2Utils.debug.error("SPACSUtils.getResponseFromArtifact: " 312 + "Unable to decode and parse artifact string:" + samlArt); 313 SAMLUtils.sendError(request, response, response.SC_BAD_REQUEST, 314 "errorObtainArtifact", 315 SAML2Utils.bundle.getString("errorObtainArtifact")); 316 throw se; 317 } 318 319 String idpEntityID = getIDPEntityID(art, request, response, orgName, sm); 320 IDPSSODescriptorElement idp = null; 321 try { 322 idp = sm.getIDPSSODescriptor(orgName, idpEntityID); 323 } catch (SAML2MetaException se) { 324 String[] data = {orgName, idpEntityID}; 325 LogUtil.error(Level.INFO, 326 LogUtil.IDP_META_NOT_FOUND, 327 data, 328 null); 329 SAMLUtils.sendError(request, response, 330 response.SC_INTERNAL_SERVER_ERROR, 331 "failedToGetIDPSSODescriptor", se.getMessage()); 332 throw se; 333 } 334 335 String location = getIDPArtifactResolutionServiceUrl( 336 art.getEndpointIndex(), idpEntityID, idp, request, response); 337 338 // create ArtifactResolve message 339 ArtifactResolve resolve = null; 340 SOAPMessage resMsg = null; 341 try { 342 resolve = ProtocolFactory.getInstance().createArtifactResolve(); 343 resolve.setID(SAML2Utils.generateID()); 344 resolve.setVersion(SAML2Constants.VERSION_2_0); 345 resolve.setIssueInstant(new Date()); 346 resolve.setArtifact(art); 347 resolve.setDestination(XMLUtils.escapeSpecialCharacters(location)); 348 Issuer issuer = AssertionFactory.getInstance().createIssuer(); 349 issuer.setValue(hostEntityId); 350 resolve.setIssuer(issuer); 351 String needArtiResolveSigned = 352 SAML2Utils.getAttributeValueFromSSOConfig( 353 orgName, 354 idpEntityID, 355 SAML2Constants.IDP_ROLE, 356 SAML2Constants.WANT_ARTIFACT_RESOLVE_SIGNED); 357 358 if (needArtiResolveSigned != null && 359 needArtiResolveSigned.equals("true")) { 360 // or save it somewhere? 361 String signAlias = getAttributeValueFromSPSSOConfig( 362 orgName, 363 hostEntityId, 364 sm, 365 SAML2Constants.SIGNING_CERT_ALIAS); 366 if (signAlias == null) { 367 throw new SAML2Exception( 368 SAML2Utils.bundle.getString("missingSigningCertAlias")); 369 } 370 KeyProvider kp = KeyUtil.getKeyProviderInstance(); 371 if (kp == null) { 372 throw new SAML2Exception( 373 SAML2Utils.bundle.getString("nullKeyProvider")); 374 } 375 resolve.sign(kp.getPrivateKey(signAlias), 376 kp.getX509Certificate(signAlias)); 377 } 378 379 String resolveString = resolve.toXMLString(true, true); 380 if (SAML2Utils.debug.messageEnabled()) { 381 SAML2Utils.debug.message("SPACSUtils.getResponseFromArtifact: " 382 + "ArtifactResolve=" + resolveString); 383 } 384 385 SOAPConnection con = SOAPCommunicator.getInstance().openSOAPConnection(); 386 SOAPMessage msg = SOAPCommunicator.getInstance().createSOAPMessage(resolveString, true); 387 388 IDPSSOConfigElement config = null; 389 config = sm.getIDPSSOConfig(orgName, idpEntityID); 390 location = SAML2Utils.fillInBasicAuthInfo( 391 config, location); 392 resMsg = con.call(msg, location); 393 } catch (SAML2Exception s2e) { 394 SAML2Utils.debug.error("SPACSUtils.getResponseFromArtifact: " 395 + "couldn't create ArtifactResolve:", s2e); 396 String[] data = {hostEntityId, art.getArtifactValue()}; 397 LogUtil.error(Level.INFO, 398 LogUtil.CANNOT_CREATE_ARTIFACT_RESOLVE, 399 data, 400 null); 401 SAMLUtils.sendError(request, response, 402 response.SC_INTERNAL_SERVER_ERROR, 403 "errorCreateArtifactResolve", 404 SAML2Utils.bundle.getString("errorCreateArtifactResolve")); 405 throw s2e; 406 } catch (SOAPException se) { 407 SAML2Utils.debug.error("SPACSUtils.getResponseFromGet: " 408 + "couldn't get ArtifactResponse. SOAP error:",se); 409 String[] data = {hostEntityId, location}; 410 LogUtil.error(Level.INFO, 411 LogUtil.CANNOT_GET_SOAP_RESPONSE, 412 data, 413 null); 414 SAMLUtils.sendError(request, response, 415 response.SC_INTERNAL_SERVER_ERROR, 416 "errorInSOAPCommunication", 417 SAML2Utils.bundle.getString("errorInSOAPCommunication")); 418 throw new SAML2Exception(se.getMessage()); 419 } 420 421 Response result = getResponseFromSOAP(resMsg, resolve, request, 422 response, idpEntityID, idp, orgName, hostEntityId, sm); 423 String[] data = {hostEntityId, idpEntityID, 424 art.getArtifactValue(), ""}; 425 if (LogUtil.isAccessLoggable(Level.FINE)) { 426 data[3] = result.toXMLString(); 427 } 428 LogUtil.access(Level.INFO, 429 LogUtil.GOT_RESPONSE_FROM_ARTIFACT, 430 data, 431 null); 432 return result; 433 } 434 435 // Finds the IDP who sends the artifact; 436 private static String getIDPEntityID( 437 Artifact art, 438 HttpServletRequest request, 439 HttpServletResponse response, 440 String orgName, 441 SAML2MetaManager metaManager) 442 throws SAML2Exception,IOException 443 { 444 String sourceID = art.getSourceID(); 445 // find the idp 446 String idpEntityID = null; 447 try { 448 Iterator iter = 449 metaManager.getAllRemoteIdentityProviderEntities(orgName). 450 iterator(); 451 String tmpSourceID = null; 452 while (iter.hasNext()) { 453 idpEntityID = (String) iter.next(); 454 tmpSourceID = SAML2Utils.generateSourceID(idpEntityID); 455 if (sourceID.equals(tmpSourceID)) { 456 break; 457 } 458 idpEntityID = null; 459 } 460 if (idpEntityID == null) { 461 SAML2Utils.debug.error("SPACSUtils.getResponseFromGet: Unable " 462 + "to find the IDP based on the SourceID in the artifact"); 463 String[] data = {art.getArtifactValue(), orgName}; 464 LogUtil.error(Level.INFO, 465 LogUtil.IDP_NOT_FOUND, 466 data, 467 null); 468 throw new SAML2Exception( 469 SAML2Utils.bundle.getString("cannotFindIDP")); 470 } 471 } catch (SAML2Exception se) { 472 String[] data = {art.getArtifactValue(), orgName}; 473 LogUtil.error(Level.INFO, 474 LogUtil.IDP_NOT_FOUND, 475 data, 476 null); 477 SAMLUtils.sendError(request, response, 478 response.SC_INTERNAL_SERVER_ERROR, 479 "cannotFindIDP", se.getMessage()); 480 throw se; 481 } 482 return idpEntityID; 483 } 484 485 // Retrieves the ArtifactResolutionServiceURL for an IDP. 486 private static String getIDPArtifactResolutionServiceUrl( 487 int endpointIndex, 488 String idpEntityID, 489 IDPSSODescriptorElement idp, 490 HttpServletRequest request, 491 HttpServletResponse response) 492 throws SAML2Exception,IOException 493 { 494 // find the artifact resolution service url 495 List arsList=idp.getArtifactResolutionService(); 496 ArtifactResolutionServiceElement ars = null; 497 String location = null; 498 String defaultLocation = null; 499 String firstLocation = null; 500 int index; 501 boolean isDefault = false; 502 for (int i=0; i<arsList.size(); i++) { 503 ars = (ArtifactResolutionServiceElement)arsList.get(i); 504 location = ars.getLocation(); 505 //String binding = ars.getBinding(); 506 index = ars.getIndex(); 507 isDefault = ars.isIsDefault(); 508 if (index == endpointIndex) { 509 break; 510 } 511 if (isDefault) { 512 defaultLocation = location; 513 } 514 if (i==0) { 515 firstLocation = location; 516 } 517 location = null; 518 } 519 if (location == null || location.length() == 0) { 520 location = defaultLocation; 521 if (location == null || location.length() == 0) { 522 location = firstLocation; 523 if (location == null || location.length() == 0) { 524 SAML2Utils.debug.error("SPACSUtils: Unable to get the " 525 + "location of artifact resolution service for " 526 + idpEntityID); 527 String[] data = {idpEntityID}; 528 LogUtil.error(Level.INFO, 529 LogUtil.ARTIFACT_RESOLUTION_URL_NOT_FOUND, 530 data, 531 null); 532 SAMLUtils.sendError(request, response, 533 response.SC_INTERNAL_SERVER_ERROR, 534 "cannotFindArtifactResolutionUrl", 535 SAML2Utils.bundle.getString( 536 "cannotFindArtifactResolutionUrl")); 537 throw new SAML2Exception( 538 SAML2Utils.bundle.getString( 539 "cannotFindArtifactResolutionUrl")); 540 } 541 } 542 } 543 if (SAML2Utils.debug.messageEnabled()) { 544 SAML2Utils.debug.message("SPACSUtils: IDP artifact resolution " 545 + "service url =" + location); 546 } 547 return location; 548 } 549 550 /** 551 * Obtains <code>SAML Response</code> from <code>SOAPBody</code>. 552 * Used by Artifact profile. 553 */ 554 private static Response getResponseFromSOAP(SOAPMessage resMsg, 555 ArtifactResolve resolve, 556 HttpServletRequest request, 557 HttpServletResponse response, 558 String idpEntityID, 559 IDPSSODescriptorElement idp, 560 String orgName, 561 String hostEntityId, 562 SAML2MetaManager sm) 563 throws SAML2Exception,IOException 564 { 565 String method = "SPACSUtils.getResponseFromSOAP:"; 566 Element resElem = null; 567 try { 568 resElem = SOAPCommunicator.getInstance().getSamlpElement(resMsg, "ArtifactResponse"); 569 } catch (SAML2Exception se) { 570 String[] data = {idpEntityID}; 571 LogUtil.error(Level.INFO, 572 LogUtil.SOAP_ERROR, 573 data, 574 null); 575 SAMLUtils.sendError(request, response, 576 response.SC_INTERNAL_SERVER_ERROR, 577 "soapError", se.getMessage()); 578 throw se; 579 } 580 ArtifactResponse artiResp = null; 581 try { 582 artiResp = ProtocolFactory.getInstance(). 583 createArtifactResponse(resElem); 584 } catch (SAML2Exception se) { 585 if (SAML2Utils.debug.messageEnabled()) { 586 SAML2Utils.debug.message(method + "Couldn't create " 587 + "ArtifactResponse:", se); 588 } 589 String[] data = {idpEntityID}; 590 LogUtil.error(Level.INFO, 591 LogUtil.CANNOT_INSTANTIATE_ARTIFACT_RESPONSE, 592 data, 593 null); 594 SAMLUtils.sendError(request, response, 595 response.SC_INTERNAL_SERVER_ERROR, 596 "failedToCreateArtifactResponse", se.getMessage()); 597 throw se; 598 } 599 600 if (artiResp == null) { 601 String[] data = {idpEntityID}; 602 LogUtil.error(Level.INFO, 603 LogUtil.MISSING_ARTIFACT_RESPONSE, 604 data, 605 null); 606 SAMLUtils.sendError(request, response, 607 response.SC_INTERNAL_SERVER_ERROR, 608 "missingArtifactResponse", 609 SAML2Utils.bundle.getString("missingArtifactResponse")); 610 throw new SAML2Exception( 611 SAML2Utils.bundle.getString("missingArtifactResponse")); 612 } else { 613 if (SAML2Utils.debug.messageEnabled()) { 614 SAML2Utils.debug.message(method + "Received ArtifactResponse:" 615 + artiResp.toXMLString(true, true)); 616 } 617 } 618 619 // verify ArtifactResponse 620 String wantArtiRespSigned = getAttributeValueFromSPSSOConfig( 621 orgName, 622 hostEntityId, 623 sm, 624 SAML2Constants.WANT_ARTIFACT_RESPONSE_SIGNED); 625 if (wantArtiRespSigned != null && wantArtiRespSigned.equals("true")) { 626 Set<X509Certificate> verificationCerts = KeyUtil.getVerificationCerts(idp, idpEntityID, 627 SAML2Constants.IDP_ROLE); 628 if (!artiResp.isSigned() || !artiResp.isSignatureValid(verificationCerts)) { 629 if (SAML2Utils.debug.messageEnabled()) { 630 SAML2Utils.debug.message(method 631 + "ArtifactResponse's signature is invalid."); 632 } 633 String[] data = {idpEntityID}; 634 LogUtil.error(Level.INFO, 635 LogUtil.ARTIFACT_RESPONSE_INVALID_SIGNATURE, 636 data, 637 null); 638 SAMLUtils.sendError(request, response, 639 response.SC_INTERNAL_SERVER_ERROR, "invalidSignature", 640 SAML2Utils.bundle.getString("invalidSignature")); 641 throw new SAML2Exception( 642 SAML2Utils.bundle.getString("invalidSignature")); 643 } 644 } 645 646 String inResponseTo = artiResp.getInResponseTo(); 647 if (inResponseTo == null || !inResponseTo.equals(resolve.getID())) { 648 if (SAML2Utils.debug.messageEnabled()) { 649 SAML2Utils.debug.message(method 650 + "ArtifactResponse's InResponseTo is invalid."); 651 } 652 String[] data = {idpEntityID}; 653 LogUtil.error(Level.INFO, 654 LogUtil.ARTIFACT_RESPONSE_INVALID_INRESPONSETO, 655 data, 656 null); 657 SAMLUtils.sendError(request, response, 658 response.SC_INTERNAL_SERVER_ERROR, "invalidInResponseTo", 659 SAML2Utils.bundle.getString("invalidInResponseTo")); 660 throw new SAML2Exception( 661 SAML2Utils.bundle.getString("invalidInResponseTo")); 662 } 663 664 Issuer idpIssuer = artiResp.getIssuer(); 665 if (idpIssuer == null || !idpIssuer.getValue().equals(idpEntityID)) { 666 if (SAML2Utils.debug.messageEnabled()) { 667 SAML2Utils.debug.message(method 668 + "ArtifactResponse's Issuer is invalid."); 669 } 670 String[] data = {idpEntityID}; 671 LogUtil.error(Level.INFO, 672 LogUtil.ARTIFACT_RESPONSE_INVALID_ISSUER, 673 data, 674 null); 675 SAMLUtils.sendError(request, response, 676 response.SC_INTERNAL_SERVER_ERROR, "invalidIssuer", 677 SAML2Utils.bundle.getString("invalidIssuer")); 678 throw new SAML2Exception( 679 SAML2Utils.bundle.getString("invalidIssuer")); 680 } 681 682 // check time? 683 684 Status status = artiResp.getStatus(); 685 if (status == null || !status.getStatusCode().getValue().equals( 686 SAML2Constants.SUCCESS)) 687 { 688 String statusCode = 689 (status == null)?"":status.getStatusCode().getValue(); 690 if (SAML2Utils.debug.messageEnabled()) { 691 SAML2Utils.debug.message(method 692 + "ArtifactResponse's status code is not success." 693 + statusCode); 694 } 695 String[] data = {idpEntityID, ""}; 696 if (LogUtil.isErrorLoggable(Level.FINE)) { 697 data[1] = statusCode; 698 } 699 LogUtil.error(Level.INFO, 700 LogUtil.ARTIFACT_RESPONSE_INVALID_STATUS_CODE, 701 data, 702 null); 703 SAMLUtils.sendError(request, response, 704 response.SC_INTERNAL_SERVER_ERROR, "invalidStatusCode", 705 SAML2Utils.bundle.getString("invalidStatusCode")); 706 throw new SAML2Exception( 707 SAML2Utils.bundle.getString("invalidStatusCode")); 708 } 709 710 try { 711 return ProtocolFactory.getInstance().createResponse( 712 artiResp.getAny()); 713 } catch (SAML2Exception se) { 714 if (SAML2Utils.debug.messageEnabled()) { 715 SAML2Utils.debug.message(method 716 + "couldn't instantiate Response:", se); 717 } 718 String[] data = {idpEntityID}; 719 LogUtil.error(Level.INFO, 720 LogUtil.CANNOT_INSTANTIATE_RESPONSE_ARTIFACT, 721 data, 722 null); 723 SAMLUtils.sendError(request, response, 724 response.SC_INTERNAL_SERVER_ERROR, 725 "failedToCreateResponse", se.getMessage()); 726 throw se; 727 } 728 } 729 730 /** 731 * Obtains <code>SAML Response</code> from <code>SOAPBody</code>. 732 * Used by ECP profile. 733 */ 734 private static ResponseInfo getResponseFromPostECP( 735 HttpServletRequest request, HttpServletResponse response, 736 String orgName, String hostEntityId, SAML2MetaManager metaManager) 737 throws SAML2Exception,IOException 738 { 739 Message message = null; 740 try { 741 message = new Message(SOAPCommunicator.getInstance().getSOAPMessage(request)); 742 } catch (SOAPException soapex) { 743 String[] data = { hostEntityId } ; 744 LogUtil.error(Level.INFO, 745 LogUtil.CANNOT_INSTANTIATE_SOAP_MESSAGE_ECP, data, null); 746 SAMLUtils.sendError(request, response, 747 response.SC_INTERNAL_SERVER_ERROR, 748 "failedToCreateSOAPMessage", soapex.getMessage()); 749 throw new SAML2Exception(soapex.getMessage()); 750 } catch (SOAPBindingException soapex) { 751 String[] data = { hostEntityId } ; 752 LogUtil.error(Level.INFO, 753 LogUtil.CANNOT_INSTANTIATE_SOAP_MESSAGE_ECP, data, null); 754 SAMLUtils.sendError(request, response, 755 response.SC_INTERNAL_SERVER_ERROR, 756 "failedToCreateSOAPMessage", soapex.getMessage()); 757 throw new SAML2Exception(soapex.getMessage()); 758 } catch(SOAPFaultException sfex) { 759 String[] data = { hostEntityId } ; 760 LogUtil.error(Level.INFO, LogUtil.RECEIVE_SOAP_FAULT_ECP, 761 data, null); 762 String faultString = 763 sfex.getSOAPFaultMessage().getSOAPFault().getFaultString(); 764 SAMLUtils.sendError(request, response, 765 response.SC_INTERNAL_SERVER_ERROR, 766 "failedToCreateSOAPMessage", faultString); 767 throw new SAML2Exception(faultString); 768 } 769 770 List soapHeaders = message.getOtherSOAPHeaders(); 771 ECPRelayState ecpRelayState = null; 772 if ((soapHeaders != null) && (!soapHeaders.isEmpty())) { 773 for(Iterator iter = soapHeaders.iterator(); iter.hasNext();) { 774 Element headerEle = (Element)iter.next(); 775 try { 776 ecpRelayState = 777 ECPFactory.getInstance().createECPRelayState(headerEle); 778 break; 779 } catch (SAML2Exception saml2ex) { 780 // not ECP RelayState 781 } 782 } 783 } 784 String relayState = null; 785 if (ecpRelayState != null) { 786 relayState = ecpRelayState.getValue(); 787 } 788 789 List soapBodies = message.getBodies(); 790 if ((soapBodies == null) || (soapBodies.isEmpty())) { 791 String[] data = { hostEntityId } ; 792 LogUtil.error(Level.INFO, 793 LogUtil.CANNOT_INSTANTIATE_SAML_RESPONSE_FROM_ECP, data, null); 794 SAMLUtils.sendError(request, response, response.SC_BAD_REQUEST, 795 "missingSAMLResponse", 796 SAML2Utils.bundle.getString("missingSAMLResponse")); 797 throw new SAML2Exception( 798 SAML2Utils.bundle.getString("missingSAMLResponse")); 799 } 800 801 Element resElem = (Element)soapBodies.get(0); 802 803 Response resp = null; 804 try { 805 resp = ProtocolFactory.getInstance().createResponse(resElem); 806 } catch (SAML2Exception se) { 807 if (SAML2Utils.debug.messageEnabled()) { 808 SAML2Utils.debug.message("SPACSUtils.getResponseFromPostECP:" + 809 "Couldn't create Response:", se); 810 } 811 String[] data = { hostEntityId } ; 812 LogUtil.error(Level.INFO, 813 LogUtil.CANNOT_INSTANTIATE_SAML_RESPONSE_FROM_ECP, data, null); 814 SAMLUtils.sendError(request, response, 815 response.SC_INTERNAL_SERVER_ERROR, 816 "failedToCreateResponse", se.getMessage()); 817 throw se; 818 } 819 820 String idpEntityID = resp.getIssuer().getValue(); 821 IDPSSODescriptorElement idpDesc = null; 822 try { 823 idpDesc = metaManager.getIDPSSODescriptor(orgName, idpEntityID); 824 } catch (SAML2MetaException se) { 825 String[] data = { orgName, idpEntityID }; 826 LogUtil.error(Level.INFO, LogUtil.IDP_META_NOT_FOUND, data, null); 827 SAMLUtils.sendError(request, response, 828 response.SC_INTERNAL_SERVER_ERROR, 829 "failedToGetIDPSSODescriptor", se.getMessage()); 830 throw se; 831 } 832 833 Set<X509Certificate> certificates = KeyUtil.getVerificationCerts(idpDesc, idpEntityID, SAML2Constants.IDP_ROLE); 834 List assertions = resp.getAssertion(); 835 if ((assertions != null) && (!assertions.isEmpty())) { 836 for(Iterator iter = assertions.iterator(); iter.hasNext(); ) { 837 Assertion assertion = (Assertion)iter.next(); 838 if (!assertion.isSigned()) { 839 if (SAML2Utils.debug.messageEnabled()) { 840 SAML2Utils.debug.message( 841 "SPACSUtils.getResponseFromPostECP: " + 842 " Assertion is not signed."); 843 } 844 String[] data = { idpEntityID }; 845 LogUtil.error(Level.INFO, 846 LogUtil.ECP_ASSERTION_NOT_SIGNED, data, null); 847 SAMLUtils.sendError(request, response, 848 response.SC_INTERNAL_SERVER_ERROR, 849 "assertionNotSigned", 850 SAML2Utils.bundle.getString("assertionNotSigned")); 851 throw new SAML2Exception( 852 SAML2Utils.bundle.getString("assertionNotSigned")); 853 } else if (!assertion.isSignatureValid(certificates)) { 854 if (SAML2Utils.debug.messageEnabled()) { 855 SAML2Utils.debug.message( 856 "SPACSUtils.getResponseFromPostECP: " + 857 " Assertion signature is invalid."); 858 } 859 String[] data = { idpEntityID }; 860 LogUtil.error(Level.INFO, 861 LogUtil.ECP_ASSERTION_INVALID_SIGNATURE, data, null); 862 SAMLUtils.sendError(request, response, 863 response.SC_INTERNAL_SERVER_ERROR, 864 "invalidSignature", 865 SAML2Utils.bundle.getString("invalidSignature")); 866 throw new SAML2Exception( 867 SAML2Utils.bundle.getString("invalidSignature")); 868 } 869 } 870 } 871 872 return new ResponseInfo(resp, SAML2Constants.PAOS, relayState); 873 } 874 875 // Obtains SAML Response from POST. 876 private static ResponseInfo getResponseFromPost(HttpServletRequest request, 877 HttpServletResponse response, String orgName, String hostEntityId, 878 SAML2MetaManager metaManager) throws SAML2Exception,IOException 879 { 880 String classMethod = "SPACSUtils:getResponseFromPost"; 881 SAML2Utils.debug.message("SPACSUtils:getResponseFromPost"); 882 883 String samlArt = request.getParameter(SAML2Constants.SAML_ART); 884 if ((samlArt != null) && (samlArt.trim().length() != 0)) { 885 return new ResponseInfo(getResponseFromArtifact(samlArt, 886 hostEntityId, request, response, orgName, metaManager), 887 SAML2Constants.HTTP_ARTIFACT, null); 888 } 889 890 String samlResponse = request.getParameter( 891 SAML2Constants.SAML_RESPONSE); 892 if (samlResponse == null) { 893 LogUtil.error(Level.INFO, 894 LogUtil.MISSING_SAML_RESPONSE_FROM_POST, 895 null, 896 null); 897 SAMLUtils.sendError(request, response, response.SC_BAD_REQUEST, 898 "missingSAMLResponse", 899 SAML2Utils.bundle.getString("missingSAMLResponse")); 900 throw new SAML2Exception( 901 SAML2Utils.bundle.getString("missingSAMLResponse")); 902 } 903 904 // Get Response back 905 // decode the Response 906 Response resp = null; 907 ByteArrayInputStream bis = null; 908 try { 909 byte[] raw = Base64.decode(samlResponse); 910 if (raw != null) { 911 bis = new ByteArrayInputStream(raw); 912 Document doc = XMLUtils.toDOMDocument(bis, SAML2Utils.debug); 913 if (doc != null) { 914 resp = ProtocolFactory.getInstance(). 915 createResponse(doc.getDocumentElement()); 916 } 917 } 918 } catch (SAML2Exception se) { 919 SAML2Utils.debug.error("SPACSUtils.getResponse: Exception " 920 + "when instantiating SAMLResponse:", se); 921 LogUtil.error(Level.INFO, 922 LogUtil.CANNOT_INSTANTIATE_RESPONSE_POST, 923 null, 924 null); 925 SAMLUtils.sendError(request, response, response.SC_BAD_REQUEST, 926 "errorObtainResponse", 927 SAML2Utils.bundle.getString("errorObtainResponse")); 928 throw new SAML2Exception( 929 SAML2Utils.bundle.getString("errorObtainResponse")); 930 931 } catch (Exception e) { 932 SAML2Utils.debug.error("SPACSUtils.getResponse: Exception " 933 + "when decoding SAMLResponse:", e); 934 LogUtil.error(Level.INFO, 935 LogUtil.CANNOT_DECODE_RESPONSE, 936 null, 937 null); 938 SAMLUtils.sendError(request, response, 939 response.SC_INTERNAL_SERVER_ERROR, "errorDecodeResponse", 940 SAML2Utils.bundle.getString("errorDecodeResponse")); 941 throw new SAML2Exception( 942 SAML2Utils.bundle.getString("errorDecodeResponse")); 943 } finally { 944 if (bis != null) { 945 try { 946 bis.close(); 947 } catch (Exception ie) { 948 if (SAML2Utils.debug.messageEnabled()) { 949 SAML2Utils.debug.message("SPACSUtils.getResponse: " 950 + "Exception when close the input stream:", ie); 951 } 952 } 953 } 954 } 955 956 if (resp != null) { 957 String[] data = {""}; 958 if (LogUtil.isAccessLoggable(Level.FINE)) { 959 data[0] = resp.toXMLString(); 960 } 961 LogUtil.access(Level.INFO, 962 LogUtil.GOT_RESPONSE_FROM_POST, 963 data, 964 null); 965 return (new ResponseInfo(resp, SAML2Constants.HTTP_POST, null)); 966 } 967 if (SAML2Utils.debug.messageEnabled()) { 968 SAML2Utils.debug.message("SPACSUtils.getResponse: Decoded response, " + 969 "resp is null"); 970 } 971 return null; 972 } 973 974 /** 975 * Authenticates user with <code>Response</code>. 976 * Auth session upgrade will be called if input session is 977 * not null. 978 * Otherwise, saml2 auth module is called. The name of the auth module 979 * is retrieved from <code>SPSSOConfig</code>. If not found, "SAML2" will 980 * be used. 981 * 982 * @param request HTTP Servlet request 983 * @param response HTTP Servlet response. 984 * @param out the print writer for writing out presentation 985 * @param metaAlias metaAlias for the service provider 986 * @param session input session object. It could be null. 987 * @param respInfo <code>ResponseInfo</code> to be verified. 988 * @param realm realm or organization name of the service provider. 989 * @param hostEntityId hosted service provider Entity ID. 990 * @param metaManager <code>SAML2MetaManager</code> instance for meta operation. 991 * @param auditor a <code>SAML2EventLogger</code> auditor object to hook into 992 * tracking information for the saml request 993 * @return <code>Object</code> which holds result of the session. 994 * @throws SAML2Exception if the processing failed. 995 */ 996 public static Object processResponse( 997 HttpServletRequest request, HttpServletResponse response, PrintWriter out, 998 String metaAlias, Object session, ResponseInfo respInfo, 999 String realm, String hostEntityId, SAML2MetaManager metaManager, SAML2EventLogger auditor 1000 ) throws SAML2Exception { 1001 1002 String classMethod = "SPACSUtils.processResponse: "; 1003 if (SAML2Utils.debug.messageEnabled()) { 1004 SAML2Utils.debug.message(classMethod + "Response : " + 1005 respInfo.getResponse()); 1006 } 1007 Map smap = null; 1008 try { 1009 // check Response/Assertion and get back a Map of relevant data 1010 smap = SAML2Utils.verifyResponse(request, response, 1011 respInfo.getResponse(), realm, hostEntityId, 1012 respInfo.getProfileBinding()); 1013 } catch (SAML2Exception se) { 1014 // invoke SPAdapter for failure 1015 invokeSPAdapterForSSOFailure(hostEntityId, realm, 1016 request, response, smap, respInfo, 1017 SAML2ServiceProviderAdapter.INVALID_RESPONSE, se); 1018 throw se; 1019 } 1020 1021 com.sun.identity.saml2.assertion.Subject assertionSubject = 1022 (com.sun.identity.saml2.assertion.Subject) 1023 smap.get(SAML2Constants.SUBJECT); 1024 NameID nameId = assertionSubject.getNameID(); 1025 EncryptedID encId = assertionSubject.getEncryptedID(); 1026 Assertion authnAssertion = 1027 (Assertion) smap.get(SAML2Constants.POST_ASSERTION); 1028 String sessionIndex = (String)smap.get(SAML2Constants.SESSION_INDEX); 1029 respInfo.setSessionIndex(sessionIndex); 1030 Integer authLevel = (Integer) smap.get(SAML2Constants.AUTH_LEVEL); 1031 Long maxSessionTime = (Long) smap.get(SAML2Constants.MAX_SESSION_TIME); 1032 String inRespToResp = (String) smap.get(SAML2Constants.IN_RESPONSE_TO); 1033 List assertions = (List) smap.get(SAML2Constants.ASSERTIONS); 1034 1035 if (SAML2Utils.debug.messageEnabled()) { 1036 SAML2Utils.debug.message(classMethod + "Assertions : " + 1037 assertions); 1038 } 1039 1040 SPSSOConfigElement spssoconfig = 1041 metaManager.getSPSSOConfig(realm, hostEntityId); 1042 1043 // get mappers 1044 SPAccountMapper acctMapper = SAML2Utils.getSPAccountMapper(realm, hostEntityId); 1045 SPAttributeMapper attrMapper = SAML2Utils.getSPAttributeMapper(realm, hostEntityId); 1046 1047 String assertionEncryptedAttr = 1048 SAML2Utils.getAttributeValueFromSPSSOConfig(spssoconfig, SAML2Constants.WANT_ASSERTION_ENCRYPTED); 1049 1050 boolean needAttributeEncrypted = getNeedAttributeEncrypted(assertionEncryptedAttr, spssoconfig); 1051 boolean needNameIDEncrypted = getNeedNameIDEncrypted(assertionEncryptedAttr, spssoconfig); 1052 1053 Set<PrivateKey> decryptionKeys = KeyUtil.getDecryptionKeys(spssoconfig); 1054 if (needNameIDEncrypted && encId == null) { 1055 SAML2Utils.debug.error(classMethod + 1056 "process: NameID was not encrypted."); 1057 SAML2Exception se = new SAML2Exception(SAML2Utils.bundle.getString( 1058 "nameIDNotEncrypted")); 1059 // invoke SPAdapter for failure 1060 invokeSPAdapterForSSOFailure(hostEntityId, realm, 1061 request, response, smap, respInfo, 1062 SAML2ServiceProviderAdapter.INVALID_RESPONSE, se); 1063 throw se; 1064 } 1065 if (encId != null) { 1066 try { 1067 nameId = encId.decrypt(decryptionKeys); 1068 } catch (SAML2Exception se) { 1069 // invoke SPAdapter for failure 1070 invokeSPAdapterForSSOFailure(hostEntityId, realm, 1071 request, response, smap, respInfo, 1072 SAML2ServiceProviderAdapter.INVALID_RESPONSE, se); 1073 throw se; 1074 } 1075 } 1076 respInfo.setNameId(nameId); 1077 1078 SPSSODescriptorElement spDesc = null; 1079 try { 1080 spDesc = metaManager.getSPSSODescriptor(realm, hostEntityId); 1081 } catch (SAML2MetaException ex) { 1082 SAML2Utils.debug.error(classMethod, ex); 1083 } 1084 if (spDesc == null) { 1085 SAML2Exception se = new SAML2Exception(SAML2Utils.bundle.getString( 1086 "metaDataError")); 1087 invokeSPAdapterForSSOFailure(hostEntityId, realm, request, 1088 response, smap, respInfo, 1089 SAML2ServiceProviderAdapter.SSO_FAILED_META_DATA_ERROR, se); 1090 throw se; 1091 } 1092 String nameIDFormat = nameId.getFormat(); 1093 if (nameIDFormat != null) { 1094 List spNameIDFormatList = spDesc.getNameIDFormat(); 1095 1096 if ((spNameIDFormatList != null) && (!spNameIDFormatList.isEmpty()) 1097 && (!spNameIDFormatList.contains(nameIDFormat))) { 1098 1099 Object[] args = { nameIDFormat }; 1100 SAML2Exception se = new SAML2Exception(SAML2Utils.BUNDLE_NAME, 1101 "unsupportedNameIDFormatSP", args); 1102 1103 invokeSPAdapterForSSOFailure(hostEntityId, realm, request, 1104 response, smap, respInfo, 1105 SAML2ServiceProviderAdapter.INVALID_RESPONSE, se); 1106 throw se; 1107 } 1108 } 1109 1110 boolean isTransient = SAML2Constants.NAMEID_TRANSIENT_FORMAT.equals(nameIDFormat); 1111 boolean isPersistent = SAML2Constants.PERSISTENT.equals(nameIDFormat); 1112 boolean ignoreProfile = SAML2PluginsUtils.isIgnoredProfile(realm); 1113 String existUserName = null; 1114 SessionProvider sessionProvider = null; 1115 try { 1116 sessionProvider = SessionManager.getProvider(); 1117 } catch (SessionException se) { 1118 // invoke SPAdapter for failure 1119 SAML2Exception se2 = new SAML2Exception(se); 1120 invokeSPAdapterForSSOFailure(hostEntityId, realm, 1121 request, response, smap, respInfo, 1122 SAML2ServiceProviderAdapter.SSO_FAILED_SESSION_ERROR, se2); 1123 throw se2; 1124 } 1125 if (session != null) { 1126 try { 1127 existUserName = sessionProvider.getPrincipalName(session); 1128 } catch (SessionException se) { 1129 // invoke SPAdapter for failure 1130 SAML2Exception se2 = new SAML2Exception(se); 1131 invokeSPAdapterForSSOFailure(hostEntityId, realm, request, response, smap, respInfo, 1132 SAML2ServiceProviderAdapter.SSO_FAILED_SESSION_ERROR, se2); 1133 throw se2; 1134 } 1135 } 1136 1137 String remoteHostId = authnAssertion.getIssuer().getValue(); 1138 String userName = null; 1139 boolean isNewAccountLink = false; 1140 boolean shouldPersistNameID = isPersistent || (!isTransient && !ignoreProfile 1141 && acctMapper.shouldPersistNameIDFormat(realm, hostEntityId, remoteHostId, nameIDFormat)); 1142 try { 1143 if (shouldPersistNameID) { 1144 if (SAML2Utils.debug.messageEnabled()) { 1145 SAML2Utils.debug.message(classMethod + "querying data store for existing federation links: realm = " 1146 + realm + " hostEntityID = " + hostEntityId + " remoteEntityID = " + remoteHostId); 1147 } 1148 1149 try { 1150 userName = SAML2Utils.getDataStoreProvider().getUserID(realm, SAML2Utils.getNameIDKeyMap( 1151 nameId, hostEntityId, remoteHostId, realm, SAML2Constants.SP_ROLE)); 1152 } catch (DataStoreProviderException dse) { 1153 SAML2Utils.debug.error(classMethod + "DataStoreProviderException whilst retrieving NameID " + 1154 "information", dse); 1155 throw new SAML2Exception(dse.getMessage()); 1156 } 1157 } 1158 if (userName == null) { 1159 userName = acctMapper.getIdentity(authnAssertion, hostEntityId, realm); 1160 isNewAccountLink = true; 1161 } 1162 } catch (SAML2Exception se) { 1163 // invoke SPAdapter for failure 1164 invokeSPAdapterForSSOFailure(hostEntityId, realm, request, response, smap, respInfo, 1165 SAML2ServiceProviderAdapter.SSO_FAILED_NO_USER_MAPPING, se); 1166 throw se; 1167 } 1168 1169 if (userName == null && respInfo.isLocalLogin()) { 1170 // In case we just got authenticated locally, we should accept the freshly authenticated session's principal 1171 // as the username corresponding to the received assertion. 1172 userName = existUserName; 1173 } 1174 if (null != auditor) { 1175 auditor.setUserId(userName); 1176 } 1177 if (SAML2Utils.debug.messageEnabled()) { 1178 SAML2Utils.debug.message( 1179 classMethod + "process: userName =[" + userName + "]"); 1180 } 1181 List attrs = null; 1182 for (Iterator it = assertions.iterator(); it.hasNext(); ) { 1183 Assertion assertion = (Assertion)it.next(); 1184 List origAttrs = getSAMLAttributes(assertion, needAttributeEncrypted, decryptionKeys); 1185 if (origAttrs != null && !origAttrs.isEmpty()) { 1186 if (attrs == null) { 1187 attrs = new ArrayList(); 1188 } 1189 attrs.addAll(origAttrs); 1190 } 1191 } 1192 Map attrMap = null; 1193 if (attrs != null) { 1194 try { 1195 attrMap = attrMapper.getAttributes(attrs, userName, 1196 hostEntityId, remoteHostId, realm); 1197 } catch (SAML2Exception se) { 1198 // invoke SPAdapter for failure 1199 invokeSPAdapterForSSOFailure(hostEntityId, realm, 1200 request, response, smap, respInfo, 1201 SAML2ServiceProviderAdapter.SSO_FAILED_ATTRIBUTE_MAPPING, 1202 se); 1203 throw se; 1204 } 1205 } 1206 if (SAML2Utils.debug.messageEnabled()) { 1207 SAML2Utils.debug.message( 1208 classMethod + "process: remoteHostId = " + remoteHostId); 1209 SAML2Utils.debug.message( 1210 classMethod + "process: attrMap = " + attrMap); 1211 } 1212 respInfo.setAttributeMap(attrMap); 1213 1214 // return error code for local user login 1215 if (StringUtils.isEmpty(userName)) { 1216 // If we couldn't determine the username based on the incoming assertion, then we shouldn't automatically 1217 // map the user to the existing session. 1218 if (session != null) { 1219 try { 1220 sessionProvider.invalidateSession(session, request, response); 1221 } catch (SessionException se) { 1222 SAML2Utils.debug.error("An error occurred while trying to invalidate session", se); 1223 } 1224 } 1225 throw new SAML2Exception(SAML2Utils.bundle.getString("noUserMapping")); 1226 } 1227 1228 boolean writeFedInfo = isNewAccountLink && shouldPersistNameID; 1229 1230 if (SAML2Utils.debug.messageEnabled()) { 1231 SAML2Utils.debug.message( 1232 classMethod + "userName : " + userName); 1233 SAML2Utils.debug.message( 1234 classMethod + "writeFedInfo : " + writeFedInfo); 1235 } 1236 AuthnRequest authnRequest = null; 1237 if (smap != null) { 1238 authnRequest = (AuthnRequest) 1239 smap.get(SAML2Constants.AUTHN_REQUEST); 1240 } 1241 if (inRespToResp != null && inRespToResp.length() != 0) { 1242 SPCache.requestHash.remove(inRespToResp); 1243 } 1244 Map sessionInfoMap = new HashMap(); 1245 sessionInfoMap.put(SessionProvider.REALM, realm); 1246 sessionInfoMap.put(SessionProvider.PRINCIPAL_NAME, userName); 1247 // set client info. always use client IP address to prevent 1248 // reverse host lookup 1249 String clientAddr = ClientUtils.getClientIPAddress(request); 1250 sessionInfoMap.put(SessionProvider.HOST, clientAddr); 1251 sessionInfoMap.put(SessionProvider.HOST_NAME, clientAddr); 1252 sessionInfoMap.put(SessionProvider.AUTH_LEVEL, 1253 String.valueOf(authLevel)); 1254 request.setAttribute(SessionProvider.ATTR_MAP, attrMap); 1255 try { 1256 session = sessionProvider.createSession( 1257 sessionInfoMap, request, response, null); 1258 } catch (SessionException se) { 1259 // invoke SPAdapter for failure 1260 int failureCode = 1261 SAML2ServiceProviderAdapter.SSO_FAILED_SESSION_GENERATION; 1262 int sessCode = se.getErrCode(); 1263 if (sessCode == SessionException.AUTH_USER_INACTIVE) { 1264 failureCode = 1265 SAML2ServiceProviderAdapter.SSO_FAILED_AUTH_USER_INACTIVE; 1266 } else if (sessCode == SessionException.AUTH_USER_LOCKED) { 1267 failureCode = 1268 SAML2ServiceProviderAdapter.SSO_FAILED_AUTH_USER_LOCKED; 1269 } else if (sessCode == SessionException.AUTH_ACCOUNT_EXPIRED) { 1270 failureCode = 1271 SAML2ServiceProviderAdapter.SSO_FAILED_AUTH_ACCOUNT_EXPIRED; 1272 } 1273 if (SAML2Utils.debug.messageEnabled()) { 1274 SAML2Utils.debug.message( 1275 "SPACSUtils.processResponse : error code=" + sessCode, se); 1276 } 1277 SAML2Exception se2 = new SAML2Exception(se); 1278 invokeSPAdapterForSSOFailure(hostEntityId, realm, 1279 request, response, smap, respInfo, failureCode, se2); 1280 throw se2; 1281 } 1282 1283 // set metaAlias 1284 String[] values = { metaAlias }; 1285 try { 1286 setAttrMapInSession(sessionProvider, attrMap, session); 1287 setDiscoBootstrapCredsInSSOToken(sessionProvider, authnAssertion, 1288 session); 1289 sessionProvider.setProperty( 1290 session, SAML2Constants.SP_METAALIAS, values); 1291 } catch (SessionException se) { 1292 // invoke SPAdapter for failure 1293 SAML2Exception se2 = new SAML2Exception(se); 1294 invokeSPAdapterForSSOFailure(hostEntityId, realm, 1295 request, response, smap, respInfo, 1296 SAML2ServiceProviderAdapter.SSO_FAILED_SESSION_ERROR, se2); 1297 throw se2; 1298 } 1299 1300 NameIDInfo info = null; 1301 String affiID = nameId.getSPNameQualifier(); 1302 boolean isDualRole = SAML2Utils.isDualRole(hostEntityId, realm); 1303 AffiliationDescriptorType affiDesc = null; 1304 if (affiID != null && !affiID.isEmpty()) { 1305 affiDesc = metaManager.getAffiliationDescriptor(realm, affiID); 1306 } 1307 1308 if (affiDesc != null) { 1309 if (!affiDesc.getAffiliateMember().contains(hostEntityId)) { 1310 throw new SAML2Exception(SAML2Utils.bundle.getString( 1311 "spNotAffiliationMember")); 1312 } 1313 if (isDualRole) { 1314 info = new NameIDInfo(affiID, remoteHostId, nameId, 1315 SAML2Constants.DUAL_ROLE, true); 1316 } else { 1317 info = new NameIDInfo(affiID, remoteHostId, nameId, 1318 SAML2Constants.SP_ROLE, true); 1319 } 1320 } else { 1321 if (isDualRole) { 1322 info = new NameIDInfo(hostEntityId, remoteHostId, nameId, 1323 SAML2Constants.DUAL_ROLE, false); 1324 } else { 1325 info = new NameIDInfo(hostEntityId, remoteHostId, nameId, 1326 SAML2Constants.SP_ROLE, false); 1327 } 1328 } 1329 Map props = new HashMap(); 1330 String nameIDValueString = info.getNameIDValue(); 1331 props.put(LogUtil.NAME_ID, info.getNameIDValue()); 1332 try { 1333 userName = sessionProvider.getPrincipalName(session); 1334 } catch (SessionException se) { 1335 // invoke SPAdapter for failure 1336 SAML2Exception se2 = new SAML2Exception(se); 1337 invokeSPAdapterForSSOFailure(hostEntityId, realm, 1338 request, response, smap, respInfo, 1339 SAML2ServiceProviderAdapter.SSO_FAILED_SESSION_ERROR, se2); 1340 throw se2; 1341 } 1342 String[] data1 = {userName, nameIDValueString}; 1343 LogUtil.access(Level.INFO, LogUtil.SUCCESS_FED_SSO, data1, session, 1344 props); 1345 // write fed info into data store 1346 if (writeFedInfo) { 1347 try { 1348 AccountUtils.setAccountFederation(info, userName); 1349 } catch (SAML2Exception se) { 1350 // invoke SPAdapter for failure 1351 invokeSPAdapterForSSOFailure(hostEntityId, realm, 1352 request, response, smap, respInfo, 1353 SAML2ServiceProviderAdapter.FEDERATION_FAILED_WRITING_ACCOUNT_INFO, se); 1354 throw se; 1355 } 1356 String[] data = {userName, ""}; 1357 if (LogUtil.isAccessLoggable(Level.FINE)) { 1358 data[1] = info.toValueString(); 1359 } 1360 LogUtil.access(Level.INFO, 1361 LogUtil.FED_INFO_WRITTEN, 1362 data, 1363 session, 1364 props); 1365 } 1366 String requestID = respInfo.getResponse().getInResponseTo(); 1367 // save info in memory for logout 1368 saveInfoInMemory(sessionProvider, session, sessionIndex, metaAlias, 1369 info, IDPProxyUtil.isIDPProxyEnabled(requestID), isTransient); 1370 1371 // invoke SP Adapter 1372 SAML2ServiceProviderAdapter spAdapter = 1373 SAML2Utils.getSPAdapterClass(hostEntityId, realm); 1374 if (spAdapter != null) { 1375 boolean redirected = spAdapter.postSingleSignOnSuccess( 1376 hostEntityId, realm, request, 1377 response, out, session, authnRequest, respInfo.getResponse(), 1378 respInfo.getProfileBinding(), writeFedInfo); 1379 String[] value = null; 1380 if (redirected) { 1381 value = new String[] {"true"}; 1382 } else { 1383 value = new String[] {"false"}; 1384 } 1385 try { 1386 sessionProvider.setProperty(session, 1387 SAML2Constants.RESPONSE_REDIRECTED, value); 1388 } catch (SessionException ex) { 1389 SAML2Utils.debug.warning("SPSingleLogout.processResp", ex); 1390 } catch (UnsupportedOperationException ex) { 1391 SAML2Utils.debug.warning("SPSingleLogout.processResp", ex); 1392 } 1393 } 1394 1395 String assertionID=authnAssertion.getID(); 1396 if (respInfo.getProfileBinding().equals(SAML2Constants.HTTP_POST)) { 1397 SPCache.assertionByIDCache.put(assertionID, SAML2Constants.ONETIME); 1398 try { 1399 if (SAML2FailoverUtils.isSAML2FailoverEnabled()) { 1400 SAML2FailoverUtils.saveSAML2TokenWithoutSecondaryKey( 1401 assertionID, 1402 SAML2Constants.ONETIME, 1403 ((Long) smap.get(SAML2Constants.NOTONORAFTER)).longValue() / 1000); 1404 } 1405 } catch (SAML2TokenRepositoryException se) { 1406 SAML2Utils.debug.error(classMethod + 1407 "There was a problem saving the assertionID to the SAML2 Token Repository for assertionID:" 1408 + assertionID, se); 1409 } 1410 } 1411 respInfo.setAssertion(authnAssertion); 1412 1413 return session; 1414 } 1415 1416 private static boolean getNeedNameIDEncrypted(String assertionEncryptedAttr, SPSSOConfigElement spssoconfig) { 1417 if (Boolean.parseBoolean(assertionEncryptedAttr)) { 1418 String idEncryptedStr = SAML2Utils.getAttributeValueFromSPSSOConfig(spssoconfig, 1419 SAML2Constants.WANT_NAMEID_ENCRYPTED); 1420 if (Boolean.parseBoolean(idEncryptedStr)) { 1421 return true; 1422 } 1423 } 1424 1425 return false; 1426 } 1427 1428 public static boolean getNeedAttributeEncrypted(String assertionEncryptedAttr, SPSSOConfigElement spssoconfig) { 1429 if (Boolean.parseBoolean(assertionEncryptedAttr)) { 1430 String attrEncryptedStr = 1431 SAML2Utils.getAttributeValueFromSPSSOConfig(spssoconfig, SAML2Constants.WANT_ATTRIBUTE_ENCRYPTED); 1432 if (Boolean.parseBoolean(attrEncryptedStr)) { 1433 return true; 1434 } 1435 } 1436 1437 return false; 1438 } 1439 1440 private static void invokeSPAdapterForSSOFailure(String hostEntityId, 1441 String realm, HttpServletRequest request, HttpServletResponse response, 1442 Map smap, ResponseInfo respInfo, int errorCode, 1443 SAML2Exception se) { 1444 SAML2ServiceProviderAdapter spAdapter = null; 1445 try { 1446 spAdapter = SAML2Utils.getSPAdapterClass(hostEntityId, realm); 1447 } catch (SAML2Exception e) { 1448 if (SAML2Utils.debug.messageEnabled()) { 1449 SAML2Utils.debug.message( 1450 "SPACSUtils.invokeSPAdapterForSSOFailure", e); 1451 } 1452 } 1453 if (spAdapter != null) { 1454 AuthnRequest authnRequest = null; 1455 if (smap != null) { 1456 authnRequest = (AuthnRequest) 1457 smap.get(SAML2Constants.AUTHN_REQUEST); 1458 } 1459 boolean redirected = spAdapter.postSingleSignOnFailure( 1460 hostEntityId, realm, request, response, authnRequest, 1461 respInfo.getResponse(), respInfo.getProfileBinding(), 1462 errorCode); 1463 se.setRedirectionDone(redirected); 1464 } 1465 } 1466 1467 public static void saveInfoInMemory(SessionProvider sessionProvider, 1468 Object session, String sessionIndex, String metaAlias, 1469 NameIDInfo info, boolean isIDPProxy, boolean isTransient) 1470 throws SAML2Exception { 1471 1472 String infoKeyString = (new NameIDInfoKey( 1473 info.getNameIDValue(), 1474 info.getHostEntityID(), 1475 info.getRemoteEntityID())).toValueString(); 1476 String infoKeyAttribute = 1477 AccountUtils.getNameIDInfoKeyAttribute(); 1478 String[] fromToken = null; 1479 try { 1480 fromToken = sessionProvider. 1481 getProperty(session, infoKeyAttribute); 1482 if (fromToken == null || fromToken.length == 0 || 1483 fromToken[0] == null || fromToken[0].length() == 0) { 1484 String[] values = { infoKeyString }; 1485 sessionProvider.setProperty( 1486 session, infoKeyAttribute, values); 1487 } else { 1488 if (fromToken[0].indexOf(infoKeyString) == -1) { 1489 String[] values = { fromToken[0] + 1490 SAML2Constants.SECOND_DELIM + 1491 infoKeyString }; 1492 sessionProvider.setProperty( 1493 session, infoKeyAttribute, values); 1494 } 1495 } 1496 if (isTransient) { 1497 String nameIDInfoStr = info.toValueString(); 1498 String infoAttribute = AccountUtils.getNameIDInfoAttribute(); 1499 String[] nameIDInfoStrs = sessionProvider.getProperty(session, 1500 infoAttribute); 1501 if (nameIDInfoStrs == null) { 1502 nameIDInfoStrs = new String[1]; 1503 nameIDInfoStrs[0] = nameIDInfoStr; 1504 } else { 1505 Set nameIDInfoStrSet = new HashSet(); 1506 for(int i=0; i<nameIDInfoStrs.length; i++) { 1507 nameIDInfoStrSet.add(nameIDInfoStrs[i]); 1508 } 1509 nameIDInfoStrSet.add(nameIDInfoStr); 1510 nameIDInfoStrs = (String[])nameIDInfoStrSet.toArray( 1511 new String[nameIDInfoStrSet.size()]); 1512 } 1513 sessionProvider.setProperty(session, infoAttribute, 1514 nameIDInfoStrs); 1515 } 1516 } catch (SessionException sessE) { 1517 throw new SAML2Exception(sessE); 1518 } 1519 String tokenID = sessionProvider.getSessionID(session); 1520 if (!SPCache.isFedlet) { 1521 List fedSessions = (List) 1522 SPCache.fedSessionListsByNameIDInfoKey.get(infoKeyString); 1523 if (fedSessions == null) { 1524 synchronized (SPCache.fedSessionListsByNameIDInfoKey) { 1525 fedSessions = (List) 1526 SPCache.fedSessionListsByNameIDInfoKey.get(infoKeyString); 1527 if (fedSessions == null) { 1528 fedSessions = new ArrayList(); 1529 } 1530 } 1531 synchronized (fedSessions) { 1532 fedSessions.add(new SPFedSession(sessionIndex, tokenID, 1533 info, metaAlias)); 1534 SPCache.fedSessionListsByNameIDInfoKey.put( 1535 infoKeyString, fedSessions); 1536 } 1537 if ((agent != null) && agent.isRunning() && (saml2Svc != null)){ 1538 saml2Svc.setFedSessionCount( 1539 (long)SPCache.fedSessionListsByNameIDInfoKey.size()); 1540 } 1541 1542 if (isIDPProxy) { 1543 //IDP Proxy 1544 IDPSession idpSess = (IDPSession) 1545 IDPCache.idpSessionsBySessionID.get( 1546 tokenID); 1547 if (idpSess == null) { 1548 idpSess = new IDPSession(session); 1549 IDPCache.idpSessionsBySessionID.put( 1550 tokenID, idpSess); 1551 } 1552 if (SAML2Utils.debug.messageEnabled()) { 1553 SAML2Utils.debug.message("Add Session Partner: " + 1554 info.getRemoteEntityID()); 1555 } 1556 idpSess.addSessionPartner(new SAML2SessionPartner( 1557 info.getRemoteEntityID(), true)); 1558 // end of IDP Proxy 1559 } 1560 } else { 1561 synchronized (fedSessions) { 1562 Iterator iter = fedSessions.iterator(); 1563 boolean found = false; 1564 while (iter.hasNext()) { 1565 SPFedSession temp = (SPFedSession) iter.next(); 1566 String idpSessionIndex = null; 1567 if(temp != null) { 1568 idpSessionIndex = temp.idpSessionIndex; 1569 } 1570 if ((idpSessionIndex != null) && 1571 (idpSessionIndex.equals(sessionIndex))) { 1572 temp.spTokenID = tokenID; 1573 temp.info = info; 1574 found = true; 1575 break; 1576 } 1577 } 1578 if (!found) { 1579 fedSessions.add( 1580 new SPFedSession(sessionIndex, tokenID, info, 1581 metaAlias)); 1582 SPCache.fedSessionListsByNameIDInfoKey.put( 1583 infoKeyString, fedSessions); 1584 if ((agent != null) && 1585 agent.isRunning() && 1586 (saml2Svc != null)) 1587 { 1588 saml2Svc.setFedSessionCount( 1589 (long)SPCache.fedSessionListsByNameIDInfoKey. 1590 size()); 1591 } 1592 } 1593 } 1594 } 1595 SPCache.fedSessionListsByNameIDInfoKey.put(infoKeyString, 1596 fedSessions); 1597 if ((agent != null) && agent.isRunning() && (saml2Svc != null)) { 1598 saml2Svc.setFedSessionCount( 1599 (long)SPCache.fedSessionListsByNameIDInfoKey.size()); 1600 } 1601 } 1602 try { 1603 sessionProvider.addListener( 1604 session, new SPSessionListener(infoKeyString, tokenID)); 1605 } catch (SessionException e) { 1606 SAML2Utils.debug.error( 1607 "SPACSUtils.saveInfoInMemory: "+ 1608 "Unable to add session listener."); 1609 } 1610 } 1611 1612 /** Sets the attribute map in the session 1613 * 1614 * @param sessionProvider Session provider 1615 * @param attrMap the Attribute Map 1616 * @param session the valid session object 1617 * @throws com.sun.identity.plugin.session.SessionException 1618 */ 1619 public static void setAttrMapInSession( 1620 SessionProvider sessionProvider, 1621 Map attrMap, Object session) 1622 throws SessionException { 1623 if (attrMap != null && !attrMap.isEmpty()) { 1624 Set entrySet = attrMap.entrySet(); 1625 for(Iterator iter = entrySet.iterator(); iter.hasNext();) { 1626 Map.Entry entry = (Map.Entry)iter.next(); 1627 String attrName = (String)entry.getKey(); 1628 Set attrValues = (Set)entry.getValue(); 1629 if(attrValues != null && !attrValues.isEmpty()) { 1630 sessionProvider.setProperty( 1631 session, attrName, 1632 (String[]) attrValues.toArray( 1633 new String[attrValues.size()])); 1634 if (SAML2Utils.debug.messageEnabled()) { 1635 SAML2Utils.debug.message( 1636 "SPACSUtils.setAttrMapInSessioin: AttrMap:" + 1637 attrName + " , " + attrValues); 1638 } 1639 } 1640 } 1641 } 1642 } 1643 1644 /** Sets Discovery bootstrap credentials in the SSOToken 1645 * 1646 * @param sessionProvider session provider. 1647 * @param assertion assertion. 1648 * @param session the valid session object. 1649 */ 1650 private static void setDiscoBootstrapCredsInSSOToken( 1651 SessionProvider sessionProvider, Assertion assertion, Object session) 1652 throws SessionException { 1653 1654 if (assertion == null) { 1655 return; 1656 } 1657 1658 Set discoBootstrapCreds = null; 1659 Advice advice = assertion.getAdvice(); 1660 if (advice != null) { 1661 List creds = advice.getAdditionalInfo(); 1662 if ((creds != null) && !creds.isEmpty()) { 1663 if (discoBootstrapCreds == null) { 1664 discoBootstrapCreds = new HashSet(); 1665 } 1666 discoBootstrapCreds.addAll(creds); 1667 } 1668 } 1669 1670 if (discoBootstrapCreds != null) { 1671 sessionProvider.setProperty(session, 1672 SAML2Constants.DISCOVERY_BOOTSTRAP_CREDENTIALS, 1673 (String[])discoBootstrapCreds.toArray( 1674 new String[discoBootstrapCreds.size()])); 1675 } 1676 } 1677 1678 /** 1679 * Obtains relay state. Retrieves the relay state from relay state cache. 1680 * If input relay state is null, retrieve it from <code>SPSSOConfig</code>. 1681 * 1682 * @param relayStateID relay state value received from http request. 1683 * @param orgName realm or organization name the service provider resides in 1684 * @param hostEntityId Entity ID of the hosted service provider 1685 * @param sm <code>SAML2MetaManager</code> instance. 1686 * @return final relay state. Or <code>null</code> if the input 1687 * relayStateID is null and no default relay state is configured. 1688 */ 1689 public static String getRelayState( 1690 String relayStateID, 1691 String orgName, 1692 String hostEntityId, 1693 SAML2MetaManager sm 1694 ) { 1695 String relayStateUrl = null; 1696 1697 if ((relayStateID != null) && (relayStateID.trim().length() != 0)) { 1698 CacheObject cache = (CacheObject)SPCache.relayStateHash.remove( 1699 relayStateID); 1700 1701 if (cache != null) { 1702 relayStateUrl = (String)cache.getObject(); 1703 } else if (SAML2FailoverUtils.isSAML2FailoverEnabled()) { 1704 // The key is this way to make it unique compared to when 1705 // the same key is used to store a copy of the AuthnRequestInfo 1706 String key = relayStateID + relayStateID; 1707 try { 1708 // Try and retrieve the value from the SAML2 repository 1709 String relayState = (String) SAML2FailoverUtils.retrieveSAML2Token(key); 1710 if (relayState != null) { 1711 // Get back the relayState 1712 relayStateUrl = relayState; 1713 if (SAML2Utils.debug.messageEnabled()) { 1714 SAML2Utils.debug.message("SPACUtils.getRelayState: relayState" 1715 + " retrieved from SAML2 repository for key: " + key); 1716 } 1717 } 1718 } catch (SAML2TokenRepositoryException se) { 1719 SAML2Utils.debug.error("SPACUtils.getRelayState: Unable to retrieve relayState for key " 1720 + key, se); 1721 } 1722 } else { 1723 if (SAML2Utils.debug.messageEnabled()) { 1724 SAML2Utils.debug.message("SPACUtils.getRelayState: relayState" 1725 + " is null for relayStateID: " + relayStateID + ", SAML2 failover is disabled"); 1726 } 1727 } 1728 1729 if (relayStateUrl == null || relayStateUrl.trim().length() == 0) { 1730 relayStateUrl = relayStateID; 1731 } 1732 } 1733 1734 if (relayStateUrl == null || relayStateUrl.trim().length() == 0) { 1735 relayStateUrl = getAttributeValueFromSPSSOConfig( 1736 orgName, hostEntityId, sm, SAML2Constants.DEFAULT_RELAY_STATE); 1737 } 1738 1739 return relayStateUrl; 1740 } 1741 1742 /** 1743 * Retrieves intermediate redirect url from SP sso config. This url is used 1744 * if you want to goto some place before the final relay state. 1745 * 1746 * @param orgName realm or organization name the service provider resides in 1747 * @param hostEntityId Entity ID of the hosted service provider 1748 * @param sm <code>SAML2MetaManager</code> instance. 1749 * @return intermediate redirect url; or <code>null</code> if the url is 1750 * is not configured or an error occured during the retrieval 1751 * process. 1752 */ 1753 public static String getIntermediateURL(String orgName, 1754 String hostEntityId, 1755 SAML2MetaManager sm) 1756 { 1757 return getAttributeValueFromSPSSOConfig(orgName, hostEntityId, sm, 1758 SAML2Constants.INTERMEDIATE_URL); 1759 } 1760 1761 /** 1762 * Saves response for later retrieval and retrieves local auth url from 1763 * <code>SPSSOConfig</code>. 1764 * If the url does not exist, generate one from request URI. 1765 * If still cannot get it, (shouldn't happen), get it from 1766 * <code>AMConfig.properties</code>. 1767 * 1768 * @param orgName realm or organization name the service provider resides in 1769 * @param hostEntityId Entity ID of the hosted service provider 1770 * @param sm <code>SAML2MetaManager</code> instance to perform meta 1771 * operation. 1772 * @param respInfo to be cached <code>ResponseInfo</code>. 1773 * @param requestURI http request URI. 1774 * @return local login url. 1775 */ 1776 public static String prepareForLocalLogin( 1777 String orgName, 1778 String hostEntityId, 1779 SAML2MetaManager sm, 1780 ResponseInfo respInfo, 1781 String requestURI) 1782 { 1783 String localLoginUrl = getAttributeValueFromSPSSOConfig( 1784 orgName, hostEntityId, sm, SAML2Constants.LOCAL_AUTH_URL); 1785 if ((localLoginUrl == null) || (localLoginUrl.length() == 0)) { 1786 // get it from request 1787 try { 1788 int index = requestURI.indexOf("Consumer/metaAlias"); 1789 if (index != -1) { 1790 localLoginUrl = requestURI.substring(0, index) 1791 + "UI/Login?org=" 1792 + orgName; 1793 } 1794 } catch (IndexOutOfBoundsException e) { 1795 localLoginUrl = null; 1796 } 1797 if ((localLoginUrl == null) || (localLoginUrl.length() == 0)) { 1798 // shouldn't be here, but in case 1799 localLoginUrl = 1800 SystemConfigurationUtil.getProperty(SAMLConstants.SERVER_PROTOCOL) 1801 + "://" 1802 + SystemConfigurationUtil.getProperty(SAMLConstants.SERVER_HOST) 1803 + SystemConfigurationUtil.getProperty(SAMLConstants.SERVER_PORT) 1804 + "/UI/Login?org=" 1805 + orgName; 1806 } 1807 } 1808 1809 respInfo.setIsLocalLogin(true); 1810 synchronized (SPCache.responseHash) { 1811 SPCache.responseHash.put(respInfo.getResponse().getID(), 1812 respInfo); 1813 } 1814 if (SAML2Utils.debug.messageEnabled()) { 1815 SAML2Utils.debug.message("SPACSUtils:prepareForLocalLogin: " + 1816 "localLoginUrl = " + localLoginUrl); 1817 } 1818 return localLoginUrl; 1819 } 1820 1821 /** 1822 * Retrieves attribute value for a given attribute name from 1823 * <code>SPSSOConfig</code>. 1824 * @param orgName realm or organization name the service provider resides in 1825 * @param hostEntityId hosted service provider's Entity ID. 1826 * @param sm <code>SAML2MetaManager</code> instance to perform meta 1827 * operations. 1828 * @param attrName name of the attribute whose value ot be retrived. 1829 * @return value of the attribute; or <code>null</code> if the attribute 1830 * if not configured, or an error occured in the process. 1831 */ 1832 private static String getAttributeValueFromSPSSOConfig(String orgName, 1833 String hostEntityId, 1834 SAML2MetaManager sm, 1835 String attrName) 1836 { 1837 String result = null; 1838 try { 1839 SPSSOConfigElement config = sm.getSPSSOConfig(orgName, 1840 hostEntityId); 1841 if (config == null) { 1842 return null; 1843 } 1844 Map attrs = SAML2MetaUtils.getAttributes(config); 1845 List value = (List) attrs.get(attrName); 1846 if (value != null && value.size() != 0) { 1847 result = ((String) value.iterator().next()).trim(); 1848 } 1849 } catch (SAML2MetaException sme) { 1850 if (SAML2Utils.debug.messageEnabled()) { 1851 SAML2Utils.debug.message("SPACSUtils.getAttributeValueFromSPSSO" 1852 + "Config:", sme); 1853 } 1854 result = null; 1855 } 1856 return result; 1857 } 1858 1859 /** 1860 * Gets the attributes from an assert's AttributeStates. 1861 * 1862 * @param assertion The assertion from which to pull the AttributeStates. 1863 * @param needAttributeEncrypted Whether attributes must be encrypted (or else rejected). 1864 * @param privateKeys Private keys used to decrypt those encrypted attributes. 1865 * @return a list of attributes pulled from the provided assertion. 1866 */ 1867 public static List<Attribute> getSAMLAttributes(Assertion assertion, boolean needAttributeEncrypted, 1868 Set<PrivateKey> privateKeys) { 1869 List<Attribute> attrList = null; 1870 if (assertion != null) { 1871 List<AttributeStatement> statements = assertion.getAttributeStatements(); 1872 if (CollectionUtils.isNotEmpty(statements)) { 1873 for (AttributeStatement statement : statements) { 1874 List<Attribute> attributes = statement.getAttribute(); 1875 if (needAttributeEncrypted && attributes != null && !attributes.isEmpty()) { 1876 SAML2Utils.debug.error("Attribute not encrypted."); 1877 return null; 1878 } 1879 if (attributes != null) { 1880 if (attrList == null) { 1881 attrList = new ArrayList<>(); 1882 } 1883 attrList.addAll(attributes); 1884 } 1885 List<EncryptedAttribute> encAttrs = statement.getEncryptedAttribute(); 1886 if (encAttrs != null) { 1887 for (EncryptedAttribute encAttr : encAttrs) { 1888 if (attrList == null) { 1889 attrList = new ArrayList<>(); 1890 } 1891 try { 1892 attrList.add((encAttr).decrypt(privateKeys)); 1893 } catch (SAML2Exception se) { 1894 SAML2Utils.debug.error("Decryption error:", se); 1895 return null; 1896 } 1897 } 1898 } 1899 } 1900 } 1901 } 1902 return attrList; 1903 } 1904 1905 /** 1906 * Processes response from Identity Provider to Fedlet (SP). 1907 * This will do all required protocol processing, include signature, 1908 * issuer and audience validation etc. A map containing processing 1909 * result will be returned. <br> 1910 * Here is a list of keys and values for the returned map: <br> 1911 * SAML2Constants.ATTRIBUTE_MAP -- Attribute map containing all attributes 1912 * passed down from IDP inside the 1913 * Assertion. The value is a 1914 * <code>java.util.Map</code> whose keys 1915 * are attribute names and values are 1916 * <code>java.util.Set</code> of string 1917 * values for the attributes. <br> 1918 * SAML2Constants.RELAY_STATE -- Relay state, value is a string <br> 1919 * SAML2Constants.IDPENTITYID -- IDP entity ID, value is a string<br> 1920 * SAML2Constants.RESPONSE -- Response object, value is an instance of 1921 * com.sun.identity.saml2.protocol.Response 1922 * SAML2Constants.ASSERTION -- Assertion object, value is an instance of 1923 * com.sun.identity.saml2.assertion.Assertion 1924 * SAML2Constants.SUBJECT -- Subject object, value is an instance of 1925 * com.sun.identity.saml2.assertion.Subject 1926 * SAML2Constants.NAMEID -- NameID object, value is an instance of 1927 * com.sun.identity.saml2.assertion.NameID 1928 * 1929 * @param request HTTP Servlet request 1930 * @param response HTTP Servlet response. 1931 * @param out the print writer for writing out presentation 1932 * 1933 * @return <code>Map</code> which holds result of the processing. 1934 * @throws SAML2Exception if the processing failed due to server error. 1935 * @throws IOException if the processing failed due to IO error. 1936 * @throws SessionException if the processing failed due to session error. 1937 * @throws ServletException if the processing failed due to request error. 1938 * 1939 * @supported.api 1940 */ 1941 public static Map processResponseForFedlet (HttpServletRequest request, 1942 HttpServletResponse response, PrintWriter out) throws SAML2Exception, IOException, 1943 SessionException, ServletException { 1944 if ((request == null) || (response == null)) { 1945 throw new ServletException( 1946 SAML2SDKUtils.bundle.getString("nullInput")); 1947 } 1948 1949 String requestURL = request.getRequestURL().toString(); 1950 SAML2MetaManager metaManager = new SAML2MetaManager(); 1951 if (metaManager == null) { 1952 throw new SAML2Exception( 1953 SAML2SDKUtils.bundle.getString("errorMetaManager")); 1954 } 1955 String metaAlias = SAML2MetaUtils.getMetaAliasByUri(requestURL); 1956 if ((metaAlias == null) || (metaAlias.length() == 0)) { 1957 // Check in case metaAlias has been supplied as a parameter 1958 metaAlias = request.getParameter(SAML2MetaManager.NAME_META_ALIAS_IN_URI); 1959 if (metaAlias == null || metaAlias.length() == 0) { 1960 // pick the first available one 1961 List spMetaAliases = 1962 metaManager.getAllHostedServiceProviderMetaAliases("/"); 1963 if ((spMetaAliases != null) && !spMetaAliases.isEmpty()) { 1964 // get first one 1965 metaAlias = (String) spMetaAliases.get(0); 1966 } 1967 if ((metaAlias == null) || (metaAlias.length() == 0)) { 1968 throw new ServletException( 1969 SAML2SDKUtils.bundle.getString("nullSPEntityID")); 1970 } 1971 } 1972 } 1973 String hostEntityId = null; 1974 try { 1975 hostEntityId = metaManager.getEntityByMetaAlias(metaAlias); 1976 } catch (SAML2MetaException sme) { 1977 SAML2SDKUtils.debug.error("SPACSUtils.processResponseForFedlet", 1978 sme); 1979 throw new SAML2Exception( 1980 SAML2SDKUtils.bundle.getString("metaDataError")); 1981 } 1982 if (hostEntityId == null) { 1983 // logging? 1984 throw new SAML2Exception( 1985 SAML2SDKUtils.bundle.getString("metaDataError")); 1986 } 1987 // organization is always root org 1988 String orgName = "/"; 1989 String relayState = request.getParameter(SAML2Constants.RELAY_STATE); 1990 SessionProvider sessionProvider = null; 1991 ResponseInfo respInfo = null; 1992 try { 1993 sessionProvider = SessionManager.getProvider(); 1994 } catch (SessionException se) { 1995 SAML2SDKUtils.debug.error("SPACSUtils.processResponseForFedlet", 1996 se); 1997 throw new SAML2Exception(se); 1998 } 1999 respInfo = SPACSUtils.getResponse( 2000 request, response, orgName, hostEntityId, metaManager); 2001 2002 Object newSession = null; 2003 2004 // Throws a SAML2Exception if the response cannot be validated 2005 // or contains a non-Success StatusCode, invoking the SPAdapter SPI 2006 // for taking action on the failed validation. 2007 // The resulting exception has its redirectionDone flag set if 2008 // the SPAdapter issued a HTTP redirect. 2009 newSession = SPACSUtils.processResponse( 2010 request, response, out, metaAlias, null, respInfo, 2011 orgName, hostEntityId, metaManager, null); 2012 2013 SAML2SDKUtils.debug.message("SSO SUCCESS"); 2014 String[] redirected = sessionProvider.getProperty(newSession, 2015 SAML2Constants.RESPONSE_REDIRECTED); 2016 if ((redirected != null) && (redirected.length != 0) && 2017 redirected[0].equals("true")) { 2018 SAML2SDKUtils.debug.message("Already redirected in SPAdapter."); 2019 // response redirected already in SPAdapter 2020 return createMapForFedlet(respInfo, null, hostEntityId); 2021 } 2022 // redirect to relay state 2023 String finalUrl = SPACSUtils.getRelayState( 2024 relayState, orgName, hostEntityId, metaManager); 2025 String realFinalUrl = finalUrl; 2026 if (finalUrl != null && finalUrl.length() != 0) { 2027 try { 2028 realFinalUrl = 2029 sessionProvider.rewriteURL(newSession, finalUrl); 2030 } catch (SessionException se) { 2031 SAML2SDKUtils.debug.message("SPACSUtils.processRespForFedlet", 2032 se); 2033 realFinalUrl = finalUrl; 2034 } 2035 } 2036 String redirectUrl = SPACSUtils.getIntermediateURL( 2037 orgName, hostEntityId, metaManager); 2038 String realRedirectUrl = null; 2039 if (redirectUrl != null && redirectUrl.length() != 0) { 2040 if (realFinalUrl != null && realFinalUrl.length() != 0) { 2041 if (redirectUrl.indexOf("?") != -1) { 2042 redirectUrl += "&goto="; 2043 } else { 2044 redirectUrl += "?goto="; 2045 } 2046 redirectUrl += URLEncDec.encode(realFinalUrl); 2047 try { 2048 realRedirectUrl = sessionProvider.rewriteURL( 2049 newSession, redirectUrl); 2050 } catch (SessionException se) { 2051 SAML2SDKUtils.debug.message( 2052 "SPACSUtils.processRespForFedlet: rewriting failed.", se); 2053 realRedirectUrl = redirectUrl; 2054 } 2055 } else { 2056 realRedirectUrl = redirectUrl; 2057 } 2058 } else { 2059 realRedirectUrl = finalUrl; 2060 } 2061 return createMapForFedlet(respInfo, realRedirectUrl, hostEntityId); 2062 } 2063 2064 private static Map createMapForFedlet( 2065 ResponseInfo respInfo, String relayUrl, String hostedEntityId) { 2066 Map map = new HashMap(); 2067 if (relayUrl != null) { 2068 map.put(SAML2Constants.RELAY_STATE, relayUrl); 2069 } 2070 Response samlResp = respInfo.getResponse(); 2071 map.put(SAML2Constants.RESPONSE, samlResp); 2072 Assertion assertion = respInfo.getAssertion(); 2073 map.put(SAML2Constants.ASSERTION, assertion); 2074 map.put(SAML2Constants.SUBJECT, assertion.getSubject()); 2075 map.put(SAML2Constants.IDPENTITYID, assertion.getIssuer().getValue()); 2076 map.put(SAML2Constants.SPENTITYID, hostedEntityId); 2077 map.put(SAML2Constants.NAMEID, respInfo.getNameId()); 2078 map.put(SAML2Constants.ATTRIBUTE_MAP, respInfo.getAttributeMap()); 2079 map.put(SAML2Constants.SESSION_INDEX, respInfo.getSessionIndex()); 2080 return map; 2081 } 2082 2083 /** 2084 * Returns the username if there was one from the Assertion we were able to map into a local user account. Returns 2085 * null if not. Should only be used from the SP side. Should only be called in conjuncture with the Auth Module. 2086 * In addition, it performs what attribute federation it can. 2087 * 2088 * This method is a picked apart version of the "processResponse" function. 2089 */ 2090 public static String getPrincipalWithoutLogin(Subject assertionSubject, Assertion authnAssertion, String realm, 2091 String spEntityId, SAML2MetaManager metaManager, String idpEntityId, 2092 String storageKey) 2093 throws SAML2Exception { 2094 2095 final EncryptedID encId = assertionSubject.getEncryptedID(); 2096 final SPSSOConfigElement spssoconfig = metaManager.getSPSSOConfig(realm, spEntityId); 2097 final Set<PrivateKey> decryptionKeys = KeyUtil.getDecryptionKeys(spssoconfig); 2098 final SPAccountMapper acctMapper = SAML2Utils.getSPAccountMapper(realm, spEntityId); 2099 2100 boolean needNameIDEncrypted = false; 2101 NameID nameId = assertionSubject.getNameID(); 2102 2103 String assertionEncryptedAttr = 2104 SAML2Utils.getAttributeValueFromSPSSOConfig(spssoconfig, SAML2Constants.WANT_ASSERTION_ENCRYPTED); 2105 if (assertionEncryptedAttr == null || !Boolean.parseBoolean(assertionEncryptedAttr)) { 2106 String idEncryptedStr = 2107 SAML2Utils.getAttributeValueFromSPSSOConfig(spssoconfig, SAML2Constants.WANT_NAMEID_ENCRYPTED); 2108 if (idEncryptedStr != null && Boolean.parseBoolean(idEncryptedStr)) { 2109 needNameIDEncrypted = true; 2110 } 2111 } 2112 2113 if (needNameIDEncrypted && encId == null) { 2114 throw new SAML2Exception(SAML2Utils.bundle.getString("nameIDNotEncrypted")); 2115 } 2116 if (encId != null) { 2117 nameId = encId.decrypt(decryptionKeys); 2118 } 2119 2120 SPSSODescriptorElement spDesc = null; 2121 try { 2122 spDesc = metaManager.getSPSSODescriptor(realm, spEntityId); 2123 } catch (SAML2MetaException ex) { 2124 SAML2Utils.debug.error("Unable to read SPSSODescription", ex); 2125 } 2126 2127 if (spDesc == null) { 2128 throw new SAML2Exception(SAML2Utils.bundle.getString("metaDataError")); 2129 } 2130 2131 final String nameIDFormat = nameId.getFormat(); 2132 2133 if (nameIDFormat != null) { 2134 List spNameIDFormatList = spDesc.getNameIDFormat(); 2135 2136 if (CollectionUtils.isNotEmpty(spNameIDFormatList) && !spNameIDFormatList.contains(nameIDFormat)) { 2137 Object[] args = {nameIDFormat}; 2138 throw new SAML2Exception(SAML2Utils.BUNDLE_NAME, "unsupportedNameIDFormatSP", args); 2139 } 2140 } 2141 2142 final boolean isTransient = SAML2Constants.NAMEID_TRANSIENT_FORMAT.equals(nameIDFormat); 2143 final boolean isPersistent = SAML2Constants.PERSISTENT.equals(nameIDFormat); 2144 final boolean ignoreProfile = SAML2PluginsUtils.isIgnoredProfile(realm); 2145 2146 final boolean shouldPersistNameID = isPersistent || (!isTransient && !ignoreProfile 2147 && acctMapper.shouldPersistNameIDFormat(realm, spEntityId, idpEntityId, nameIDFormat)); 2148 2149 String userName = null; 2150 boolean isNewAccountLink = false; 2151 2152 try { 2153 if (shouldPersistNameID) { 2154 try { 2155 userName = SAML2Utils.getDataStoreProvider().getUserID(realm, SAML2Utils.getNameIDKeyMap( 2156 nameId, spEntityId, idpEntityId, realm, SAML2Constants.SP_ROLE)); 2157 } catch (DataStoreProviderException dse) { 2158 throw new SAML2Exception(dse.getMessage()); 2159 } 2160 } 2161 2162 //if we can't get an already linked account, see if we'll be generating a new one based on federated data 2163 if (userName == null) { 2164 userName = acctMapper.getIdentity(authnAssertion, spEntityId, realm); 2165 isNewAccountLink = true; //we'll use this later to inform us 2166 } 2167 } catch (SAML2Exception se) { 2168 return null; 2169 } 2170 2171 //if we're new and we're persistent, store the federation data in the user pref 2172 if (isNewAccountLink && isPersistent) { 2173 try { 2174 writeFedData(nameId, spEntityId, realm, metaManager, idpEntityId, userName, storageKey); 2175 } catch (SAML2Exception se) { 2176 return userName; 2177 } 2178 } 2179 2180 return userName; 2181 } 2182 2183 private static void writeFedData(NameID nameId, String spEntityId, String realm, SAML2MetaManager metaManager, 2184 String idpEntityId, String userName, String storageKey) throws SAML2Exception { 2185 final NameIDInfo info; 2186 final String affiID = nameId.getSPNameQualifier(); 2187 boolean isDualRole = SAML2Utils.isDualRole(spEntityId, realm); 2188 AffiliationDescriptorType affiDesc = null; 2189 2190 if (affiID != null && !affiID.isEmpty()) { 2191 affiDesc = metaManager.getAffiliationDescriptor(realm, affiID); 2192 } 2193 2194 if (affiDesc != null) { 2195 if (!affiDesc.getAffiliateMember().contains(spEntityId)) { 2196 throw new SAML2Exception("Unable to locate SP Entity ID in the affiliate descriptor."); 2197 } 2198 if (isDualRole) { 2199 info = new NameIDInfo(affiID, idpEntityId, nameId, SAML2Constants.DUAL_ROLE, true); 2200 } else { 2201 info = new NameIDInfo(affiID, idpEntityId, nameId, SAML2Constants.SP_ROLE, true); 2202 } 2203 } else { 2204 if (isDualRole) { 2205 info = new NameIDInfo(spEntityId, idpEntityId, nameId, SAML2Constants.DUAL_ROLE, false); 2206 } else { 2207 info = new NameIDInfo(spEntityId, idpEntityId, nameId, SAML2Constants.SP_ROLE, false); 2208 } 2209 } 2210 2211 // write fed info into data store 2212 SPCache.fedAccountHash.put(storageKey, "true"); 2213 AccountUtils.setAccountFederation(info, userName); 2214 } 2215 2216 /** 2217 * Gets the attributes for this assertion in a new List. 2218 * @param authnAssertion Assertion from which to reead the attributes. 2219 * @param needAttributeEncrypted Whether the attributes must be encrypted. 2220 * @param decryptionKeys The keys used to decrypt the attributes, if they're encrypted. 2221 * @return a List of the attributes in this assertion. 2222 */ 2223 public static List<Attribute> getAttrs(Assertion authnAssertion, boolean needAttributeEncrypted, 2224 Set<PrivateKey> decryptionKeys) { 2225 final List<Attribute> origAttrs = getSAMLAttributes(authnAssertion, needAttributeEncrypted, decryptionKeys); 2226 2227 List<Attribute> attrs = null; 2228 if (origAttrs != null && !origAttrs.isEmpty()) { 2229 attrs = new ArrayList<>(); 2230 attrs.addAll(origAttrs); 2231 } 2232 2233 return attrs; 2234 } 2235 2236}