/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 net.jini.jeri.ssl; import java.security.Principal; import java.util.Iterator; import java.util.Set; import javax.security.auth.x500.X500Principal; import net.jini.core.constraint.ClientAuthentication; import net.jini.core.constraint.ClientMaxPrincipal; import net.jini.core.constraint.ClientMaxPrincipalType; import net.jini.core.constraint.ClientMinPrincipal; import net.jini.core.constraint.ClientMinPrincipalType; import net.jini.core.constraint.Confidentiality; import net.jini.core.constraint.ConnectionAbsoluteTime; import net.jini.core.constraint.ConnectionRelativeTime; import net.jini.core.constraint.ConstraintAlternatives; import net.jini.core.constraint.Delegation; import net.jini.core.constraint.DelegationAbsoluteTime; import net.jini.core.constraint.DelegationRelativeTime; import net.jini.core.constraint.Integrity; import net.jini.core.constraint.InvocationConstraint; import net.jini.core.constraint.InvocationConstraints; import net.jini.core.constraint.ServerAuthentication; import net.jini.core.constraint.ServerMinPrincipal; /** * Records information about a connection used for a remote call and determines * whether the connection could support specific constraints. <p> * * Does not support heterogeneous constraint alternatives. As a result, * callers can be assured that the choice of possible principals or of * Integrity.YES is independent of the suites, insuring that those items can be * picked before negotiating the cipher suite. * * @author Sun Microsystems, Inc. */ final class ConnectionContext extends Utilities { /** Constraints supported, without integrity or connection timeout */ private static final long OK = Long.MAX_VALUE; /** Constraints supported with codebase integrity */ private static final long INTEGRITY = -3; /** Constraints not supported */ private static final long NOT_SUPPORTED = -4; /** The ClientMinPrincipalType supported by the provider. */ private static final ClientMinPrincipalType clientMinPrincipalType = new ClientMinPrincipalType(X500Principal.class); /** The cipher suite */ final String cipherSuite; /** The client principal, or null for an anonymous client */ final Principal client; /** The server principal, or null for an anonymous server */ final Principal server; /** Whether codebase integrity should be enforced */ private final boolean integrity; /** * Whether the connection is being considered on the client side, which * does not support relative time constraints. */ private final boolean clientSide; /** Set to true if the principals and cipher suite conflict */ private boolean notSupported; /** Whether the requirements specify Integrity.YES */ private boolean integrityRequired; /** Whether the preferences specify Integrity.YES */ private boolean integrityPreferred; /** The absolute connection time, or Long.MAX_VALUE if not specified */ private long connectionTime = Long.MAX_VALUE; /** The number of preferences satisfied */ private int preferences; /** * Creates an instance that represents using the specified cipher suite, * client and server principals, whether to guarantee codebase integrity, * and constraints. Null values for the principals mean they are * anonymous. Non-X.500 principals are permitted to allow specifying a * dummy principal if the principal is unknown. Returns null if the * constraints are not supported. */ static ConnectionContext getInstance(String cipherSuite, Principal client, Principal server, boolean integrity, boolean clientSide, InvocationConstraints constraints) { ConnectionContext context = new ConnectionContext( cipherSuite, client, server, integrity, clientSide); return context.supported(constraints) ? context : null; } /** Creates an instance of this class. */ private ConnectionContext(String cipherSuite, Principal client, Principal server, boolean integrity, boolean clientSide) { this.cipherSuite = cipherSuite; this.client = client; this.server = server; this.integrity = integrity; this.clientSide = clientSide; boolean serverAuth = doesServerAuthentication(cipherSuite); if (serverAuth != (server != null) || (client != null && server == null)) { notSupported = true; } } public String toString() { StringBuffer sb = new StringBuffer("ConnectionContext["); fieldsToString(sb); sb.append("]"); return sb.toString(); } void fieldsToString(StringBuffer sb) { sb.append(cipherSuite); if (client != null) { sb.append(", client: ").append(client); } if (server != null) { sb.append(", server: ").append(server); } if (integrityRequired) { sb.append(", integrity: required"); } else if (integrityPreferred) { sb.append(", integrity: preferred"); } if (connectionTime != Long.MAX_VALUE) { sb.append(", connectionTime = ").append(connectionTime); } sb.append(", preferences: ").append(preferences); } /** Returns whether integrity is required. */ boolean getIntegrityRequired() { return integrityRequired; } /** Returns whether integrity is preferred. */ boolean getIntegrityPreferred() { return integrityPreferred; } /** * Returns the absolute time when the connection should be completed, or * Long.MAX_VALUE for no limit. */ long getConnectionTime() { return connectionTime; } /** Returns the number of preferences that can be satisfied. */ int getPreferences() { return preferences; } /** * Checks if the specified constraints are supported, computing * integrityRequired, integrityPreferred, connectionTime and preferences as * a side effect. */ private boolean supported(InvocationConstraints constraints) { if (notSupported) { return false; } for (Iterator i = constraints.requirements().iterator(); i.hasNext(); ) { long r = supported((InvocationConstraint) i.next()); if (r == NOT_SUPPORTED) { return false; } if (r == INTEGRITY) { integrityRequired = true; } else if (connectionTime > r) { connectionTime = r; } } for (Iterator i = constraints.preferences().iterator(); i.hasNext(); ) { long r = supported((InvocationConstraint) i.next()); if (r == NOT_SUPPORTED) { continue; } preferences++; if (r == INTEGRITY) { if (!integrityRequired) { integrityPreferred = true; } } else if (connectionTime > r) { connectionTime = r; } } if (integrity && !integrityRequired && !integrityPreferred) { return false; } else { return true; } } /** * Checks if the constraint is supported, returning NOT_SUPPORTED if it is * not supported, INTEGRITY if the constraint is Integrity.YES or * constraint alternatives with elements of type Integrity, the connection * time if the constraint is an instance of ConnectionAbsoluteTime or * constraint alternatives of them, and otherwise OK. */ private long supported(InvocationConstraint constraint) { if (constraint instanceof ConstraintAlternatives) { return supported((ConstraintAlternatives) constraint); } else if (constraint instanceof Integrity) { return integrity && constraint == Integrity.YES ? INTEGRITY : NOT_SUPPORTED; } else if (constraint instanceof Confidentiality) { return ok(doesEncryption(cipherSuite) == (constraint == Confidentiality.YES)); } else if (constraint instanceof ConfidentialityStrength) { return ok(!doesEncryption(cipherSuite) || (hasStrongCipherAlgorithm(cipherSuite) == (constraint == ConfidentialityStrength.STRONG))); } else if (constraint instanceof ClientAuthentication) { return ok((client == null) == (constraint == ClientAuthentication.NO)); } else if (constraint instanceof ClientMinPrincipalType) { return ok(client == null || constraint.equals(clientMinPrincipalType)); } else if (constraint instanceof ClientMaxPrincipalType) { return ok(client == null || ((ClientMaxPrincipalType) constraint).elements().contains( X500Principal.class)); } else if (constraint instanceof ClientMinPrincipal) { if (client == null) { return OK; } Set elements = ((ClientMinPrincipal) constraint).elements(); return ok(elements.size() == 1 && elements.contains(client)); } else if (constraint instanceof ClientMaxPrincipal) { return ok(client == null || ((ClientMaxPrincipal) constraint).elements().contains( client)); } else if (constraint instanceof Delegation) { return ok(client == null || (constraint == Delegation.NO)); } else if (constraint instanceof DelegationAbsoluteTime) { return OK; } else if (constraint instanceof DelegationRelativeTime) { return ok(!clientSide); } else if (constraint instanceof ServerAuthentication) { return ok((server == null) == (constraint == ServerAuthentication.NO)); } else if (constraint instanceof ServerMinPrincipal) { if (server == null) { return OK; } Set elements = ((ServerMinPrincipal) constraint).elements(); return ok(elements.size() == 1 && elements.contains(server)); } else if (constraint instanceof ConnectionAbsoluteTime) { return Math.max(((ConnectionAbsoluteTime) constraint).getTime(), 0); } else if (constraint instanceof ConnectionRelativeTime) { return ok(!clientSide); } else { return NOT_SUPPORTED; } } /** Returns OK if the argument is true, else NOT_SUPPORTED. */ private static long ok(boolean ok) { return ok ? OK : NOT_SUPPORTED; } /** * Checks if the constraint alternatives are supported, returning * NOT_SUPPORTED if the elements have different types or none are * supported, INTEGRITY if the elements are instances of Integrity, the * largest connection time if the elements are instances of * ConnectionAbsoluteTime, and otherwise OK. */ private long supported(ConstraintAlternatives constraint) { Set alts = constraint.elements(); long connectionTime = -1; Class type = null; boolean supported = false; boolean integrity = false; for (Iterator i = alts.iterator(); i.hasNext(); ) { InvocationConstraint alt = (InvocationConstraint) i.next(); if (type == null) { type = alt.getClass(); } else if (type != alt.getClass()) { return NOT_SUPPORTED; } long r = supported(alt); if (r != NOT_SUPPORTED) { supported = true; if (r == INTEGRITY) { integrity = true; } else if (r > connectionTime) { connectionTime = r; } } } if (!supported) { return NOT_SUPPORTED; } else if (integrity) { return INTEGRITY; } else if (connectionTime >= 0) { return connectionTime; } else { return OK; } } }