/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.ericsson.ssa.sip;
import org.jvnet.glassfish.comms.deployment.backend.ResourceCollection;
import org.jvnet.glassfish.comms.deployment.backend.SecurityConstraint;
import org.jvnet.glassfish.comms.deployment.backend.SipApplication;
import com.ericsson.ssa.sip.dns.TargetTuple;
import com.sun.enterprise.config.serverbeans.SipService;
import org.jvnet.glassfish.comms.security.auth.impl.DigestAuthenticator;
import org.jvnet.glassfish.comms.security.auth.impl.IdentityAuthenticator;
import org.jvnet.glassfish.comms.security.auth.impl.PAssertedAuthenticator;
import org.jvnet.glassfish.comms.security.authorize.SipResourcePermission;
import org.jvnet.glassfish.comms.security.authorize.SipRoleRefPermission;
import org.jvnet.glassfish.comms.security.authorize.SipUserDataPermission;
import org.jvnet.glassfish.comms.security.util.PolicyBuilder;
import java.net.URL;
import java.security.CodeSource;
import java.security.Policy;
import java.security.Principal;
import java.security.ProtectionDomain;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.security.auth.Subject;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipServletResponse;
import org.jvnet.glassfish.comms.security.auth.impl.PAssertedAuthenticatorFactory;
import org.jvnet.glassfish.comms.security.auth.impl.RunAsAuthenticator;
import org.jvnet.glassfish.comms.security.auth.impl.HeaderParser;
import com.sun.enterprise.config.serverbeans.SipListener;
import com.sun.enterprise.deployment.runtime.web.WebProperty;
import com.sun.enterprise.config.serverbeans.ServerBeansFactory;
import com.sun.enterprise.server.ApplicationServer;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.util.ListIterator;
import javax.servlet.sip.ServletParseException;
import org.jvnet.glassfish.comms.deployment.backend.LoginConfigurationImpl;
import com.sun.logging.LogDomains;
import com.sun.enterprise.config.ConfigException;
import com.sun.enterprise.config.serverbeans.Config;
import com.sun.enterprise.config.serverbeans.ElementProperty;
import com.sun.enterprise.config.serverbeans.SecurityService;
import com.sun.enterprise.security.auth.PrincipalMapper;
import java.util.Properties;
import org.jvnet.glassfish.comms.security.auth.impl.SipSecurityManager;
/**
*
* @author K.Venugopal@sun.com
*/
public final class AuthModule {
private SipApplication sipApplicationModel;
private HashMap<Key, ServletConstraints> servletConstraints = null;
private PolicyBuilder policyUtil = new PolicyBuilder();
private String contextId = null;
private CodeSource codeSource = null;
private Policy policy = null;
private String sarName = "";
private Map protectionDomainCache =
Collections.synchronizedMap(new WeakHashMap());
private HashMap<String, String> runAsPrincipals = null;
private String trustConfigId = "";
private String authMethod = null;
private String realmName = null;
private String assertionType = ""; //P-Asserted or Identity
//false SUPPORTED , true REQUIRED
private boolean idAssertionSupport = false;
private boolean paAuth = false;
private boolean identityAuth = false;
private String identityAuthRealm = null;
private String principalMapper = null;
private Properties identityConfig = null;
private Properties principalProps = null;
private PrincipalMapper principalMapperInstance = null;
private static final Logger logger =
LogDomains.getLogger(LogDomains.SECURITY_LOGGER);
public AuthModule(SipApplication am, String sarName, String contextId) {
this.contextId = contextId;
this.runAsPrincipals = new HashMap<String, String>(5);
this.sipApplicationModel = am;
policy = Policy.getPolicy();
policy.refresh();
this.sarName = sarName;
String code = removeSpaces(contextId);
servletConstraints = new HashMap<Key, ServletConstraints>(5);
java.net.URI uri = null;
try {
uri = new java.net.URI("file:///" + code);
if (uri != null) {
codeSource = new CodeSource(new URL(uri.toString()),
(java.security.cert.Certificate[]) null);
}
} catch (java.net.URISyntaxException use) {
//log
throw new RuntimeException(use);
} catch (java.net.MalformedURLException mue) {
//log
throw new RuntimeException(mue);
}
LoginConfigurationImpl loginConfig = (LoginConfigurationImpl) sipApplicationModel.getLoginConfiguration();
if (loginConfig != null) {
authMethod = loginConfig.getAuthenticationMethod();
if (!"DIGEST".equalsIgnoreCase(authMethod)) {
throw new RuntimeException(authMethod + " is not supported");
}
realmName = loginConfig.getRealmName();
assertionType = loginConfig.getIdentityAssertionScheme();
String supportType = loginConfig.getIdentityAssertionSupport();
if ("REQUIRED".equals(supportType)) {
idAssertionSupport = true;
}
WebProperty[] wblist = sipApplicationModel.getSunSipDescriptor().getWebProperty();
for (int i = 0; i < wblist.length; i++) {
WebProperty prop = wblist[i];
if ("trust-id-ref".equals(prop.getValue("name"))) {
trustConfigId = (String) prop.getValue("value");
} else if ("trust-auth-realm-ref".equals(prop.getValue("name"))) {
identityAuthRealm = (String) prop.getValue("value");
}
}
}
if ("P-Asserted-Identity".equals(assertionType)) {
paAuth = true;
} else if ("Identity".equals(assertionType)) {
identityAuth = true;
}
Enumeration<SecurityConstraint> scCollection = sipApplicationModel.getSecurityConstraints();
while (scCollection.hasMoreElements()) {
SecurityConstraint constraint = scCollection.nextElement();
@SuppressWarnings(value = "unchecked")
Collection<ResourceCollection> resourceCollection = constraint.getResourceCollections();
Iterator<ResourceCollection> rcItr = resourceCollection.iterator();
while (rcItr.hasNext()) {
ResourceCollection rc = rcItr.next();
@SuppressWarnings(value = "unchecked")
Collection<String> sn = rc.getServletNames();
@SuppressWarnings(value = "unchecked")
Collection<String> sm = rc.getSipMethods();
Collection<String> roles = constraint.getAuthorizationConstraintRoleNames();
Iterator<String> snItr = sn.iterator();
if(roles == null)continue;
while (snItr.hasNext()) {
String servletName = snItr.next();
Iterator<String> smItr = sm.iterator();
while (smItr.hasNext()) {
String sipMethod = smItr.next();
Key key = new Key(servletName, sipMethod);
Set<String> roleSet = null;
roleSet = new HashSet<String>(8);
if (servletConstraints.containsKey(key)) {
ServletConstraints sc = servletConstraints.get(key);
roleSet = sc.roles;
} else {
boolean proxy = constraint.isProxyAuthenticated();
ServletConstraints sc = new ServletConstraints(proxy,
roleSet);
servletConstraints.put(key, sc);
}
roleSet.addAll(roles);
}
}
}
}
initConfig();
}
private void initConfig() {
try {
Config cfg = (Config) ServerBeansFactory.getConfigBean(ApplicationServer.getServerContext().getConfigContext());
SecurityService secService = cfg.getSecurityService();
ElementProperty[] props = secService.getElementProperty();
String identityConfigValue = null;
String principalMapperConfig = null;
for (ElementProperty value : props) {
if ("IdentityValidatorConfiguration".equalsIgnoreCase(value.getName())) {
identityConfigValue = value.getValue();
continue;
}
if ("PrincipalMapper".equalsIgnoreCase(value.getName())) {
principalMapperConfig = value.getValue();
}
}
HeaderParser parser = new HeaderParser();
if (identityConfigValue != null && identityConfigValue.length() > 0) {
identityConfig = parser.parse(identityConfigValue);
}
if (principalMapperConfig != null && principalMapperConfig.length() > 0) {
principalProps = parser.parse(principalMapperConfig);
principalMapper = (String) principalProps.remove("classname");
if (principalMapper != null) {
Class pmClass;
try {
pmClass = Class.forName(principalMapper);
principalMapperInstance = (PrincipalMapper) pmClass.newInstance();
principalMapperInstance.initialize(principalProps);
} catch (SecurityException ex) {
logger.log(Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
logger.log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
logger.log(Level.SEVERE, null, ex);
} catch (ClassNotFoundException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
}
} catch (ConfigException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
public static String getTLSPort() throws com.sun.enterprise.config.ConfigException {
SipService sipService = ServerBeansFactory.getConfigBean(ApplicationServer.getServerContext().getConfigContext()).getSipService();
SipListener[] sipListeners = (SipListener[]) sipService.getSipListener();
String tlsPort = "";
for (SipListener sipListener : sipListeners) {
if (!sipListener.isEnabled()) {
continue;
}
if (sipListener.getSsl() != null && sipListener.getSsl().isTlsEnabled()) {
int port = Integer.parseInt(sipListener.getPort());
tlsPort = Integer.toString(port);
break;
}
}
return tlsPort;
}
public final void authenticate(SipServletRequest request, String servletName) throws IOException {
if (sipApplicationModel != null) {
boolean proxy = isProxy(request.getMethod(), servletName);
boolean digestHeader = false;
if (hasAuthorizationHeader(request, proxy)) {
digestHeader = true;
}
if (paAuth) {
if (hasPAssertedHeader(request)) {
handlePAssertedAuthentication(request);
} else if (!idAssertionSupport && request.getUserPrincipal() == null) {
handleDigestAuthentication(request, servletName, proxy);
} else {
sendErrorResponse((SipServletRequestImpl) request, 403, null);
}
return;
}
if (identityAuth) {
if (hasIdentityHeader(request)) {
handleIdentityAuthentication(request);
} else if (!idAssertionSupport && request.getUserPrincipal() == null) {
handleDigestAuthentication(request, servletName, proxy);
} else {
//return 428
sendErrorResponse((SipServletRequestImpl) request, 428, "Use Identity Header");
}
return;
}
handleDigestAuthentication(request, servletName, proxy);
}
}
private void handleDigestAuthentication(SipServletRequest request, String servletToInvoke, boolean proxy) throws IOException {
if (!((authMethod == null) || (authMethod.length() == 0))) {
if ("DIGEST".equals(authMethod)) {
DigestAuthenticator auth = new DigestAuthenticator(
realmName, proxy);
Principal authPrincipal = auth.authenticate(request);
if (authPrincipal != null) {
((SipServletRequestImpl) request).setUserPrincipal(authPrincipal);
((SipServletRequestImpl) request).setUser(authPrincipal.getName());
((SipServletRequestImpl) request).setAuthInfoHeader(auth.getAuthInfoHeader());
removeProcessedAuthHeader(request, proxy);
} else {
if (!(request.getMethod().equals("ACK") || request.getMethod().equals("CANCEL"))) {
SipServletResponse response = createErrorResponse(request, servletToInvoke);
response.send();
return;
}
}
}
}
}
private void handlePAssertedAuthentication(SipServletRequest request) throws IOException {
PAssertedAuthenticatorFactory paf = PAssertedAuthenticatorFactory.getInstance();
PAssertedAuthenticator auth = null;
if (trustConfigId == null || trustConfigId.length() == 0) {
auth = (PAssertedAuthenticator) paf.getAuthenticator();
} else {
auth = (PAssertedAuthenticator) paf.getAuthenticator(trustConfigId);
}
if (auth == null) {
throw new SecurityException("P-Asserted Authenticator not configured");
}
TargetTuple remoteHost = ((SipServletRequestImpl) request).getInitialRemote();
String host = "";
if (remoteHost != null) {
host = remoteHost.getIP();
}
Principal authPrincipal = auth.authenticate(request, host,
identityAuthRealm, auth.INTERMEDIATE);
if (authPrincipal != null) {
((SipServletRequestImpl) request).setUserPrincipal(authPrincipal);
((SipServletRequestImpl) request).setUser(authPrincipal.getName());
} else {
sendErrorResponse((SipServletRequestImpl) request, 403, null);
}
return;
}
public void handleIdentityAuthentication(SipServletRequest request) throws IOException {
try {
Principal authPrincipal = new IdentityAuthenticator(identityConfig, principalMapperInstance).authenticate(request, identityAuthRealm);
if (authPrincipal != null) {
((SipServletRequestImpl) request).setUserPrincipal(authPrincipal);
((SipServletRequestImpl) request).setUser(authPrincipal.getName());
}
return;
} catch (SignatureException ex) {
logger.log(Level.FINE, "", ex);
sendErrorResponse((SipServletRequestImpl) request, 438, "Invalid Identity Header");
} catch (SecurityException ex) {
logger.log(Level.FINE, "", ex);
sendErrorResponse((SipServletRequestImpl) request, 436, "Bad Identity-Info");
} catch (CertificateException ce) {
//437
logger.log(Level.FINE, "", ce);
sendErrorResponse((SipServletRequestImpl) request, 437, "Unsupported Certificate");
} catch (NoSuchAlgorithmException ne) {
logger.log(Level.FINE, "", ne);
sendErrorResponse((SipServletRequestImpl) request, 436, "Bad Identity-Info");
} catch (ServletParseException spe) {
//436
logger.log(Level.FINE, "", spe);
sendErrorResponse((SipServletRequestImpl) request, 436, "Bad Identity-Info");
} catch (InvalidKeyException ike) {
logger.log(Level.FINE, "", ike);
sendErrorResponse((SipServletRequestImpl) request, 436, "Bad Identity-Info");
}
}
private void removeProcessedAuthHeader(SipServletRequest request, boolean isProxy) {
//TODO: remove the header now, handle this when supporting multiple auth header values
if (!isProxy) {
request.removeHeader("Authorization");
return;
}
String pheader = request.getHeader("Proxy-Authorization");
request.removeHeader("Proxy-Authorization");
}
public SipServletResponse createErrorResponse(
SipServletRequest request, String servletName) {
SipServletResponse resp = null;
int statusCode = 401;
boolean proxyAuthenticate = false;
Key key = new Key(servletName, request.getMethod());
if (servletConstraints.containsKey(key)) {
ServletConstraints sc = servletConstraints.get(key);
proxyAuthenticate =
sc.proxy;
}
if (proxyAuthenticate) {
statusCode = 407;
}
resp = request.createResponse(statusCode);
DigestAuthenticator auth =
new DigestAuthenticator(realmName, proxyAuthenticate);
if (hasAuthorizationHeader(request, proxyAuthenticate)) {
auth.setAuthenticateHeader(resp, request, proxyAuthenticate, true);
} else {
auth.setAuthenticateHeader(resp, request, proxyAuthenticate, false);
}
return resp;
}
private void sendErrorResponse(SipServletRequestImpl request, int statusCode, String msg) throws IOException {
SipServletResponse resp = request.createResponse(statusCode);
if (msg != null) {
resp.setStatus(statusCode, msg);
resp.addHeader("Error-Info", msg);
}
resp.send();
}
public boolean hasResourcePermissions(SipServletRequest request,
String servletName) {
return hasResourcePermissions(request.getMethod(), servletName);
}
public boolean hasResourcePermissions(String sipMethod, String servletName) {
try {
SipSecurityManager.setPolicyContext(contextId);
Subject subject = SipSecurityManager.getCurrentSubject();
Set<Principal> principalSet = subject.getPrincipals();
ProtectionDomain pd =
(ProtectionDomain) protectionDomainCache.get(principalSet);
if (pd == null) {
Principal[] principals = principals = ((principalSet == null) ? null
: (Principal[]) principalSet.toArray(new Principal[0]));
pd =
new ProtectionDomain(codeSource, null, null, principals);
protectionDomainCache.put(principalSet, pd);
}
SipResourcePermission srp = new SipResourcePermission(servletName,
sipMethod);
return policy.implies(pd, srp);
} catch (Throwable ex) {
Logger.getLogger(AuthModule.class.getName()).log(Level.SEVERE, null, ex);
throw new RuntimeException(ex);
}
}
public void setRunAs(Map<String, String> values) {
this.runAsPrincipals = (HashMap<String, String>) values;
}
public Object preSetRunAsIdentity(
String servlet) {
Object currentSC = null;
if (runAsPrincipals.containsKey(servlet)) {
String user = runAsPrincipals.get(servlet);
RunAsAuthenticator ras = new RunAsAuthenticator(user, "");
ras.authenticate(null);
currentSC =
SipSecurityManager.getSecurityContext();
}
return currentSC;
}
public void postSetRunAsIdentity(Object oldSC) {
SipSecurityManager.setSecurityContext(oldSC);
}
public boolean checkServletConstraints(SipServletRequest request,
String servletName) {
if (hasRoleRefPermission(request, servletName) &&
hasResourcePermissions(request, servletName)) {
return true;
}
return false;
}
/**
* All Security constraints must be satisfied.
*/
public boolean hasRoleRefPermission(SipServletRequest request,
String servletName) {
return hasRoleRefPermission(request.getMethod(), servletName);
}
public boolean hasRoleRefPermission(String sipMethod, String servletName) {
try {
SipSecurityManager.setPolicyContext(contextId);
} catch (Throwable ex) {
throw new RuntimeException(ex);
}
Subject subject = SipSecurityManager.getCurrentSubject();
Set<Principal> principalSet = subject.getPrincipals();
Key key = new Key(servletName, sipMethod);
ServletConstraints sc = servletConstraints.get(key);
if (sc == null) {
return false;
}
Set<String> roles = sc.roles;
Iterator<String> roleItr = roles.iterator();
boolean isInRole = false;
ProtectionDomain pd =
(ProtectionDomain) protectionDomainCache.get(principalSet);
if (pd == null) {
Principal[] principals = principals = ((principalSet == null) ? null
: (Principal[]) principalSet.toArray(new Principal[0]));
pd =
new ProtectionDomain(codeSource, null, null, principals);
protectionDomainCache.put(principalSet, pd);
}
while (roleItr.hasNext()) {
String role = roleItr.next();
SipRoleRefPermission srp = new SipRoleRefPermission(servletName,
role);
isInRole =
policy.implies(pd, srp);
if (isInRole) {
break;
}
}
return isInRole;
}
public boolean isUserInRole(String servletName, String role, Principal principal) {
Object context = SipSecurityManager.getSecurityContext();
SipSecurityManager.setSecurityContext(principal);
boolean result = hasRole(servletName, role, principal);
SipSecurityManager.setSecurityContext(context);
return result;
}
public boolean hasRole(String servletName, String role, Principal principal) {
try {
SipSecurityManager.setPolicyContext(contextId);
} catch (Throwable ex) {
throw new RuntimeException(ex);
}
Subject sub = SipSecurityManager.getSubject(principal);
Set<Principal> principalSet = sub.getPrincipals();
ProtectionDomain pd =
(ProtectionDomain) protectionDomainCache.get(principalSet);
if (pd == null) {
Principal[] principals = principals = ((principalSet == null) ? null
: (Principal[]) principalSet.toArray(new Principal[0]));
pd =
new ProtectionDomain(codeSource, null, null, principals);
protectionDomainCache.put(principalSet, pd);
}
SipRoleRefPermission srp = new SipRoleRefPermission(servletName,
role);
return policy.implies(pd, srp);
}
public boolean hasUserDataPermissions(String servletName, String sipMethod,
boolean isSecured) {
try {
SipSecurityManager.setPolicyContext(contextId);
} catch (Throwable ex) {
throw new RuntimeException(ex);
}
Subject subject = SipSecurityManager.getCurrentSubject();
Set<Principal> principalSet = subject.getPrincipals();
boolean isInRole = false;
Principal[] principals = ((principalSet == null) ? null
: (Principal[]) principalSet.toArray(new Principal[0]));
ProtectionDomain pd =
(ProtectionDomain) protectionDomainCache.get(principalSet);
if (pd == null) {
pd = new ProtectionDomain(codeSource, null, null, principals);
protectionDomainCache.put(principalSet, pd);
}
String transport = "NONE";
if (isSecured) {
transport = "CONFIDENTIAL";
}
Set<String> sm = new HashSet<String>(8);
sm.add(sipMethod);
SipUserDataPermission wdp = new SipUserDataPermission(servletName, sm,
transport);
return policy.implies(pd, wdp);
}
private String removeSpaces(String withSpaces) {
return withSpaces.replace(' ', '_');
}
private boolean isProxy(String method, String servletName) {
Key key = new Key(servletName, method);
if (servletConstraints.containsKey(key)) {
ServletConstraints sc = servletConstraints.get(key);
return sc.proxy;
}
return false;
}
private boolean hasAuthorizationHeader(SipServletRequest request, boolean proxy) {
if (!proxy) {
String header = request.getHeader("Authorization");
if (header != null && header.length() > 0) {
return true;
}
return false;
}
String pheader = request.getHeader("Proxy-Authorization");
if (pheader != null && pheader.length() > 0) {
return true;
}
return false;
}
private boolean hasPAssertedHeader(SipServletRequest request) {
ListIterator passertedHeader = request.getHeaders(PAssertedAuthenticator.P_ASSERTED_IDENTITY);
if (passertedHeader.hasNext()) {
return true;
}
return false;
}
private boolean hasIdentityHeader(SipServletRequest request) {
String hv = request.getHeader("Identity");
if (hv != null && hv.length() > 0) {
return true;
}
return false;
}
public boolean needsAuthentication(String servletName, String sipMethod) {
Key key = new Key(servletName, sipMethod);
if (servletConstraints.containsKey(key)) {
return true;
}
return false;
}
class Key {
String servletName = null;
String methodName = null;
int hashCode = -1;
public Key() {
}
public Key(String servletName, String methodName) {
this.servletName = servletName;
this.methodName = methodName;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Key)) {
return false;
}
Key key = (Key) obj;
if (((servletName != null) && servletName.equals(key.servletName)) &&
((methodName != null) && methodName.equals(key.methodName))) {
return true;
}
return false;
}
@Override
public int hashCode() {
if (hashCode == -1) {
StringBuilder builder = new StringBuilder();
builder.append(servletName);
builder.append(methodName);
hashCode = builder.toString().hashCode();
}
return hashCode;
}
public void reset() {
servletName = null;
methodName = null;
}
}
class ServletConstraints {
boolean proxy = false;
Set<String> roles = null;
public ServletConstraints(boolean proxy, Set<String> roles) {
this.proxy = proxy;
this.roles = roles;
}
}
}