/*********************************************************************** * * Copyright (C) 2005-2006 Novell, Inc. Inc. All Rights Reserved. * * This library 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; version 2.1 * of the License. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, Novell, Inc. * * To contact Novell about this file by physical or electronic mail, * you may find current contact information at www.novell.com. * ***********************************************************************/ using System; using System.Collections; using System.Threading; using System.IO; using sscs.cache; using sscs.common; using sscs.constants; using System.Diagnostics; namespace sscs.common { class SessionManager { private static readonly SessionManager sessionManager = new SessionManager(); private static Mutex mutex = new Mutex(); private static Hashtable sessionTable = new Hashtable(); private static Thread tJanitor = null; private static int JANITOR_SLEEP_TIME = 1000*60*5; // 5 minutes private SessionManager() { #if LINUX if (tJanitor == null) { tJanitor = new Thread(new ThreadStart(CleanUpSessionsThread)); tJanitor.Start(); } #endif } ~SessionManager() { if (tJanitor != null) { tJanitor.Abort(); tJanitor.Join(); } mutex.Close(); } internal static SessionManager GetSessionManager { get { return sessionManager; } } internal static SecretStore CreateUserSession(UserIdentifier userId) { CSSSLogger.ExecutionTrace(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); SecretStore ss; userId.PrintIdentifier(); try { ss = GetUserSecretStore(userId); ss.IncrRefCount(); ss.CreateTime = DateTime.Now; return ss; } catch(UserNotInSessionException e) { // Would create either windows/unix user // depending on the platform. User user = User.CreateUser(userId); mutex.WaitOne(); sessionTable.Add(userId,user); mutex.ReleaseMutex(); ss = user.GetSecretStore(); ss.IncrRefCount(); ss.CreateTime = DateTime.Now; return ss; } } internal static bool RemoveUserSession(UserIdentifier userId, bool destroySession) { CSSSLogger.ExecutionTrace(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); try { mutex.WaitOne(); SecretStore ss = GetUserSecretStore(userId); ss.DecrRefCount(); // We must keep the cache alive, and destroy it on // a logout event //if( 0 == ss.refCount ) if (destroySession) { CSSSLogger.DbgLog("Removing the user session of " + userId.GetUID()); ss.CommitStore(); sessionTable.Remove(userId); } mutex.ReleaseMutex(); return true; } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); } return false; } internal static bool CheckIfUserSessionExists(UserIdentifier userId) { CSSSLogger.ExecutionTrace(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); mutex.WaitOne(); if( sessionTable.ContainsKey(userId) ) { mutex.ReleaseMutex(); return true; } else { mutex.ReleaseMutex(); return false; } } internal static SecretStore GetUserSecretStore(UserIdentifier userId) { CSSSLogger.ExecutionTrace(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); userId.PrintIdentifier(); ListActiveUserSessions(); mutex.WaitOne(); if( sessionTable.ContainsKey(userId) ) { User user = (User)sessionTable[userId]; SecretStore ss = user.GetSecretStore(); // start persistent if not going yet if (!ss.IsStorePersistent()) { string sDesktopPWD = ss.GetDesktopPasswd(); if (sDesktopPWD != null) ss.StartPersistenceByDesktopPasswd(sDesktopPWD); } mutex.ReleaseMutex(); return ss; } else { mutex.ReleaseMutex(); throw new UserNotInSessionException(userId); } } internal static DateTime GetSessionCreateTime(UserIdentifier userId) { CSSSLogger.ExecutionTrace(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); mutex.WaitOne(); if( sessionTable.ContainsKey(userId) ) { User user = (User)sessionTable[userId]; SecretStore ss = user.GetSecretStore(); mutex.ReleaseMutex(); return ss.CreateTime; } else { mutex.ReleaseMutex(); throw new UserNotInSessionException(userId); } } internal static void ListActiveUserSessions() { CSSSLogger.ExecutionTrace(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); mutex.WaitOne(); IDictionaryEnumerator etor = sessionTable.GetEnumerator(); int i = 0; while(etor.MoveNext()) { i++; /* CSSSLogger.DbgLog("Listing Active User Sessions"); Console.WriteLine(etor.Key); Console.WriteLine((((SecretStore)(etor.Value)).secretStoreName + ":" + ((SecretStore)(etor.Value)).refCount); */ } mutex.ReleaseMutex(); } private static void CleanUpSessionsThread() { CSSSLogger.ExecutionTrace(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); try { while (true) { // enumerate users in the session IEnumerator etor; ICollection keys = sessionTable.Keys; if (keys != null) { etor = keys.GetEnumerator(); while(etor.MoveNext()) { UserIdentifier userIdentifier = (UserIdentifier)etor.Current; // check if this user still has // processes running if(CheckAndDestroySession(userIdentifier,false)) { /* If at least 1 session was removed, * the etor must be * re-initiated, else * Invalidoperationexception will be * thrown. */ keys = sessionTable.Keys; if( null == keys ) break; else { etor = keys.GetEnumerator(); } } }//while etor.MoveNext ends here. } Thread.Sleep(JANITOR_SLEEP_TIME); } //while true ends here. } catch(ThreadAbortException e) { CSSSLogger.DbgLog("Janitor thread is going down."); } }//Method ends here. /* As the pam module does a seteuid(), when is ps is * execed it would appear as if the user owns the process. * Hence, if this method is called from CloseSecretStore * verb ( that would have been initiated from the pam * module with ssFlags = 1), then if number of processes * is one, then delete the session. */ internal static bool CheckAndDestroySession(UserIdentifier userID, bool calledFromClose) { CSSSLogger.ExecutionTrace(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); int iUID = userID.GetUID(); bool retVal = false; Process myProcess = null; StreamReader myStreamReader = null; if (iUID != -1) { // make the 'ps h U UID' call try { myProcess = new Process(); ProcessStartInfo myProcessStartInfo = new ProcessStartInfo("ps" ); myProcessStartInfo.Arguments = "h U " + iUID.ToString(); myProcessStartInfo.UseShellExecute = false; myProcessStartInfo.RedirectStandardOutput = true; myProcess.StartInfo = myProcessStartInfo; myProcess.Start(); myProcess.WaitForExit(); myStreamReader = myProcess.StandardOutput; // Read the standard output of the spawned process. string myString = myStreamReader.ReadLine(); int numProcs = 0; while( myString != null) { if(numProcs > 1) break; numProcs++; myString = myStreamReader.ReadLine(); } do { /* If this has been called from * CloseSecretStore verb, * verb, the session must be deleted. */ if( calledFromClose ) { RemoveUserSession(userID, true); retVal = true; break; } /* If the session was created during login, * and the janitor thread starts processing * before user login is completed, we need * maintain the user session (say for 5 mts). */ if( (numProcs == 0) && (CheckIfLoginTimeSession(userID)) ) { retVal = false; break; } /* If the user does not own any processes and * if this method has not been called from * CloseSecretStore verb, it implies that a user * background process, which existed during user * logout has died now. * So, clean the user session. */ if ( (numProcs == 0) && (!calledFromClose) ) { RemoveUserSession(userID, true); retVal = true; break; } }while(false); /* myProcess.Close(); myStreamReader.Close(); */ } catch (Exception e) { CSSSLogger.DbgLog(e.ToString()); } finally { if( myProcess != null ) myProcess.Close(); if( myStreamReader != null ) myStreamReader.Close(); } } return retVal; } internal static bool CheckIfLoginTimeSession(UserIdentifier userId) { if( ((TimeSpan)(DateTime.Now - GetSessionCreateTime(userId))).TotalMinutes < 3 ) { return true; } else return false; } } }