Home | History | Annotate | Download | only in session
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 package org.opensolaris.os.vp.server.module.session;
     28 
     29 import com.sun.cacao.agent.auth.*;
     30 import java.security.*;
     31 import java.util.*;
     32 import java.util.logging.*;
     33 import javax.management.ObjectName;
     34 import javax.security.auth.Subject;
     35 import org.opensolaris.os.vp.common.panel.SessionMXBean;
     36 import org.opensolaris.os.vp.util.misc.Base64Util;
     37 
     38 /**
     39  * 'Session' mechanism, accepts session-key in the private credential
     40  * to look up an existing connection's Subject and re-use it.
     41  * @author Nick Stephen
     42  */
     43 public class SessionMechanism implements Mechanism, SessionMXBean {
     44 
     45     /* Tracing utility. */
     46     private static final Logger logger = Logger.getLogger(
     47         SessionMechanism.class.getPackage().getName());
     48 
     49     public Logger getLogger() {
     50         return logger;
     51     }
     52 
     53     private static final SecureRandom sr = new SecureRandom();
     54 
     55     private static final Map<String, Subject> sessionIdSubjectMap =
     56         new WeakHashMap<String, Subject>();
     57 
     58     private static final Map<String, Properties> sessionIdPropertiesMap =
     59         new WeakHashMap<String, Properties>();
     60 
     61     private static SessionMechanism myInstance;
     62 
     63     /**
     64      * Return the name of this mechanism, must correspond to first field
     65      * of the SASL/PLAIN authentication-id
     66      * @return a <code>String</code> value
     67      */
     68     public String getName() {
     69 	return (SESSION_MECHANISM_NAME);
     70     }
     71 
     72     public ObjectName getObjectName() {
     73 	return (SessionMXBean.OBJECT_NAME);
     74     }
     75     /**
     76      * Obtain a reference to the session mechanism instance
     77      * @return a reference to the singleton SessionMechanism instance
     78      */
     79     public synchronized static SessionMechanism getInstance() {
     80 	if (myInstance == null) {
     81 	    myInstance = new SessionMechanism();
     82 	}
     83 	return myInstance;
     84     }
     85 
     86     /**
     87      * Instance reference must be obtained through getInstance().
     88      */
     89     protected SessionMechanism() {
     90         // empty
     91     }
     92 
     93     /**
     94      * Indicate if this mechanism asserts identity
     95      * @return a <code>boolean</code> value
     96      */
     97     public boolean isIdentityAsserted() {
     98 	return false;
     99     }
    100 
    101     /**
    102      * Parse and validate the given credentials, returning a CallbackInfo
    103      * @return a <code>CallbackInfo</code> value
    104      *
    105      * @param authenticationID a <code>String</code> value
    106      * @param password a <code>String</code> value
    107      * @param authPieces a <code>String[]</code> value containing fields
    108      * @param passwdPieces a <code>String[]</code> value containing fields
    109      *
    110      * @exception SecurityException if an error occurs
    111      */
    112     public CallbackInfo parse(String authenticationID,
    113 	String password, String[] authPieces, String[] passwdPieces)
    114 	throws SecurityException {
    115 
    116 	if (logger.isLoggable(Level.FINER)) {
    117 	    logger.finer("Authentication_Module: SASL/PLAIN: "+
    118 			 "authenticate-id: " +
    119 			 authenticationID + ", mechanism: " + authPieces[0] +
    120 			 ", public credential: " + authPieces[1]);
    121 	}
    122 
    123 	// Try and find a suitable 'Subject' object with these credentials
    124 
    125         // private credentials contains our session id
    126 	Subject subject = sessionIdSubjectMap.get(passwdPieces[0]);
    127 
    128         if (subject == null) {
    129             throw (new SecurityException(
    130 		"Authentication failed - invalid session ID"));
    131         }
    132 	CallbackInfo callbackInfo = new CallbackInfo(subject, false);
    133 
    134 	logger.finer("Authentication_Module: authenticated session");
    135 
    136 	return callbackInfo;
    137     }
    138 
    139     // Implementation of MBean interface
    140 
    141     public String getSessionSaslAuthenticationId() {
    142 
    143 	return (SESSION_MECHANISM_NAME + CacaoCallbackHandler.SEPARATOR +
    144 	    "existing session");
    145     }
    146 
    147     public String getSessionSaslPassword() {
    148 	return getSessionId();
    149     }
    150 
    151     public synchronized String getSessionId() {
    152 
    153         // get the Subject from the current thread context
    154         java.security.AccessControlContext acc =
    155             java.security.AccessController.getContext();
    156         javax.security.auth.Subject subject =
    157             javax.security.auth.Subject.getSubject(acc);
    158 
    159         if (subject == null) {
    160             throw new SecurityException("No subject");
    161         }
    162         Set sessionCredSet =
    163 	    subject.getPrivateCredentials(SessionCredential.class);
    164         String sessionId = null;
    165         switch (sessionCredSet.size()) {
    166             case 0 : {
    167                 // need to create new session id
    168 
    169                 byte[] bytes = new byte[1024 / 8];
    170                 sr.nextBytes(bytes);
    171                 sessionId = Base64Util.encode(bytes);
    172                 SessionCredential sessionCred =
    173 		    new SessionCredential(sessionId);
    174 
    175                 subject.getPrivateCredentials().add(sessionCred);
    176 
    177                 sessionIdSubjectMap.put(sessionId, subject);
    178                 sessionIdPropertiesMap.put(sessionId, new Properties());
    179                 break;
    180             }
    181             case 1: {
    182                 // session Id already exists
    183                 SessionCredential sessionCred =
    184 		    (SessionCredential)sessionCredSet.iterator().next();
    185                 sessionId = sessionCred.getSessionId();
    186                 break;
    187             }
    188             default : {
    189                 throw (new RuntimeException(
    190 		    "Too many SessionId objects in Subject"));
    191             }
    192         }
    193         return sessionId;
    194     }
    195 
    196     public synchronized boolean invalidateSession() {
    197         // get the Subject from the current thread context
    198         java.security.AccessControlContext acc =
    199             java.security.AccessController.getContext();
    200         javax.security.auth.Subject subject =
    201             javax.security.auth.Subject.getSubject(acc);
    202 
    203         if (subject == null) {
    204             return false;
    205         }
    206         Set sessionCredSet =
    207 	    subject.getPrivateCredentials(SessionCredential.class);
    208         switch (sessionCredSet.size()) {
    209             case 0 : {
    210                 // nothing to do
    211                 return false;
    212             }
    213             case 1: {
    214                 // session Id exists, get it
    215                 SessionCredential sessionCred =
    216 		    (SessionCredential)sessionCredSet.iterator().next();
    217                 String sessionId = sessionCred.getSessionId();
    218                 // delete our session id from the subject credentials
    219                 subject.getPrivateCredentials().removeAll(sessionCredSet);
    220                 // delete our session id from our internal Maps
    221                 sessionIdSubjectMap.remove(sessionId);
    222                 sessionIdPropertiesMap.remove(sessionId);
    223 
    224                 return true;
    225             }
    226             default : {
    227 		/*
    228 		 * delete session ids from the subject credentials
    229 		 * before throwing exception
    230 		 */
    231                 subject.getPrivateCredentials().removeAll(sessionCredSet);
    232                 throw (new RuntimeException(
    233 		    "Too many SessionId objects in Subject"));
    234             }
    235         }
    236     }
    237 
    238     public synchronized String getProperty(String key) {
    239 
    240         String sessionId = getSessionId();
    241         Properties props = sessionIdPropertiesMap.get(sessionId);
    242         if (props == null) {
    243             throw new RuntimeException("No properties object available");
    244         }
    245         return props.getProperty(key);
    246     }
    247 
    248     public synchronized String setProperty(String key, String value) {
    249 
    250         String sessionId = getSessionId();
    251         Properties props = sessionIdPropertiesMap.get(sessionId);
    252         if (props == null) {
    253             throw new RuntimeException("No properties object available");
    254         }
    255         return (String)props.setProperty(key, value);
    256     }
    257 
    258     public synchronized String[] getPropertyNames() {
    259         String sessionId = getSessionId();
    260         Properties props = sessionIdPropertiesMap.get(sessionId);
    261         if (props == null) {
    262             throw new RuntimeException("No properties object available");
    263         }
    264         return (new ArrayList<Object>(props.keySet()).toArray(new String[] {}));
    265     }
    266 
    267     public synchronized Map<String, String> getPropertyMap() {
    268         String sessionId = getSessionId();
    269         Properties props = sessionIdPropertiesMap.get(sessionId);
    270 
    271 	/* Sigh; work around broken Properties definition */
    272 	Map<String, String> result = new HashMap<String, String>(props.size());
    273 	for (Map.Entry<Object, Object> entry : props.entrySet())
    274 	    result.put(entry.getKey().toString(),
    275 		entry.getValue().toString());
    276 
    277 	return (result);
    278     }
    279 
    280     private static class SessionCredential {
    281         private String sessionId = null;
    282 
    283         private SessionCredential(String id) {
    284             this.sessionId = id;
    285         }
    286 
    287         private String getSessionId() {
    288             return sessionId;
    289         }
    290     }
    291 }
    292