Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
* $Id: ConfigurationInstanceImpl.java,v 1.12 2009/10/29 00:03:50 exu Exp $
*
* Portions Copyrighted 2015 ForgeRock AS.
* Portions Copyrighted 2026 3A Systems LLC.
*/

package com.sun.identity.plugin.configuration.impl;
Expand Down Expand Up @@ -89,6 +90,8 @@ public class ConfigurationInstanceImpl implements ConfigurationInstance {
serviceNameMap.put("MULTI_PROTOCOL","sunMultiFederationProtocol");
serviceNameMap.put("STS_CONFIG","sunFAMSTSService");
serviceNameMap.put("SAML2_CONFIG", "sunFAMSAML2Configuration");
// The realm's Valid goto URL list, for the protocols without an allow-list of their own.
serviceNameMap.put("VALIDATION", "validationService");
}

private SSOToken getSSOToken() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import com.sun.identity.saml.common.SAMLServiceManager;
import com.sun.identity.saml.common.SAMLUtils;
import com.sun.identity.saml.protocol.AssertionArtifact;
import org.openidentityplatform.openam.federation.plugins.RealmGotoUrlValidator;

import java.io.IOException;

Expand Down Expand Up @@ -341,7 +342,20 @@ private void ArtifactHandler( HttpServletRequest request,
Map attrMap = null;
try {
Map sessionAttr = SAMLUtils.processArtifact(arti, targeturl);
Object token = SAMLUtils.generateSession(request,
// TARGET comes from the request and is redirected to below; the
// realm's Valid goto URL list decides whether it may be, before
// any session is created for it.
if (!RealmGotoUrlValidator.isValid(targeturl,
(String) sessionAttr.get(SessionProvider.REALM))) {
SAMLUtils.debug.warning("SAMLAwareServlet.ArtifactHandler: "
+ "refusing a TARGET outside the realm's valid goto URLs");
SAMLUtils.sendError(request, response,
HttpServletResponse.SC_BAD_REQUEST,
"invalidTargetSite",
SAMLUtils.bundle.getString("invalidTargetSite"));
return;
}
Object token = SAMLUtils.generateSession(request,
response, sessionAttr);
} catch (Exception ex) {
SAMLUtils.debug.error("generateSession: ", ex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import com.sun.identity.saml.protocol.Response;
import com.sun.identity.saml.protocol.Status;
import com.sun.identity.saml.protocol.StatusCode;
import org.openidentityplatform.openam.federation.plugins.RealmGotoUrlValidator;

import java.io.IOException;
import java.io.PrintWriter;
Expand Down Expand Up @@ -436,6 +437,19 @@ public void doPost(HttpServletRequest request, HttpServletResponse response)
try {
Map sessionAttr = SAMLUtils.processResponse(
sResponse, target);
// TARGET comes from the request and is redirected to below; the
// realm's Valid goto URL list decides whether it may be, before
// any session is created for it.
if (!RealmGotoUrlValidator.isValid(target,
(String) sessionAttr.get(SessionProvider.REALM))) {
SAMLUtils.debug.warning("SAMLPOSTProfileServlet.doPost: "
+ "refusing a TARGET outside the realm's valid goto URLs");
SAMLUtils.sendError(request, response,
HttpServletResponse.SC_BAD_REQUEST,
"invalidTargetSite",
SAMLUtils.bundle.getString("invalidTargetSite"));
return;
}
Object token = SAMLUtils.generateSession(request,
response, sessionAttr);
} catch (Exception ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
* $Id: RPSigninRequest.java,v 1.9 2009/11/03 00:48:54 madan_ranganath Exp $
*
* Portions Copyrighted 2015-2016 ForgeRock AS.
* Portions Copyrighted 2025 3A Systems LLC.
* Portions Copyrighted 2025-2026 3A Systems LLC.
*/

package com.sun.identity.wsfederation.servlet;
Expand All @@ -51,6 +51,7 @@
import com.sun.identity.wsfederation.jaxb.wsfederation.FederationElement;
import com.sun.identity.wsfederation.meta.WSFederationMetaManager;
import com.sun.identity.wsfederation.meta.WSFederationMetaUtils;
import org.openidentityplatform.openam.federation.plugins.RealmGotoUrlValidator;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -97,12 +98,6 @@ public void process() throws WSFederationException, IOException
debug.message(classMethod+"entered method");
}

if (wctx == null || wctx.length() == 0){
// Exchange reply URL for opaque identifier
wctx = (wreply != null && (wreply.length() > 0)) ?
WSFederationUtils.putReplyURL(wreply) : null;
}

String spMetaAlias = WSFederationMetaUtils.getMetaAliasByUri(
request.getRequestURI());

Expand All @@ -112,7 +107,24 @@ public void process() throws WSFederationException, IOException
}

String spRealm = SAML2MetaUtils.getRealmByMetaAlias(spMetaAlias);


if (wctx == null || wctx.length() == 0){
if (wreply != null && (wreply.length() > 0)) {
// The browser is sent to wreply once the sign-in completes;
// the realm's Valid goto URL list decides whether it may be.
if (!RealmGotoUrlValidator.isValid(wreply, spRealm)) {
debug.warning(classMethod
+ "refusing a wreply outside the realm's valid goto URLs");
throw new WSFederationException(
WSFederationUtils.bundle.getString("invalidWreply"));
}
// Exchange reply URL for opaque identifier
wctx = WSFederationUtils.putReplyURL(wreply);
} else {
wctx = null;
}
}

WSFederationMetaManager metaManager =
WSFederationUtils.getMetaManager();
String spEntityId =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright 2026 3A Systems, LLC.
*/
package org.openidentityplatform.openam.federation.plugins;

import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import com.sun.identity.plugin.configuration.ConfigurationException;
import com.sun.identity.plugin.configuration.ConfigurationInstance;
import com.sun.identity.plugin.configuration.ConfigurationManager;
import com.sun.identity.shared.debug.Debug;
import org.forgerock.openam.shared.security.whitelist.ValidDomainExtractor;

/**
* The realm's <em>Valid goto URL</em> list, read through the federation configuration plugin.
* It is the list the login {@code goto} parameter is checked against; the federation library
* cannot reach the core extractor of that list, so this one reads the same service attribute
* through {@link ConfigurationManager}.
* <p>
* {@code null} means "no list": a realm that configures none, a realm without a configuration,
* a missing plugin or a read failure. The validator then restricts nothing, as it does for the
* login goto.
*/
public class RealmGotoUrlExtractor implements ValidDomainExtractor<String> {

/** The component name {@code ConfigurationInstanceImpl} maps to the validation service. */
public static final String VALIDATION_COMPONENT = "VALIDATION";
static final String VALID_GOTO_RESOURCES = "openam-auth-valid-goto-resources";
private static final String ROOT_REALM = "/";
private static final Debug DEBUG = Debug.getInstance("libSAML");

/** Where the validation service is read from; a seam for tests. */
interface ConfigurationSource {
ConfigurationInstance get() throws ConfigurationException;
}

private final ConfigurationSource source;

public RealmGotoUrlExtractor() {
this(new ConfigurationSource() {
@Override
public ConfigurationInstance get() throws ConfigurationException {
return ConfigurationManager.getConfigurationInstance(VALIDATION_COMPONENT);
}
});
}

RealmGotoUrlExtractor(ConfigurationSource source) {
this.source = source;
}

@Override
public Collection<String> extractValidDomains(String realm) {
try {
ConfigurationInstance instance = source.get();
if (instance == null) {
return null;
}
Map<?, ?> attributes = instance.getConfiguration(realm == null || realm.isEmpty() ? ROOT_REALM : realm, null);
if (attributes == null) {
return null;
}
Object values = attributes.get(VALID_GOTO_RESOURCES);
if (!(values instanceof Collection) || ((Collection<?>) values).isEmpty()) {
return null;
}
Set<String> patterns = new LinkedHashSet<>();
for (Object value : (Collection<?>) values) {
if (value != null) {
patterns.add(value.toString());
}
}
return patterns.isEmpty() ? null : patterns;
} catch (ConfigurationException e) {
DEBUG.error("RealmGotoUrlExtractor.extractValidDomains: cannot read the valid goto URLs of realm "
+ realm, e);
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright 2026 3A Systems, LLC.
*/
package org.openidentityplatform.openam.federation.plugins;

import org.forgerock.openam.shared.security.whitelist.RedirectUrlValidator;

/**
* Whether a URL supplied with a request may be redirected to once single sign-on has completed,
* for protocols whose own configuration carries no allow-list (SAML 1.x {@code TARGET}, WS-Federation
* {@code wreply}). The realm's <em>Valid goto URL</em> list decides, exactly as it does for the
* login {@code goto} parameter: a relative URL always passes, a scheme other than http(s) never
* does, and a realm that configures no list restricts nothing.
*/
public final class RealmGotoUrlValidator {

private static final String ROOT_REALM = "/";
private static final RedirectUrlValidator<String> DEFAULT_VALIDATOR =
new RedirectUrlValidator<String>(new RealmGotoUrlExtractor());
private static volatile RedirectUrlValidator<String> validator = DEFAULT_VALIDATOR;

private RealmGotoUrlValidator() {
}

/**
* @param url the URL taken from the request; {@code null} or empty is refused
* @param realm the realm the session was created in; {@code null} or empty reads as the root realm
* @return {@code true} if the realm's list admits {@code url}
*/
public static boolean isValid(String url, String realm) {
return validator.isRedirectUrlValid(url, realm == null || realm.isEmpty() ? ROOT_REALM : realm);
}

/** Test seam: {@code null} restores the validator that reads the realm configuration. */
static void setValidator(RedirectUrlValidator<String> replacement) {
validator = replacement == null ? DEFAULT_VALIDATOR : replacement;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
# $Id: libSAML.properties,v 1.6 2009/06/12 22:21:38 mallas Exp $
#
# Portions Copyrighted 2014 ForgeRock AS.
# Portions Copyrighted 2026 3A Systems LLC.

onlinehelp.doc=saml.html

Expand Down Expand Up @@ -137,6 +138,7 @@ noAssertioninResponse=No Assertion in SAML response.
URLNotFoundException=URLNotFoundException
illegalNamingService=Illegal naming service name.
missingTargetSite=The TARGET parameter is missing from the request.
invalidTargetSite=The TARGET is not an allowed redirect target of this realm.
nullInputParameter=The input parameter is null.
untrustedSite=Received Request from an untrusted site.
misconfigurationError=There is a misconfiguration in the data.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#
# Portions Copyrighted 2015-2016 ForgeRock AS.
# Portions Copyrighted 2016 Nomura Research Institute, Ltd.
# Portions Copyrighted 2026 3A Systems LLC.
#

nullConfig=Unable to get configuration instance for WSFederation meta service.
Expand Down Expand Up @@ -54,6 +55,7 @@ nullWresult=Null wresult.
nullWctx=Null wctx.
invalidWresult=Invalid wresult.
invalidMetaAlias=Invalid metaAlias {0} for realm {1}.
invalidWreply=The wreply URL is not an allowed redirect target of this realm.
invalidAssertion=Invalid Assertion.
badAccountRealm=Unexpected value for account realm.
noIDPConfigured=No IDP configured.
Expand Down
Loading
Loading