/*
- * The Shibboleth License, Version 1. Copyright (c) 2002 University Corporation for Advanced Internet Development, Inc.
- * All rights reserved Redistribution and use in source and binary forms, with or without modification, are permitted
- * provided that the following conditions are met: Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials
- * provided with the distribution, if any, must include the following acknowledgment: "This product includes software
- * developed by the University Corporation for Advanced Internet Development <http://www.ucaid.edu> Internet2 Project.
- * Alternately, this acknowledegement may appear in the software itself, if and wherever such third-party
- * acknowledgments normally appear. Neither the name of Shibboleth nor the names of its contributors, nor Internet2, nor
- * the University Corporation for Advanced Internet Development, Inc., nor UCAID may be used to endorse or promote
- * products derived from this software without specific prior written permission. For written permission, please contact
- * shibboleth@shibboleth.org Products derived from this software may not be called Shibboleth, Internet2, UCAID, or the
- * University Corporation for Advanced Internet Development, nor may Shibboleth appear in their name, without prior
- * written permission of the University Corporation for Advanced Internet Development. THIS SOFTWARE IS PROVIDED BY THE
- * COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE
- * DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. IN NO
- * EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC.
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
- * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package edu.internet2.middleware.shibboleth.idp.provider;
import java.io.IOException;
-import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
-import java.net.URL;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import org.opensaml.SAMLResponse;
import org.opensaml.SAMLStatement;
import org.opensaml.SAMLSubject;
+import org.opensaml.XML;
import org.w3c.dom.Element;
import edu.internet2.middleware.shibboleth.aa.AAException;
return "SAML v1.1 Attribute Query";
}
- private String getEffectiveName(HttpServletRequest req, RelyingParty relyingParty, IdPProtocolSupport support)
+ private String authenticateAs(String assertedId, X509Certificate[] chain, IdPProtocolSupport support)
throws InvalidProviderCredentialException {
-
- X509Certificate credential = getCredentialFromProvider(req);
-
- if (credential == null || credential.getSubjectX500Principal().getName(X500Principal.RFC2253).equals("")) {
- log.info("Request is from an unauthenticated service provider.");
+ // See if we have metadata for this provider
+ EntityDescriptor provider = support.lookup(assertedId);
+ if (provider == null) {
+ log.info("No metadata found for providerId: (" + assertedId + ").");
+ return null;
+ }
+ else {
+ log.info("Metadata found for providerId: (" + assertedId + ").");
+ }
+ RoleDescriptor ar_role = provider.getAttributeRequesterDescriptor(XML.SAML11_PROTOCOL_ENUM);
+ RoleDescriptor sp_role = provider.getSPSSODescriptor(XML.SAML11_PROTOCOL_ENUM);
+ if (ar_role == null && sp_role == null) {
+ log.info("SPSSO and Stand-Alone Requester roles not found in metadata for provider: ("
+ + assertedId + ").");
return null;
+ }
+ // Make sure that the supplied credential is valid for the selected provider role.
+ if ((ar_role != null && support.getTrust().validate(chain[0], chain, ar_role)) ||
+ (sp_role != null && support.getTrust().validate(chain[0], chain, sp_role))) {
+ log.info("Supplied credentials validated for this provider.");
+ return assertedId;
} else {
- log.info("Request contains credential: ("
- + credential.getSubjectX500Principal().getName(X500Principal.RFC2253) + ").");
- // Mockup old requester name for requests from < 1.2 SPs
- if (fromLegacyProvider(req)) {
- String legacyName = getHostNameFromDN(credential.getSubjectX500Principal());
- if (legacyName == null) {
- log.error("Unable to extract legacy requester name from certificate subject.");
- }
-
- log.info("Request from legacy service provider: (" + legacyName + ").");
- return legacyName;
-
- } else {
-
- // See if we have metadata for this provider
- EntityDescriptor provider = support.lookup(relyingParty.getProviderId());
- if (provider == null) {
- log.info("No metadata found for provider: (" + relyingParty.getProviderId() + ").");
- log.info("Treating remote provider as unauthenticated.");
- return null;
- }
- RoleDescriptor ar_role = provider.getAttributeRequesterDescriptor("urn:oasis:names:tc:SAML:1.1:protocol");
- RoleDescriptor sp_role = provider.getSPSSODescriptor("urn:oasis:names:tc:SAML:1.1:protocol");
- if (ar_role == null && sp_role == null) {
- log.info("SPSSO and Stand-Alone Requester roles not found in metadata for provider: (" + relyingParty.getProviderId() + ").");
- log.info("Treating remote provider as unauthenticated.");
- return null;
- }
-
- // Make sure that the suppplied credential is valid for the
- // selected relying party
- X509Certificate[] chain = (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate");
- if (support.getTrust().validate((chain != null && chain.length > 0) ? chain[0] : null, chain, ar_role) ||
- support.getTrust().validate((chain != null && chain.length > 0) ? chain[0] : null, chain, sp_role)) {
- log.info("Supplied credential validated for this provider.");
- log.info("Request from service provider: (" + relyingParty.getProviderId() + ").");
- return relyingParty.getProviderId();
-
- } else {
- log.error("Supplied credential ("
- + credential.getSubjectX500Principal().getName(X500Principal.RFC2253)
- + ") is NOT valid for provider (" + relyingParty.getProviderId() + ").");
- throw new InvalidProviderCredentialException("Invalid credential.");
- }
- }
+ log.error("Supplied credentials ("
+ + chain[0].getSubjectX500Principal().getName(X500Principal.RFC2253)
+ + ") are NOT valid for provider (" + assertedId + ").");
+ throw new InvalidProviderCredentialException("Invalid credentials.");
}
}
if (samlRequest.getQuery() == null || !(samlRequest.getQuery() instanceof SAMLAttributeQuery)) {
log.error("Protocol Handler can only respond to SAML Attribute Queries.");
- throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
+ throw new SAMLException("General error processing request.");
}
RelyingParty relyingParty = null;
-
SAMLAttributeQuery attributeQuery = (SAMLAttributeQuery) samlRequest.getQuery();
- if (!fromLegacyProvider(request)) {
- log.info("Remote provider has identified itself as: (" + attributeQuery.getResource() + ").");
- }
-
// This is the requester name that will be passed to subsystems
String effectiveName = null;
- X509Certificate credential = getCredentialFromProvider(request);
- if (credential == null || credential.getSubjectX500Principal().getName(X500Principal.RFC2253).equals("")) {
- log.info("Request is from an unauthenticated service provider.");
- } else {
-
- // Identify a Relying Party
- relyingParty = support.getServiceProviderMapper().getRelyingParty(attributeQuery.getResource());
+ // Log the physical credential supplied, if any.
+ X509Certificate[] credentials = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
+ if (credentials == null || credentials.length == 0 ||
+ credentials[0].getSubjectX500Principal().getName(X500Principal.RFC2253).equals("")) {
+ log.info("Request contained no credentials, treating as an unauthenticated service provider.");
+ }
+ else {
+ log.info("Request contains credentials: ("
+ + credentials[0].getSubjectX500Principal().getName(X500Principal.RFC2253) + ").");
+ // Try and authenticate the requester as any of the potentially relevant identifiers we know.
try {
- effectiveName = getEffectiveName(request, relyingParty, support);
+ if (attributeQuery.getResource() != null) {
+ log.info("Remote provider has identified itself as: (" + attributeQuery.getResource() + ").");
+ effectiveName = authenticateAs(attributeQuery.getResource(), credentials, support);
+ }
+
+ if (effectiveName == null) {
+ log.info("Remote provider not yet identified, attempting to derive requesting provider from credentials.");
+
+ // Try the additional candidates.
+ String[] candidateNames = getCredentialNames(credentials[0]);
+ for (int c = 0; effectiveName == null && c < candidateNames.length; c++) {
+ effectiveName = authenticateAs(candidateNames[c], credentials, support);
+ }
+ }
} catch (InvalidProviderCredentialException ipc) {
throw new SAMLException(SAMLException.REQUESTER, "Invalid credentials for request.");
}
}
-
+
if (effectiveName == null) {
+ log.info("Unable to locate metadata about provider, treating as an unauthenticated service provider.");
log.debug("Using default Relying Party for unauthenticated provider.");
relyingParty = support.getServiceProviderMapper().getRelyingParty(null);
}
+ else {
+ // Identify a Relying Party
+ log.debug("Mapping authenticated provider (" + effectiveName + ") to Relying Party.");
+ relyingParty = support.getServiceProviderMapper().getRelyingParty(effectiveName);
+ }
- // Fail if we can't honor SAML Subject Confirmation
- if (!fromLegacyProvider(request)) {
- Iterator iterator = attributeQuery.getSubject().getConfirmationMethods();
- boolean hasConfirmationMethod = false;
- while (iterator.hasNext()) {
- log.info("Request contains SAML Subject Confirmation method: (" + (String) iterator.next() + ").");
- }
- if (hasConfirmationMethod) { throw new SAMLException(SAMLException.REQUESTER,
- "This SAML authority cannot honor requests containing the supplied SAML Subject Confirmation Method."); }
+ // Fail if we can't honor SAML Subject Confirmation unless the only one supplied is
+ // bearer, in which case this is probably a Shib 1.1 query, and we'll let it slide for now.
+ // TODO: remove the compatibility with 1.1 and be strict about this?
+ boolean hasConfirmationMethod = false;
+ boolean hasOnlyBearer = true;
+ Iterator iterator = attributeQuery.getSubject().getConfirmationMethods();
+ while (iterator.hasNext()) {
+ String method = (String) iterator.next();
+ log.info("Request contains SAML Subject Confirmation method: (" + method + ").");
+ hasConfirmationMethod = true;
+ if (!method.equals(SAMLSubject.CONF_BEARER))
+ hasOnlyBearer = false;
+ }
+ if (hasConfirmationMethod && !hasOnlyBearer) {
+ throw new SAMLException(SAMLException.REQUESTER,
+ "This SAML authority cannot honor requests containing the supplied SAML Subject Confirmation Method(s).");
}
// Map Subject to local principal
principal = mapping.getPrincipal(nameId, relyingParty, relyingParty.getIdentityProvider());
log.info("Request is for principal (" + principal.getName() + ").");
- URL resource = null;
- if (fromLegacyProvider(request)) {
- try {
- resource = new URL(attributeQuery.getResource());
- } catch (MalformedURLException mue) {
- log.error("Request from legacy provider contained an improperly formatted resource "
- + "identifier. Attempting to handle request without one.");
- }
- }
-
// Get attributes from resolver
SAMLAttribute[] attrs;
Iterator requestedAttrsIterator = attributeQuery.getDesignators();
}
}
- attrs = support.getReleaseAttributes(principal, relyingParty, effectiveName, resource,
+ attrs = support.getReleaseAttributes(principal, relyingParty, effectiveName, null,
(URI[]) requestedAttrs.toArray(new URI[0]));
} else {
log.info("Request does not designate specific attributes, resolving all available.");
- attrs = support.getReleaseAttributes(principal, relyingParty, effectiveName, resource);
+ attrs = support.getReleaseAttributes(principal, relyingParty, effectiveName, null);
}
log.info("Found " + attrs.length + " attribute(s) for " + principal.getName());
if (relyingParty.getName() != null && !relyingParty.getName().equals(relyingParty.getProviderId())) {
audiences.add(relyingParty.getName());
}
- //String remoteProviderId = request.getParameter("providerId");
- if (attributeQuery.getResource() != null && !attributeQuery.getResource().equals("") && !audiences.contains(attributeQuery.getResource())) {
- audiences.add(attributeQuery.getResource());
- }
-
+
SAMLCondition condition = new SAMLAudienceRestrictionCondition(audiences);
// Put all attributes into an assertion
boolean metaDataIndicatesSignAssertions = false;
EntityDescriptor descriptor = support.lookup(relyingParty.getProviderId());
if (descriptor != null) {
- AttributeRequesterDescriptor ar = descriptor.getAttributeRequesterDescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
- if (ar != null) {
- if (ar.getWantAssertionsSigned()) {
- metaDataIndicatesSignAssertions = true;
- }
- }
- if (!metaDataIndicatesSignAssertions) {
- SPSSODescriptor sp = descriptor.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
- if (sp != null) {
- if (sp.getWantAssertionsSigned()) {
- metaDataIndicatesSignAssertions = true;
- }
- }
- }
+ AttributeRequesterDescriptor ar = descriptor
+ .getAttributeRequesterDescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
+ if (ar != null) {
+ if (ar.getWantAssertionsSigned()) {
+ metaDataIndicatesSignAssertions = true;
+ }
+ }
+ if (!metaDataIndicatesSignAssertions) {
+ SPSSODescriptor sp = descriptor.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
+ if (sp != null) {
+ if (sp.getWantAssertionsSigned()) {
+ metaDataIndicatesSignAssertions = true;
+ }
+ }
+ }
}
if (relyingParty.wantsAssertionsSigned() || metaDataIndicatesSignAssertions) {
support.signAssertions(new SAMLAssertion[]{sAssertion}, relyingParty);
log.info("Successfully created response for principal (" + principal.getName() + ").");
if (effectiveName == null) {
- if (fromLegacyProvider(request)) {
- support.getTransactionLog().info(
- "Attribute assertion issued to anonymous legacy provider at (" + request.getRemoteAddr()
- + ") on behalf of principal (" + principal.getName() + ").");
- } else {
- support.getTransactionLog().info(
- "Attribute assertion issued to anonymous provider at (" + request.getRemoteAddr()
- + ") on behalf of principal (" + principal.getName() + ").");
- }
+ support.getTransactionLog().info(
+ "Attribute assertion issued to anonymous provider at (" + request.getRemoteAddr()
+ + ") on behalf of principal (" + principal.getName() + ").");
} else {
- if (fromLegacyProvider(request)) {
- support.getTransactionLog().info(
- "Attribute assertion issued to legacy provider (" + effectiveName
- + ") on behalf of principal (" + principal.getName() + ").");
- } else {
- support.getTransactionLog().info(
- "Attribute assertion issued to provider (" + effectiveName + ") on behalf of principal ("
- + principal.getName() + ").");
- }
+ support.getTransactionLog().info(
+ "Attribute assertion issued to provider (" + effectiveName + ") on behalf of principal ("
+ + principal.getName() + ").");
}
return samlResponse;
} catch (SAMLException e) {
if (relyingParty.passThruErrors()) {
- throw new SAMLException(SAMLException.RESPONDER, "General error processing request.", e);
+ throw new SAMLException("General error processing request.", e);
} else {
- throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
+ throw new SAMLException("General error processing request.");
}
} catch (InvalidNameIdentifierException e) {
} catch (NameIdentifierMappingException e) {
log.error("Encountered an error while mapping the name identifier from the request: " + e);
if (relyingParty.passThruErrors()) {
- throw new SAMLException(SAMLException.RESPONDER, "General error processing request.", e);
+ throw new SAMLException("General error processing request.", e);
} else {
- throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
+ throw new SAMLException("General error processing request.");
}
} catch (AAException e) {
log.error("Encountered an error while resolving resolving attributes: " + e);
if (relyingParty.passThruErrors()) {
- throw new SAMLException(SAMLException.RESPONDER, "General error processing request.", e);
+ throw new SAMLException("General error processing request.", e);
} else {
- throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
+ throw new SAMLException("General error processing request.");
}
} catch (CloneNotSupportedException e) {
log.error("Encountered an error while cloning request subject for use in response: " + e);
if (relyingParty.passThruErrors()) {
- throw new SAMLException(SAMLException.RESPONDER, "General error processing request.", e);
+ throw new SAMLException("General error processing request.", e);
} else {
- throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
+ throw new SAMLException("General error processing request.");
}
}
}
-
- private static boolean fromLegacyProvider(HttpServletRequest request) {
-
- String version = request.getHeader("Shibboleth");
- if (version != null) {
- log.debug("Request from Shibboleth version: " + version);
- return false;
- }
- log.debug("No version header found.");
- return true;
- }
-
}
\ No newline at end of file