FileDocCategorySizeDatePackage
SecServerRequestInterceptor.javaAPI DocGlassfish v2 API25849Fri May 04 22:34:58 BST 2007com.sun.enterprise.iiop.security

SecServerRequestInterceptor.java

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. 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.sun.enterprise.iiop.security;

/**
 * This class is a server side request interceptor for CSIV2. 
 * It is used to send and receive the service context in a 
 * a service context element in the service context list in  
 * an IIOP header.
 *
 * @author: Sekhar Vajjhala
 * @author: Harpreet Singh
 * @author: Shing Wai Chan
 */

import org.omg.CORBA.*;
import org.omg.PortableInterceptor.*;
import org.omg.IOP.*;

import java.util.*;
import java.security.cert.X509Certificate;

/* Import classes generated from CSIV2 idl files */
import com.sun.corba.ee.org.omg.CSI.*;
import com.sun.corba.ee.org.omg.GSSUP.*;

/* Import classes required for DER encoding and decoding */
import sun.security.util.DerInputStream;
import sun.security.util.DerOutputStream;
import sun.security.util.DerValue;

import sun.security.x509.*;    // for X500 DN names 
import javax.security.auth.*;  // for JAAS subject

import com.sun.enterprise.security.auth.login.PasswordCredential;
import com.sun.enterprise.security.auth.login.X509CertificateCredential;
import com.sun.enterprise.util.ORBManager;
import com.sun.enterprise.util.LocalStringManagerImpl;
import com.sun.enterprise.log.Log;
import java.util.logging.*;
import com.sun.logging.*;
/*
 * Security server request interceptor 
 */

public class SecServerRequestInterceptor
    extends    org.omg.CORBA.LocalObject
    implements ServerRequestInterceptor
{

    private static java.util.logging.Logger _logger=null;
    static{
       _logger=LogDomains.getLogger(LogDomains.CORBA_LOGGER);
        }
    private static LocalStringManagerImpl localStrings =
        new LocalStringManagerImpl(SecServerRequestInterceptor.class);
    private InheritableThreadLocal counterForCalls = new InheritableThreadLocal();

    /** 
     *  Hard code the value of 15 for SecurityAttributeService until
     *  it is defined in IOP.idl.
     *     sc.context_id = SecurityAttributeService.value;
     */
    protected static final int SECURITY_ATTRIBUTE_SERVICE_ID = 15;
    // the major and minor codes for a invalid mechanism
    private static final int INVALID_MECHANISM_MAJOR = 2;
    private static final int INVALID_MECHANISM_MINOR = 1;

    /* used when inserting into service context field */
    private static final boolean  NO_REPLACE = false; 

    /* name of interceptor used for logging purposes (name + "::") */
    private String prname; 
    private String name;
    private Codec  codec;
    private ORB orb;
    SecurityService secsvc = null;       // Security Service


    public SecServerRequestInterceptor( String name, Codec codec) {
        this.name    = name;
        this.codec   = codec;
        this.prname  = name + "::";
    }

    public String name() {
        return name;
    }

    /**
     * Create a ContextError message. This is currently designed to work only
     * for the GSSUP mechanism.
     */

    /* Create a ContexError Message */
    private SASContextBody createContextError(int status) {
        /** 
         * CSIV2 SPEC NOTE:
         * 
         * Check that CSIV2 spec does not require an error token to be sent
         * for the GSSUP mechanism.
         */

        return createContextError(1, status);
    }

    /* create a context error with the specified major and minor status
     */
    private SASContextBody createContextError(int major, int minor) {
        
        if(_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE,"Creating ContextError message: major code = "+ major+ "minor code= "+ minor);
        }
        byte error_token[] = {} ;
        ContextError ce = new ContextError(0,      /* stateless client id */
                                           major, // major
                                           minor, // minor
                                           error_token);
        SASContextBody sasctxtbody = new SASContextBody();
        sasctxtbody.error_msg(ce);
        return sasctxtbody;
    }

    /**
     * Create a CompleteEstablishContext Message. This currently works only
     * for the GSSUP mechanism.
     */
    private SASContextBody createCompleteEstablishContext(int status) {
        /** 
         * CSIV2 SPEC NOTE:
         * 
         * Check CSIV2 spec to make sure that there is no 
         * final_context_token for GSSUP mechanism
         */
        if(_logger.isLoggable(Level.FINE)){
            _logger.log(Level.FINE,"Creating CompleteEstablishContext message");
        }
        byte[] final_context_token  = {} ;
        CompleteEstablishContext cec = new CompleteEstablishContext(
                              0,      // stateless client id
                              false,  // for stateless 
                              final_context_token);
        SASContextBody sasctxtbody = new SASContextBody();
        sasctxtbody.complete_msg(cec);
        return sasctxtbody;
    }

    /**
     *  CDR encode a SAS Context body and then construct a service context
     *  element.
     */
    private ServiceContext createSvcContext(SASContextBody sasctxtbody) {

        ServiceContext sc = null;

        Any a = orb.create_any();
        SASContextBodyHelper.insert(a, sasctxtbody);

        byte[] cdr_encoded_saselm = {};
        try {
            cdr_encoded_saselm = codec.encode_value(a);
        } catch (Exception e) {
                _logger.log(Level.SEVERE,"iiop.encode_exception",e);
        }
        sc               = new ServiceContext();
        sc.context_id    = SECURITY_ATTRIBUTE_SERVICE_ID;
        sc.context_data  = cdr_encoded_saselm;
        return sc;

    }

    /**
     * Create an identity from an Identity Token and stores it as a
     * public credential in the JAAS subject in a security context.
     * 
     * Set the identcls field in the security context.
     * 
     */
    private void createIdCred(SecurityContext sc, IdentityToken idtok)
        throws Exception {

        byte[] derenc ; // used to hold DER encodings
        Any    any;     // Any object returned from codec.decode_value()

        switch (idtok.discriminator()) {

        case ITTAbsent.value:
            if(_logger.isLoggable(Level.FINE)){
                _logger.log(Level.FINE,"Identity token type is Absent");
            }
            sc.identcls = null;
            break;

        case ITTAnonymous.value:
            if(_logger.isLoggable(Level.FINE)){
                _logger.log(Level.FINE,"Identity token type is Anonymous");
		_logger.log(Level.FINE,"Adding AnonyCredential to subject's PublicCredentials");
            }
            sc.subject.getPublicCredentials().add(new AnonCredential());
            sc.identcls = AnonCredential.class;
            break;

        case ITTDistinguishedName.value:
            /* Construct a X500Name */

            derenc = idtok.dn();
            /* Issue 5766: Decode CDR encoding if necessary */
            if (isCDR(derenc)) {
                any = codec.decode_value(derenc, X501DistinguishedNameHelper.type());

                /* Extract CDR encoding */
                derenc = X501DistinguishedNameHelper.extract(any);
            }
            if(_logger.isLoggable(Level.FINE)){
                _logger.log(Level.FINE,"Create an X500Name object from identity token");
            }
            X500Name name = new X500Name(derenc);
	    if(_logger.isLoggable(Level.FINE)) {
                _logger.log(Level.FINE,"Identity to be asserted is " + name.toString());
		_logger.log(Level.FINE,"Adding X500Name to subject's PublicCredentials");
	    }
            sc.subject.getPublicCredentials().add(name);
            sc.identcls = X500Name.class;
            break;
            
        case ITTX509CertChain.value:
            /*  Construct a X509CertificateChain */
            if(_logger.isLoggable(Level.FINE)){
                _logger.log(Level.FINE,"Identity token type is a X509 Certificate Chain");
            }
            derenc = idtok.certificate_chain();
            /* Issue 5766: Decode CDR encoding if necessary */
            if (isCDR(derenc)) {
                /* Decode CDR encoding */
                any = codec.decode_value(derenc, X509CertificateChainHelper.type());

                /* Extract DER encoding */
                derenc = X509CertificateChainHelper.extract(any);
            }

            DerInputStream din = new DerInputStream(derenc);

            /** 
             * Size specified for getSequence() is 1 and is just 
             * used as a guess by the method getSequence().
             */
            DerValue[] derval = din.getSequence(1);
            X509Certificate[] certchain = 
                        new X509CertImpl[derval.length];
            /**
             * X509Certificate does not have a constructor which can
             * be used to instantiate objects from DER encodings. So
             * use X509CertImpl extends X509Cerificate and also implements
             * DerEncoder interface. 
             */
            if(_logger.isLoggable(Level.FINE)){
		_logger.log(Level.FINE,"Contents of X509 Certificate chain:");
            }
            for (int i = 0; i < certchain.length; i++) {
                certchain[i] = new X509CertImpl(derval[i]);
                if(_logger.isLoggable(Level.FINE)){
                _logger.log(Level.FINE,"    " + certchain[i].getSubjectDN().getName());
                }
            }
            if(_logger.isLoggable(Level.FINE)){
                _logger.log(Level.FINE,"Creating a X509CertificateCredential object from certchain");
            }
            /**
             * The alias field in the X509CertificateCredential is currently ignored
             * by the RI. So it is set to "dummy".
             * 
             */
            X509CertificateCredential cred = 
                new X509CertificateCredential(certchain, certchain[0].getSubjectDN().getName(), "default");
            if(_logger.isLoggable(Level.FINE)){
                _logger.log(Level.FINE,"Adding X509CertificateCredential to subject's PublicCredentials");
            }
            sc.subject.getPublicCredentials().add(cred);
            sc.identcls = X509CertificateCredential.class;
            break;
 
        case ITTPrincipalName.value:
            if(_logger.isLoggable(Level.FINE)){
		_logger.log(Level.FINE,"Identity token type is GSS Exported Name");
            }
            byte[] expname = idtok.principal_name();
            /* Issue 5766: Decode CDR encoding if necessary */
            if (isCDR(expname)) {
                /* Decode CDR encoding */
                any = codec.decode_value(expname, GSS_NT_ExportedNameHelper.type());

                expname = GSS_NT_ExportedNameHelper.extract(any);
            }
            if ( ! GSSUtils.verifyMechOID(GSSUtils.GSSUP_MECH_OID, expname)) {
                throw new SecurityException(
                localStrings.getLocalString("secserverreqinterceptor.err_unknown_idassert_type",
                                            "Unknown identity assertion type."));
            }

            GSSUPName gssname = new GSSUPName(expname);

            sc.subject.getPublicCredentials().add(gssname);
            sc.identcls  = GSSUPName.class;
            _logger.log(Level.FINE,"Adding GSSUPName credential to subject");
            break;

        default:
		_logger.log(Level.SEVERE,"iiop.unknown_identity");              
            throw new SecurityException(
                localStrings.getLocalString("secserverreqinterceptor.err_unknown_idassert_type",
                                            "Unknown identity assertion type."));
        }
    }

    /**
     * Check if given byte is CDR encapsulated.
     * @param bytes an input array of byte
     * @return boolean indicates whether input is CDR
     */
    private boolean isCDR(byte[] bytes) {
        return (bytes != null && bytes.length > 0 && 
                (bytes[0] == 0x0 || bytes[0] == 0x1));
    }

    /**
     * Create an auth credential from authentication token and store
     * it as a private credential in the JAAS subject in the security 
     * context.
     *
     * Set the authcls field in the security context.
     *
     * This method currently only works for PasswordCredential tokens.
     */
    private void createAuthCred(SecurityContext sc, byte[] authtok) throws Exception
    {
		_logger.log(Level.FINE,"Constructing a PasswordCredential from client authentication token");
        /* create a GSSUPToken from the authentication token */
        GSSUPToken tok  = GSSUPToken.getServerSideInstance(orb, codec, authtok);

        final PasswordCredential pwdcred = tok.getPwdcred();
        final SecurityContext fsc = sc;
	    if(_logger.isLoggable(Level.FINE)) {
		_logger.log(Level.FINE,"Password credential = " + pwdcred.toString());
		_logger.log(Level.FINE,"Adding PasswordCredential to subject's PrivateCredentials");
	}
        java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() {
            public java.lang.Object run() {
                fsc.subject.getPrivateCredentials().add(pwdcred);
                return null;
            }
        });
        sc = fsc;
        sc.authcls  = PasswordCredential.class;
    }     
    
    private void handle_null_service_context(ServerRequestInfo ri, ServiceContext sc) {
        if(_logger.isLoggable(Level.FINE)){
            _logger.log(Level.FINE,"No SAS context element found in service context list");
        }
        int secStatus = secsvc.setSecurityContext(null, ri.object_id(),
                ri.operation());
        
        if (secStatus == SecurityService.STATUS_FAILED){
            SASContextBody sasctxbody = createContextError(INVALID_MECHANISM_MAJOR,
                    INVALID_MECHANISM_MINOR);
            sc = createSvcContext(sasctxbody);
            ri.add_reply_service_context(sc, NO_REPLACE);
            if(_logger.isLoggable(Level.FINE)) {
                _logger.log(Level.FINE,
                        "SecServerRequestInterceptor.receive_request: NO_PERMISSION");
            }
            throw new NO_PERMISSION();
        }       
    }
     
    public void receive_request(ServerRequestInfo ri) 
         throws ForwardRequest  
    {
        SecurityContext seccontext = null;   // SecurityContext to be sent
        ServiceContext  sc = null;           // service context
        int status = 0;
        boolean  raise_no_perm = false;

        if (_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE, "++++ Entered " + prname + "receive_request");
        }
        
        secsvc  = Csiv2Manager.getSecurityService();
        orb = ORBManager.getORB();

        try {
            sc = ri.get_request_service_context(SECURITY_ATTRIBUTE_SERVICE_ID);
            if (sc == null) {
                handle_null_service_context(ri, sc);
                return;
            }
        } catch (org.omg.CORBA.BAD_PARAM e) {
            handle_null_service_context(ri,sc);
            return;
        }

        if(_logger.isLoggable(Level.FINE)){
		_logger.log(Level.FINE,"Received a non null SAS context element");
        }
        /* Decode the service context field */
        Any SasAny = orb.create_any();
        try {        
            SasAny = codec.decode_value(sc.context_data, SASContextBodyHelper.type());
        } catch (Exception e) {
        _logger.log(Level.SEVERE,"iiop.decode_exception",e);
            throw new SecurityException(
                localStrings.getLocalString("secserverreqinterceptor.err_cdr_decode",
                                            "CDR Decoding error for SAS context element."));
        }

        if(_logger.isLoggable(Level.FINE)){
		_logger.log(Level.FINE,"Successfully decoded CDR encoded SAS context element.");
        }
        SASContextBody sasctxbody = SASContextBodyHelper.extract(SasAny);

        short sasdiscr = sasctxbody.discriminator();
        if(_logger.isLoggable(Level.FINE)){
		_logger.log(Level.FINE,"SAS context element is a/an " + SvcContextUtils.getMsgname(sasdiscr)+ " message");
        }
        /* Check message type received */

        /**
         *  CSIV2 SPEC NOTE:
         *
         *  Section 4.3 "TSS State Machine" , table 4-4 "TSS State Table" 
         *  shows that a MessageInContext can be received. In this case
         *  the table is somewhat unclear. But in this case a ContextError
         *  with the status code "No Context" ( specified in  
         *  section 4.5 "ContextError Values and Exceptions" must be sent back.
         *  A NO_PERMISSION exception must also be raised.
         *
         *  ISSUE: should setSecurityContext(null) be called ?
         */

        if (sasdiscr == MTMessageInContext.value) {
             sasctxbody = createContextError(SvcContextUtils.MessageInContextMinor);
             sc = createSvcContext(sasctxbody);
        if(_logger.isLoggable(Level.FINE)){
		_logger.log(Level.FINE,"Adding ContextError message to service context list");
		_logger.log(Level.FINE,"SecurityContext set to null");
        }
             ri.add_reply_service_context(sc, NO_REPLACE);
             // no need to set the security context
//              secsvc.setSecurityContext(null, ri.object_id(), ri.operation());

             throw new NO_PERMISSION();
        }

        /**
         * CSIV2 SPEC NOTE:
         * 
         * CSIV2 spec does not specify the actions for any message other than
         * a MessageInContext and EstablishContext message.So for such messages,
         * this implementation simply drops the message on the floor. No
         * other message is sent back. Neither is an exception raised.
         * 
         * ISSUE: Should there be some other action ?
         */

        if (sasdiscr != MTEstablishContext.value) {
            _logger.log(Level.SEVERE,"iiop.not_establishcontext_msg");
            throw new SecurityException(
                localStrings.getLocalString("secserverreqinterceptor.err_not_ec_msg",
                                            "Received message not an EstablishContext message."));
        }
 
        EstablishContext ec = sasctxbody.establish_msg();

        seccontext = new SecurityContext();
        seccontext.subject = new Subject();
        
        try {
            if (ec.client_authentication_token.length != 0) {
                if(_logger.isLoggable(Level.FINE)){
                    _logger.log(Level.FINE,"Message contains Client Authentication Token");
                }
                createAuthCred(seccontext, ec.client_authentication_token);
            }
        } catch (Exception e) {
            _logger.log(Level.SEVERE,"iiop.authentication_exception",e);
            throw new SecurityException(
                localStrings.getLocalString("secsercverreqinterceptor.err_cred_create",
                                            "Error while creating a JAAS subject credential."));


        }

        try{
            if (ec.identity_token != null) {
                if(_logger.isLoggable(Level.FINE)){
                    _logger.log(Level.FINE,"Message contains an Identity Token");
                }
                createIdCred(seccontext, ec.identity_token);
            }
        } catch (SecurityException secex){
            _logger.log(Level.SEVERE,"iiop.security_exception",secex);
            sasctxbody = createContextError(INVALID_MECHANISM_MAJOR,
                                            INVALID_MECHANISM_MINOR);
            sc = createSvcContext(sasctxbody);
            ri.add_reply_service_context(sc, NO_REPLACE);
            throw new NO_PERMISSION();
        } catch (Exception e) {
            _logger.log(Level.SEVERE,"iiop.generic_exception",e);
            throw new SecurityException(
                                        localStrings.getLocalString("secsercverreqinterceptor.err_cred_create",
                                                                    "Error while creating a JAAS subject credential."));
            
        }

        if(_logger.isLoggable(Level.FINE)){
            _logger.log(Level.FINE,"Invoking setSecurityContext() to set security context");
        }
        status = secsvc.setSecurityContext(seccontext, ri.object_id(), ri.operation());
	if(_logger.isLoggable(Level.FINE)){
		_logger.log(Level.FINE,"setSecurityContext() returned status code " + status);
        }
        /**
         * CSIV2 SPEC NOTE:
         *
         * If ec.client_context_id is non zero, then this is a stateful
         * request. As specified in section 4.2.1, a stateless server must
         * attempt to validate the security tokens in the security context
         * field. If validation succeeds then CompleteEstablishContext message
         * is sent back. If validation fails, a ContextError must be sent back.
         */
        if (status == SecurityService.STATUS_FAILED) {
            if(_logger.isLoggable(Level.FINE)){
		_logger.log(Level.FINE,"setSecurityContext() returned STATUS_FAILED");
            }
            sasctxbody = createContextError(status);
            sc = createSvcContext(sasctxbody);
            if(_logger.isLoggable(Level.FINE)){
		_logger.log(Level.FINE,"Adding ContextError message to service context list");
            }
            ri.add_reply_service_context(sc, NO_REPLACE);
            throw new NO_PERMISSION();
        }

        if(_logger.isLoggable(Level.FINE)){
		_logger.log(Level.FINE,"setSecurityContext() returned SUCCESS");
        }
        sasctxbody = createCompleteEstablishContext(status);
        sc = createSvcContext(sasctxbody);
        if(_logger.isLoggable(Level.FINE)){
            _logger.log(Level.FINE,"Adding CompleteEstablisContext message to service context list");
        }
        ri.add_reply_service_context(sc, NO_REPLACE);
    }

    /* This method is keeping a track of when to unset the security context
     * Currently with the re-use of the threads made by the orb the security
     * context does not get unset. This method determines when to unset the 
     * security context
     */
    public void receive_request_service_contexts(ServerRequestInfo ri)
         throws ForwardRequest
    {
        // cannot set this in receive_request due to the PI flow control
        // semantics. e.g. if receive_req for some other PI throws an 
        // exception - the send_exception will be called that will muck
        // the stack up
        Counter cntr = (Counter)counterForCalls.get();
        if (cntr == null){
            cntr = new Counter();
            counterForCalls.set(cntr);
        } 
        if (cntr.count == 0){
            SecurityService secsvc  = Csiv2Manager.getSecurityService();
            secsvc.unsetSecurityContext();
        }
        cntr.increment();
    }

    public void send_reply(ServerRequestInfo ri)
    {
        unsetSecurityContext();
    }
 
    public void send_exception(ServerRequestInfo ri) 
         throws ForwardRequest
    {
        unsetSecurityContext();
    }
   
    public void send_other(ServerRequestInfo ri)
         throws ForwardRequest
    {
        unsetSecurityContext();
    }

    public void destroy()
    {
    }

    private void unsetSecurityContext(){
        Counter cntr = (Counter)counterForCalls.get();
        if (cntr == null){      // sanity check
            cntr = new Counter(1);
        }
        cntr.decrement();
        if (cntr.count == 0){
            SecurityService secsvc  = Csiv2Manager.getSecurityService();
            secsvc.unsetSecurityContext();
        } 
    }
}

class Counter {
    
    public int count = 0;
    public Counter(int count){
        this.count = count;
    }
    public Counter(){
        count = 0;
    }
    public void setCount(int counter){
        count = counter;
    }
    public void increment(){
        count++;
    }
    public void decrement(){
        count--;
    }
    public String display(){
         return " Counter = " +count;
    }
}