/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.iiop.csiv2; /*************************************** * * * JBoss: The OpenSource J2EE WebOS * * * * Distributable under LGPL license. * * See terms of license at gnu.org. * * * ***************************************/ import java.security.Principal; import org.omg.CORBA.Any; import org.omg.CORBA.BAD_PARAM; import org.omg.CORBA.CompletionStatus; import org.omg.CORBA.LocalObject; import org.omg.CORBA.MARSHAL; import org.omg.CORBA.NO_PERMISSION; import org.omg.CORBA.ORB; import org.omg.CSI.AuthorizationElement; import org.omg.CSI.EstablishContext; import org.omg.CSI.GSS_NT_ExportedNameHelper; import org.omg.CSI.ITTAnonymous; import org.omg.CSI.IdentityToken; import org.omg.CSI.MTContextError; import org.omg.CSI.SASContextBody; import org.omg.CSI.SASContextBodyHelper; import org.omg.IOP.Codec; import org.omg.IOP.CodecPackage.FormatMismatch; import org.omg.IOP.CodecPackage.InvalidTypeForEncoding; import org.omg.IOP.CodecPackage.TypeMismatch; import org.omg.IOP.ServiceContext; import org.omg.IOP.TaggedComponent; import org.omg.PortableInterceptor.ClientRequestInfo; import org.omg.PortableInterceptor.ClientRequestInterceptor; import org.omg.CSIIOP.CompoundSecMech; import org.omg.CSIIOP.CompoundSecMechList; import org.omg.CSIIOP.CompoundSecMechListHelper; import org.omg.CSIIOP.EstablishTrustInClient; import org.omg.CSIIOP.IdentityAssertion; import org.omg.CSIIOP.TAG_CSI_SEC_MECH_LIST; import org.omg.GSSUP.InitialContextToken; import org.jacorb.orb.MinorCodes; import org.jboss.logging.Logger; import org.jboss.security.SecurityAssociation; import org.jboss.security.RunAsIdentity; /** * This implementation of * <code>org.omg.PortableInterceptor.ClientRequestInterceptor</code> inserts * the security attribute service (SAS) context into outgoing IIOP requests * and handles the SAS messages received from the target security service * in the SAS context of incoming IIOP replies. * * @author <a href="mailto:reverbel@ime.usp.br">Francisco Reverbel</a> * @version $Revision: 81018 $ */ public class SASClientIdentityInterceptor extends LocalObject implements ClientRequestInterceptor { private static final Logger log = Logger.getLogger(SASClientIdentityInterceptor.class); private static final boolean traceEnabled = log.isTraceEnabled(); // Constants ------------------------------------------------------ private static final int sasContextId = org.omg.IOP.SecurityAttributeService.value; /* * Pre-built empty tokens */ private static final IdentityToken absentIdentityToken; static { absentIdentityToken = new IdentityToken(); absentIdentityToken.absent(true); } private static final AuthorizationElement[] noAuthorizationToken = {}; private static final byte[] noAuthenticationToken = {}; // Fields --------------------------------------------------------- private Codec codec; /* * Username and password of this server, in case it does not use an * SSL certificate to authenticate itself when acting as a client. */ private static final String serverUsername = "j2ee"; // hardcoded (REVISIT!) private static final String serverPassword = "j2ee"; // hardcoded (REVISIT!) // Constructor ---------------------------------------------------- public SASClientIdentityInterceptor(Codec codec) { this.codec = codec; } // Methods ------------------------------------------------------- // org.omg.PortableInterceptor.Interceptor operations ------------ public String name() { return "SASClientIdentityInterceptor"; } public void destroy() { // do nothing } // ClientRequestInterceptor operations --------------------------- public void send_request(ClientRequestInfo ri) { try { CompoundSecMech secMech = CSIv2Util.getMatchingSecurityMech( ri, codec, (short)(EstablishTrustInClient.value + IdentityAssertion.value), /* client supports */ (short)0 /* client requires */); if (secMech == null) return; if (traceEnabled) { StringBuffer tmp = new StringBuffer(); CSIv2Util.toString(secMech, tmp); log.trace(tmp); } // these "null tokens" will be changed if needed IdentityToken identityToken = absentIdentityToken; byte[] encodedAuthenticationToken = noAuthenticationToken; if ((secMech.sas_context_mech.target_supports & IdentityAssertion.value) != 0) { // will create identity token Principal p = null; RunAsIdentity runAs = SecurityAssociation.peekRunAsIdentity(); if (runAs != null) { // will use run-as identity p = runAs; } else { // will use caller identity p = SecurityAssociation.getPrincipal(); } if (p != null) { // The name scope needs to be externalized String name = p.getName(); if (name.indexOf('@') < 0) name += "@default"; // hardcoded (REVISIT!) byte[] principalName = name.getBytes("UTF-8"); // encode the principal name as mandated by RFC2743 byte[] encodedName = CSIv2Util.encodeGssExportedName(principalName); // encapsulate the encoded name Any any = ORB.init().create_any(); byte[] encapsulatedEncodedName = null; GSS_NT_ExportedNameHelper.insert(any, encodedName); try { encapsulatedEncodedName = codec.encode_value(any); } catch (InvalidTypeForEncoding e) { throw new RuntimeException("Unexpected exception: " + e); } // create identity token identityToken = new IdentityToken(); identityToken.principal_name(encapsulatedEncodedName); } else if ((secMech.sas_context_mech.supported_identity_types & ITTAnonymous.value) != 0) { // no run-as or caller identity and the target // supports ITTAnonymous: use the anonymous identity identityToken = new IdentityToken(); identityToken.anonymous(true); } } if ((secMech.as_context_mech.target_requires & EstablishTrustInClient.value) != 0) { // will create authentication token with the // configured pair serverUsername/serverPassword byte[] encodedTargetName = secMech.as_context_mech.target_name; String name = serverUsername; if (name.indexOf('@') < 0) { byte[] decodedTargetName = CSIv2Util.decodeGssExportedName(encodedTargetName); String targetName = new String(decodedTargetName, "UTF-8"); name += "@" + targetName; // "@default" } byte[] username = name.getBytes("UTF-8"); // I don't know why there is not a better way // to go from char[] -> byte[] byte[] password = serverPassword.getBytes("UTF-8"); // create authentication token InitialContextToken authenticationToken = new InitialContextToken(username, password, encodedTargetName); // ASN.1-encode it, as defined in RFC 2743 encodedAuthenticationToken = CSIv2Util.encodeInitialContextToken(authenticationToken, codec); } if (identityToken != absentIdentityToken || encodedAuthenticationToken != noAuthenticationToken) { // at least one non-null token was created, // create EstablishContext message with it EstablishContext message = new EstablishContext(0, // stateless ctx id noAuthorizationToken, identityToken, encodedAuthenticationToken); // create SAS context with the EstablishContext message SASContextBody contextBody = new SASContextBody(); contextBody.establish_msg(message); // stuff the SAS context into the outgoing request Any any = ORB.init().create_any(); SASContextBodyHelper.insert(any, contextBody); ServiceContext sc = new ServiceContext(sasContextId, codec.encode_value(any)); ri.add_request_service_context(sc, true /*replace existing context*/); } } catch (java.io.UnsupportedEncodingException e) { throw new MARSHAL("Unexpected exception: " + e); } catch (InvalidTypeForEncoding e) { throw new MARSHAL("Unexpected exception: " + e); } } public void send_poll(ClientRequestInfo ri) { // do nothing } public void receive_reply(ClientRequestInfo ri) { try { ServiceContext sc = ri.get_reply_service_context(sasContextId); Any msg = codec.decode_value(sc.context_data, SASContextBodyHelper.type()); SASContextBody contextBody = SASContextBodyHelper.extract(msg); // At this point contextBody should contain a // CompleteEstablishContext message, which does not require any // treatment. ContextError messages should arrive via // receive_exception(). if (traceEnabled) log.trace("receive_reply: got SAS reply, type " + contextBody.discriminator()); if (contextBody.discriminator() == MTContextError.value) { // should not happen log.warn("Unexpected ContextError in SAS reply"); throw new NO_PERMISSION("Unexpected ContextError in SAS reply", MinorCodes.SAS_CSS_FAILURE, CompletionStatus.COMPLETED_YES); } } catch (BAD_PARAM e) { // no service context with sasContextId: do nothing } catch (FormatMismatch e) { throw new MARSHAL("Could not parse SAS reply: " + e, 0, CompletionStatus.COMPLETED_YES); } catch (TypeMismatch e) { throw new MARSHAL("Could not parse SAS reply: " + e, 0, CompletionStatus.COMPLETED_YES); } } public void receive_exception(ClientRequestInfo ri) { try { ServiceContext sc = ri.get_reply_service_context(sasContextId); Any msg = codec.decode_value(sc.context_data, SASContextBodyHelper.type()); SASContextBody contextBody = SASContextBodyHelper.extract(msg); // At this point contextBody may contain a either a // CompleteEstablishContext message or a ContextError message. // Neither message requires any treatment. We decoded the context // body just to check that it contains a well-formed message. if (traceEnabled) log.trace("receive_exceptpion: got SAS reply, type " + contextBody.discriminator()); } catch (BAD_PARAM e) { // no service context with sasContextId: do nothing } catch (FormatMismatch e) { throw new MARSHAL("Could not parse SAS reply: " + e, MinorCodes.SAS_CSS_FAILURE, CompletionStatus.COMPLETED_MAYBE); } catch (TypeMismatch e) { throw new MARSHAL("Could not parse SAS reply: " + e, MinorCodes.SAS_CSS_FAILURE, CompletionStatus.COMPLETED_MAYBE); } } public void receive_other(ClientRequestInfo ri) { // do nothing } CompoundSecMech getSecurityMech(ClientRequestInfo ri) { CompoundSecMechList csmList = null; CompoundSecMech securityMech = null; try { TaggedComponent tc = ri.get_effective_component(TAG_CSI_SEC_MECH_LIST.value); Any any = codec.decode_value(tc.component_data, CompoundSecMechListHelper.type()); csmList = CompoundSecMechListHelper.extract(any); // at this point you can inspect the fields csmList.stateful // and csmList.mechanism_list. The latter is an array of // org.omg.CSIIOP.CompoundSecMech instances, which in our IORs // has length 1. // // The actual info you want is in csmList.mechanism_list[0]. securityMech = csmList.mechanism_list[0]; } catch (BAD_PARAM e) { // no component with TAG_CSI_SEC_MECH_LIST was found } catch (org.omg.IOP.CodecPackage.TypeMismatch tm) { // unexpected exception in codec.decode_value } catch (org.omg.IOP.CodecPackage.FormatMismatch tm) { // unexpected exception in codec.decode_value } return securityMech; } }