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