package org.jacorb.security.sas; /* * JacORB - a free Java ORB * * Copyright (C) 2000-2014 Gerald Brose / The JacORB Team. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ import java.net.URLDecoder; import java.util.Hashtable; import org.jacorb.config.Configurable; import org.jacorb.config.Configuration; import org.jacorb.config.ConfigurationException; import org.jacorb.orb.CDRInputStream; import org.jacorb.orb.MinorCodes; import org.jacorb.orb.giop.ClientConnection; import org.jacorb.orb.portableInterceptor.ClientRequestInfoImpl; import org.omg.ATLAS.ATLASProfile; import org.omg.ATLAS.ATLASProfileHelper; import org.omg.ATLAS.AuthTokenData; import org.omg.ATLAS.AuthTokenDispenser; import org.omg.ATLAS.AuthTokenDispenserHelper; import org.omg.ATLAS.SCS_ATLAS; import org.omg.CORBA.Any; import org.omg.CORBA.BAD_PARAM; import org.omg.CORBA.CompletionStatus; import org.omg.CSI.AuthorizationElement; import org.omg.CSI.CompleteEstablishContext; import org.omg.CSI.ContextError; import org.omg.CSI.EstablishContext; import org.omg.CSI.IdentityToken; import org.omg.CSI.MTCompleteEstablishContext; import org.omg.CSI.MTContextError; import org.omg.CSI.MessageInContext; import org.omg.CSI.SASContextBody; import org.omg.CSI.SASContextBodyHelper; import org.omg.CSIIOP.CompoundSecMechList; import org.omg.CSIIOP.CompoundSecMechListHelper; import org.omg.CSIIOP.ServiceConfiguration; import org.omg.IOP.Codec; import org.omg.IOP.ENCODING_CDR_ENCAPS; import org.omg.IOP.Encoding; import org.omg.IOP.ServiceContext; import org.omg.IOP.TAG_CSI_SEC_MECH_LIST; import org.omg.IOP.TaggedComponent; import org.omg.IOP.CodecFactoryPackage.UnknownEncoding; import org.omg.PortableInterceptor.ClientRequestInterceptor; import org.omg.PortableInterceptor.ORBInitInfo; import org.slf4j.Logger; /** * This is the SAS Client Security Service (CSS) Interceptor * * @author David Robison */ public class SASClientInterceptor extends org.omg.CORBA.LocalObject implements ClientRequestInterceptor, Configurable { protected static final int SecurityAttributeService = 15; protected final String DEFAULT_NAME = "SASClientInterceptor"; protected Codec codec = null; protected String name = null; private Logger logger = null; protected byte[] contextToken = new byte[0]; protected boolean useStateful = true; protected Hashtable<String, AuthTokenData> atlasCache = new Hashtable<String, AuthTokenData>(); protected ISASContext sasContext = null; public SASClientInterceptor(ORBInitInfo info) throws UnknownEncoding, ConfigurationException { name = DEFAULT_NAME; Encoding encoding = new Encoding(ENCODING_CDR_ENCAPS.value, (byte) 1, (byte) 0); codec = info.codec_factory().create_codec(encoding); org.jacorb.orb.ORB orb = ((org.jacorb.orb.portableInterceptor.ORBInitInfoImpl)info).getORB (); configure( orb.getConfiguration()); } public void configure(Configuration configuration) throws ConfigurationException { logger = configuration.getLogger("org.jacorb.security.sas.CSS.log.verbosity"); useStateful = configuration.getAttribute("jacorb.security.sas.stateful","true").equals("true"); String contextClass = null; try { contextClass = configuration.getAttribute("jacorb.security.sas.contextClass"); Class<?> c = org.jacorb.util.ObjectUtil.classForName(contextClass); sasContext = (ISASContext)c.newInstance(); } catch (IllegalArgumentException e) { logger.error ("Caught ", e); throw new ConfigurationException ("Could not load SAS context class " + contextClass); } catch (ClassNotFoundException e) { logger.error ("Caught ", e); throw new ConfigurationException ("Could not load SAS context class " + contextClass); } catch (InstantiationException e) { logger.error ("Caught ", e); throw new ConfigurationException ("Could not load SAS context class " + contextClass); } catch (IllegalAccessException e) { logger.error ("Caught ", e); throw new ConfigurationException ("Could not load SAS context class " + contextClass); } sasContext.configure(configuration); sasContext.initClient(); } public void setContextToken(byte[] contextToken) { this.contextToken = contextToken; } public String name() { return name; } public void destroy() { } public void send_request(org.omg.PortableInterceptor.ClientRequestInfo cri) throws org.omg.PortableInterceptor.ForwardRequest { ClientRequestInfoImpl ri = (ClientRequestInfoImpl)cri; if (ri.isLocalInterceptor()) { return; } //if (ri.operation().equals("_is_a")) return; //if (ri.operation().equals("_non_existent")) return; org.omg.CORBA.ORB orb = ri.orb(); // see if target requires protected requests by looking into the IOR CompoundSecMechList csmList = null; try { TaggedComponent tc = ri.get_effective_component(TAG_CSI_SEC_MECH_LIST.value); CDRInputStream is = new CDRInputStream(tc.component_data); is.openEncapsulatedArray(); csmList = CompoundSecMechListHelper.read( is ); } catch (BAD_PARAM e) { if (logger.isDebugEnabled()) logger.debug("Did not find tagged component TAG_CSI_SEC_MECH_LIST: "+ ri.operation()); } catch (Exception e) { if (logger.isWarnEnabled()) logger.warn("Did not find tagged component TAG_CSI_SEC_MECH_LIST: "+e); } if(csmList != null && csmList.mechanism_list[0].as_context_mech.target_supports == 0 && csmList.mechanism_list[0].as_context_mech.target_requires == 0 && csmList.mechanism_list[0].sas_context_mech.target_supports == 0 && csmList.mechanism_list[0].sas_context_mech.target_requires == 0) { return; } // ask connection for client_context_id ClientConnection connection = ri.getConnection(); long client_context_id = 0; if (useStateful) client_context_id = connection.cacheSASContext("css".getBytes()); if (client_context_id < 0) { if (logger.isInfoEnabled()) logger.info("New SAS Context: " + (-client_context_id)); } // get ATLAS tokens AuthorizationElement[] authorizationList = csmList != null ? getATLASTokens(orb, csmList) : new AuthorizationElement[0]; // establish the security context try { Any msg = null; if (client_context_id <= 0) { IdentityToken identityToken = new IdentityToken(); identityToken.absent(true); contextToken = sasContext.createClientContext(orb, codec, csmList); msg = makeEstablishContext(orb, -client_context_id, authorizationList, identityToken, contextToken); } else { msg = makeMessageInContext(orb, client_context_id, false); } ri.add_request_service_context(new ServiceContext(SecurityAttributeService, codec.encode_value( msg ) ), true); } catch (Exception e) { logger.error ("Could not set security service context", e); throw new org.omg.CORBA.NO_PERMISSION("SAS Could not set security service context: " + e, MinorCodes.SAS_CSS_FAILURE, CompletionStatus.COMPLETED_NO); } } public void send_poll(org.omg.PortableInterceptor.ClientRequestInfo ri) { } public void receive_reply(org.omg.PortableInterceptor.ClientRequestInfo cri) { ClientRequestInfoImpl ri = (ClientRequestInfoImpl)cri; if (ri.isLocalInterceptor()) { return; } // get SAS message SASContextBody contextBody = null; ServiceContext ctx = null; try { ctx = ri.get_reply_service_context(SecurityAttributeService); } catch (BAD_PARAM e) { if (logger.isDebugEnabled()) logger.debug("No SAS security context found: "+ri.operation()); } catch (Exception e) { if (logger.isWarnEnabled()) logger.warn("No SAS security context found: "+e); } if (ctx == null || ctx.context_data.length <= 1) return; try { Any msg = codec.decode_value( ctx.context_data, SASContextBodyHelper.type() ); contextBody = SASContextBodyHelper.extract(msg); } catch (Exception e) { logger.error("Could not parse SAS reply", e); throw new org.omg.CORBA.NO_PERMISSION("SAS Could not parse SAS reply: " + e, MinorCodes.SAS_CSS_FAILURE, CompletionStatus.COMPLETED_MAYBE); } ClientConnection connection = ri.getConnection(); // process CompleteEstablishContext message if (contextBody.discriminator() == MTCompleteEstablishContext.value) { CompleteEstablishContext reply = contextBody.complete_msg(); // if not stateful, remove from connection if (reply.client_context_id > 0 && !reply.context_stateful) connection.purgeSASContext(reply.client_context_id); } // process ContextError message if (contextBody.discriminator() == MTContextError.value) { ContextError reply = contextBody.error_msg(); // if stateful, remove from connection if (reply.client_context_id > 0) connection.purgeSASContext(reply.client_context_id); } } public void receive_exception(org.omg.PortableInterceptor.ClientRequestInfo cri) throws org.omg.PortableInterceptor.ForwardRequest { ClientRequestInfoImpl ri = (ClientRequestInfoImpl)cri; if (ri.isLocalInterceptor()) { return; } // get SAS message SASContextBody contextBody = null; ServiceContext ctx = null; try { ctx = ri.get_reply_service_context(SecurityAttributeService); } catch (BAD_PARAM e) { if (logger.isDebugEnabled()) logger.debug("No SAS security context found (exception): "+ri.operation()); } catch (Exception e) { if (logger.isWarnEnabled()) logger.warn("No SAS security context found (exception): "+e); } if (ctx == null || ctx.context_data.length <= 1) return; try { Any msg = codec.decode_value( ctx.context_data, SASContextBodyHelper.type() ); contextBody = SASContextBodyHelper.extract(msg); } catch (Exception e) { logger.error ("Could not parse SAS reply", e); throw new org.omg.CORBA.NO_PERMISSION("SAS Could not parse SAS reply: " + e, MinorCodes.SAS_CSS_FAILURE, CompletionStatus.COMPLETED_MAYBE); } ClientConnection connection = ri.getConnection(); // process CompleteEstablishContext message if (contextBody.discriminator() == MTCompleteEstablishContext.value) { CompleteEstablishContext reply = contextBody.complete_msg(); logger.debug("receive_exception MTCompleteEstablishContext: " + reply.client_context_id); // if not stateful, remove from connection if (reply.client_context_id > 0 && !reply.context_stateful) connection.purgeSASContext(reply.client_context_id); } // process ContextError message if (contextBody.discriminator() == MTContextError.value) { ContextError reply = contextBody.error_msg(); logger.debug("receive_exception MTContextError: " + reply.client_context_id); // if stateful, remove from connection if (reply.client_context_id > 0) connection.purgeSASContext(reply.client_context_id); // if context not found, resend with empty context cache if (reply.major_status == 2) throw new org.omg.PortableInterceptor.ForwardRequest(ri.target()); } } public void receive_other(org.omg.PortableInterceptor.ClientRequestInfo ri) throws org.omg.PortableInterceptor.ForwardRequest { } protected Any makeEstablishContext(org.omg.CORBA.ORB orb, long client_context_id, AuthorizationElement[] authorization_token, IdentityToken identity_token, byte[] client_authentication_token) { EstablishContext msg = new EstablishContext(); msg.client_context_id = client_context_id; msg.client_authentication_token = client_authentication_token; msg.identity_token = identity_token; msg.authorization_token = authorization_token; SASContextBody contextBody = new SASContextBody(); contextBody.establish_msg(msg); Any any = orb.create_any(); SASContextBodyHelper.insert( any, contextBody ); return any; } protected Any makeMessageInContext(org.omg.CORBA.ORB orb, long client_context_id, boolean discard_context) { MessageInContext msg = new MessageInContext(); msg.client_context_id = client_context_id; msg.discard_context = discard_context; SASContextBody contextBody = new SASContextBody(); contextBody.in_context_msg(msg); Any any = orb.create_any(); SASContextBodyHelper.insert( any, contextBody ); return any; } protected AuthorizationElement[] getATLASTokens(org.omg.CORBA.ORB orb, CompoundSecMechList csmList) throws org.omg.CORBA.NO_PERMISSION { // find the ATLAS profile in the IOR ATLASProfile atlasProfile = null; try { ServiceConfiguration authorities[] = csmList.mechanism_list[0].sas_context_mech.privilege_authorities; for (int i = 0; i < authorities.length; i++) { if (authorities[i].syntax != SCS_ATLAS.value) continue; Any any = codec.decode(authorities[i].name); atlasProfile = ATLASProfileHelper.extract(any); } } catch (Exception e) { logger.error ("Error parsing ATLAS from IOR", e); throw new org.omg.CORBA.NO_PERMISSION("SAS Error parsing ATLAS from IOR: " + e, MinorCodes.SAS_ATLAS_FAILURE, CompletionStatus.COMPLETED_NO); } if (atlasProfile == null) return new AuthorizationElement[0]; String cacheID = new String(atlasProfile.the_cache_id); String locator = atlasProfile.the_locator.the_url(); if (locator != null) locator = URLDecoder.decode(locator); // see if the tokens are in the ATLAS cache synchronized (atlasCache) { if (atlasCache.containsKey(cacheID)) { return atlasCache.get(cacheID).auth_token; } } // contact the ATLAS server and get the credentials AuthTokenDispenser dispenser = null; try { org.omg.CORBA.Object obj = orb.string_to_object(locator); dispenser = AuthTokenDispenserHelper.narrow(obj); } catch (Exception e) { logger.error("Could not find ATLAS server {}", locator, e); throw new org.omg.CORBA.NO_PERMISSION("SAS Could not find ATLAS server " + locator + ": " + e, MinorCodes.SAS_ATLAS_FAILURE, CompletionStatus.COMPLETED_NO); } if (dispenser == null) { logger.error ("SAS found null ATLAS server {}", locator); throw new org.omg.CORBA.NO_PERMISSION("SAS found null ATLAS server "+locator, MinorCodes.SAS_ATLAS_FAILURE, CompletionStatus.COMPLETED_NO); } AuthTokenData data = null; try { data = dispenser.get_my_authorization_token(); } catch (Exception e) { logger.warn("Error getting ATLAS tokens from server {}", locator, e); throw new org.omg.CORBA.NO_PERMISSION("SAS Error getting ATLAS tokens from server: " + e, MinorCodes.SAS_ATLAS_FAILURE, CompletionStatus.COMPLETED_NO); } synchronized (atlasCache) { atlasCache.put(cacheID, data); } return data.auth_token; } }