/*********************************************************************** * * Copyright (C) 2005-2006 Novell, 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 System.Xml; using System.Xml.Serialization; using System.Security.Cryptography; using System.Text; using sscs.cache; using sscs.common; using sscs.constants; using sscs.lss; using sscs.crypto; namespace sscs.cache { class SecretStore { internal string secretStoreName; // User name ? internal int refCount; private uint version; private Hashtable tKeyChainList = new Hashtable(); private Hashtable keyChainList; //= Hashtable.Synchronized(tKeyChainList); internal User user; private Mutex ssMutex ; //reqd only for refCount private int state; // Maintains the state of SS ( keychain // type availability). TODO: Convert to a class. private static int STATE_NOT_DEFINED = 0; private static int STATE_OK = 1; private static int STATE_LOCKED = 2; private LocalStorage lss = null; bool bIsStorePersistent = false; private DateTime createTime; public DateTime CreateTime { get { return createTime; } set { createTime = value; } } ~SecretStore() { ssMutex.Close(); } internal SecretStore(User ssUser) { secretStoreName = ssUser.GetUserName(); version = 1; state = STATE_NOT_DEFINED; user = ssUser; refCount = 0; keyChainList = Hashtable.Synchronized(tKeyChainList); ssMutex = new Mutex(); } internal bool IsStorePersistent() { return bIsStorePersistent; } public bool StopPersistence() { if(lss != null && bIsStorePersistent == true) { lss.StopPersistence(); lss = null; bIsStorePersistent = false; } return true; } public bool IsStoreLocked() { if (state == STATE_LOCKED) return true; else return false; } public void LockStore() { state = STATE_LOCKED; } public bool UnlockStore(string sDesktopPassword, string sMasterPassword) { if (sDesktopPassword != null) { // verify Desktop password //state = STATE_OK; //return true; } if (sMasterPassword != null) { // verify MasterPassword if (SetMasterPassword(sMasterPassword)) { state = STATE_OK; return true; } } return false; } internal bool StartPersistenceByDesktopPasswd(string desktopPasswd) { CSSSLogger.DbgLog("StartPersistenceByDesktopPasswd - Called"); // make sure we have a user home directory if (GetUserHomeDirectory() == null || GetUserHomeDirectory().Length < 1) { CSSSLogger.DbgLog("StartPersistenceByDesktopPasswd - No Home directory yet"); return false; } else { if (!Directory.Exists(GetUserHomeDirectory())) { CSSSLogger.DbgLog("StartPersistenceByDesktopPasswd - Home directory is not created yet"); return false; } } try { byte[] baPasscode; /* Persistence could have started because the user * could have set master password. */ if(lss != null && bIsStorePersistent == true) { /* Verify passcode and if validation fails, rewrite * desktop file. */ if(File.Exists(GetPasscodeByDesktopFilePath())) { } else { /* Write the desktop passwd file. */ } CSSSLogger.DbgLog(CSSSLogger.GetExecutionPath(this) + " Store is already persistent"); CSSSLogger.DbgLog("StartPersistenceByDesktopPasswd - Started"); return true; } if(!File.Exists(GetPasscodeByDesktopFilePath())) { if (File.Exists(GetPasscodeByMasterPasswdFilePath())) { // wait for the user to start the Persistence by entering MP return false; } //Else passcode needs to be generated. baPasscode = CASACrypto.GenerateMasterPasscodeUsingString( desktopPasswd, GetPasscodeByDesktopFilePath(), GetValidationFilePath(), user.UserIdentifier); if( null == baPasscode ) return false; if(!File.Exists(GetKeyFilePath())) { GenerateAndStoreEncryptionKey(baPasscode); lss = new LocalStorage(this,baPasscode); bIsStorePersistent = true; return true; } } baPasscode = CASACrypto.GetMasterPasscodeUsingDesktopPasswd(desktopPasswd, GetPasscodeByDesktopFilePath()); if(baPasscode != null) { if(CASACrypto.ValidatePasscode(baPasscode,GetValidationFilePath())) { lss = new LocalStorage(this,baPasscode); bIsStorePersistent = true; return true; } else { lss = null; bIsStorePersistent = false; //till masterPasswd is verified } return true; } else { CSSSLogger.DbgLog(CSSSLogger.GetExecutionPath(this) + " May be desktop passwd has changed"); lss = null; bIsStorePersistent = false; return false; } } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); } return false; } internal bool GenerateAndStoreEncryptionKey(byte[] baPasscode) { RijndaelManaged myRijndael = new RijndaelManaged(); byte[] key; byte[] IV = new byte[16]; //Create a new key and initialization vector. try { myRijndael.GenerateKey(); key = myRijndael.Key; CASACrypto.StoreKeySetUsingMasterPasscode(key,IV, baPasscode, GetKeyFilePath()); } catch (Exception e) { return false; } return true; } internal bool SetMasterPassword(string mPasswdFromIDK) { try { char[] trimChars = {'\0'}; string mPasswd = mPasswdFromIDK.TrimEnd(trimChars); bool isVerifyOperation = false; string mPasswdFileName = GetPasscodeByMasterPasswdFilePath(); byte[] baPasscode; if(File.Exists(mPasswdFileName)) isVerifyOperation = true; //else it is a set operation. string desktopPasswd = GetDesktopPasswd(); if(isVerifyOperation == false) { /* Here the master password file needs to be generated. */ if(desktopPasswd != null) { baPasscode = CASACrypto.GetMasterPasscodeUsingDesktopPasswd(desktopPasswd, GetPasscodeByDesktopFilePath()); if(CASACrypto.ValidatePasscode(baPasscode,GetValidationFilePath())) { CASACrypto.EncryptAndStoreMasterPasscodeUsingString( baPasscode, mPasswd, GetPasscodeByMasterPasswdFilePath()); return true; } else { //Probably desktop passwd has changed. //But as even master passwd is being set only now, //the persistent store is lost. baPasscode = CASACrypto.GenerateMasterPasscodeUsingString(mPasswd,GetPasscodeByMasterPasswdFilePath(),GetValidationFilePath(), user.UserIdentifier); if(baPasscode != null) { CASACrypto.EncryptAndStoreMasterPasscodeUsingString(baPasscode,mPasswd,GetPasscodeByMasterPasswdFilePath()); CASACrypto.EncryptAndStoreMasterPasscodeUsingString(baPasscode,desktopPasswd,GetPasscodeByDesktopFilePath()); if(File.Exists(GetPersistenceFilePath())) { File.Delete(GetPersistenceFilePath()); CSSSLogger.DbgLog("Removing the persistent storeas its meaningless now."); } if( bIsStorePersistent == false ) { lss = new LocalStorage(this,baPasscode); bIsStorePersistent = true; } return true; } else { return false; } } //return true; }//if a valid desktop Passwd is present - if ends here else { /* If desktop passwd is not there and user sets * master password. */ if(File.Exists(GetPersistenceFilePath())) { File.Delete(GetPersistenceFilePath()); CSSSLogger.DbgLog("Removing the persistent storeas its meaningless now. - Desktop passwd is not there and Master password is being set"); } if(File.Exists((GetPasscodeByDesktopFilePath()))) { File.Delete((GetPasscodeByDesktopFilePath())); CSSSLogger.DbgLog("Removing the persistent storeas its meaningless now. - Desktop passwd is not there and Master password is being set"); } baPasscode = CASACrypto.GenerateMasterPasscodeUsingString(mPasswd,GetPasscodeByMasterPasswdFilePath(),GetValidationFilePath(), user.UserIdentifier); if(baPasscode != null) { if(!File.Exists(GetKeyFilePath())) { GenerateAndStoreEncryptionKey(baPasscode); } CASACrypto.EncryptAndStoreMasterPasscodeUsingString(baPasscode,mPasswd,GetPasscodeByMasterPasswdFilePath()); if( bIsStorePersistent == false ) { lss = new LocalStorage(this,baPasscode); bIsStorePersistent = true; } return true; } return false; } }//end of isVerifyOperation == false else { /* Verify the master password. If verified, and if * persistence has not started, start it. */ //Get the passcode from master passwd file and validate. //If validation succeeds,start persistence. if(desktopPasswd == null) { baPasscode = CASACrypto.DecryptMasterPasscodeUsingString(mPasswd, GetPasscodeByMasterPasswdFilePath()); if(CASACrypto.ValidatePasscode(baPasscode,GetValidationFilePath())) { if(bIsStorePersistent == false) { lss = new LocalStorage(this,baPasscode); bIsStorePersistent = true; } return true; } else { return false; } } else { //There are 2 cases - either desktop passwd has changed //or it hasnt. baPasscode = CASACrypto.GetMasterPasscodeUsingMasterPasswd(mPasswd, GetPasscodeByMasterPasswdFilePath()); if(CASACrypto.ValidatePasscode(baPasscode,GetValidationFilePath())) { RewriteDesktopPasswdFile(baPasscode,desktopPasswd); if(bIsStorePersistent == false) { lss = new LocalStorage(this,baPasscode); bIsStorePersistent = true; } return true; } else { return false; } } } } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); } return false; }//End of SetMasterPassword internal bool RewriteDesktopPasswdFile(byte[] baPasscode, string desktopPasswd) { try { CASACrypto.EncryptAndStoreMasterPasscodeUsingString(baPasscode, desktopPasswd, GetPasscodeByDesktopFilePath()); CSSSLogger.DbgLog("Re-encryted passcode with desktop passwd"); } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); } return true; } internal byte[] GetPasscodeFromOldDesktopPasswd(string oldDesktopPasswd) { try { byte[] baPasscode = CASACrypto.GetMasterPasscodeUsingDesktopPasswd(oldDesktopPasswd, GetPasscodeByDesktopFilePath()); if(CASACrypto.ValidatePasscode(baPasscode,GetValidationFilePath())) { return baPasscode; } } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); } return null; } /* This method would be called, when the user is setting his * master passcode for the first time. */ internal bool SetMasterPasscode(string sMasterPasscode) { return true; #if false bool bRet = false; try { if(!CASACrypto.CheckIfMasterPasscodeIsAvailable(desktopPasswd, GetPasswdFilePath())) { RijndaelManaged myRijndael = new RijndaelManaged(); byte[] key; byte[] IV = new byte[16]; //Create a new key and initialization vector. myRijndael.GenerateKey(); key = myRijndael.Key; CASACrypto.StoreKeySetUsingMasterPasscode(key,IV,sMasterPasscode,GetKeyFilePath()); //Store the master passcode encrypted with the desktopPasswd CASACrypto.EncryptAndStoreMasterPasscodeUsingString(sMasterPasscode, desktopPasswd, GetPasswdFilePath()); lss = new LocalStorage(this,sMasterPasscode); bIsStorePersistent = true; bRet = true; } else { //Console.WriteLine("Master passcode is already set"); } } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); } return bRet; #endif } internal void IncrRefCount() { try { ssMutex.WaitOne(); refCount++; ssMutex.ReleaseMutex(); CSSSLogger.DbgLog(CSSSLogger.GetExecutionPath(this) + " : RefCount = " + refCount); } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); throw e; } } internal void DecrRefCount() { try { ssMutex.WaitOne(); refCount--; ssMutex.ReleaseMutex(); CSSSLogger.DbgLog(CSSSLogger.GetExecutionPath(this) + " : RefCount = " + refCount); } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); throw e; } } internal bool AddKeyChain(KeyChain keychain) { try { keychain.CreatedTime = DateTime.Now; keyChainList.Add(keychain.GetKey(),keychain); } catch(Exception e) { CSSSLogger.DbgLog(e.ToString()); throw e; } CSSSLogger.DbgLog(CSSSLogger.GetExecutionPath(this) + " - Succefully added Keychain = "+ keychain.GetKey() + " length = "+ (keychain.GetKey()).Length); return true; } internal bool RemoveKeyChain(string id) { keyChainList.Remove(id); return true; } internal KeyChain GetKeyChainDefault() { return GetKeyChain("SSCS_SESSION_KEY_CHAIN_ID\0"); } internal KeyChain GetKeyChain(string id) { if(keyChainList.ContainsKey(id)) { CSSSLogger.DbgLog("In " + CSSSLogger.GetExecutionPath(this) + " Keychain already exists."); KeyChain kc = (KeyChain)(keyChainList[id]); kc.AccessedTime = DateTime.Now; return kc; } else { CSSSLogger.DbgLog("In " + CSSSLogger.GetExecutionPath(this) + " Keychain doesnot exist.Returning null."); throw new KeyChainDoesNotExistException(id); } } internal bool CheckIfKeyChainExists(string id) { if(keyChainList.ContainsKey(id)) return true; else return false; } internal void UpdatePersistentStore() { if (lss != null) lss.PersistStoreWithDelay(); } /* This function would need to do any storage/cleanup required * before removing a user session. */ internal bool CommitStore() { if(lss != null) lss.PersistStore(); return true; } internal IEnumerator GetKeyChainEnumerator() { //TBD // Return an Enumerator class which has all secrets in this keychain return keyChainList.GetEnumerator(); } internal void DumpSecretstore() { lock(keyChainList.SyncRoot) { IDictionaryEnumerator iter = (IDictionaryEnumerator)GetKeyChainEnumerator(); while( iter.MoveNext() ) { int i = 0; KeyChain kc = (KeyChain)iter.Value; CSSSLogger.DbgLog("\nKeychain id = " + kc.GetKey()); CSSSLogger.DbgLog("Secret List is "); IDictionaryEnumerator secIter = (IDictionaryEnumerator)(kc.GetAllSecrets()); while(secIter.MoveNext()) { Secret secret = (Secret)secIter.Value; CSSSLogger.DbgLog("Secret " + i.ToString() + " id = " + secret.GetKey() + " value = " + secret.GetValue() ); IDictionaryEnumerator etor = (IDictionaryEnumerator) secret.GetKeyValueEnumerator(); while(etor.MoveNext()) { KeyValue kv = (KeyValue)etor.Value; CSSSLogger.DbgLog("Key = " + kv.Key +" Value = " + kv.GetValue()); } i++; } } } } internal int GetSecretStoreState() { return state; } internal int GetNumKeyChains() { return keyChainList.Count; } internal bool SetSecretStoreState(int stateToSet) { //BrainShare Special Only - Only Session keychains state 1 state = STATE_OK; return true; } internal bool ChangeMasterPassword(string sCurrentPWD, string sNewPWD) { string sMasterFilePath = GetPasscodeByMasterPasswdFilePath(); byte[] baPasscode = CASACrypto.GetMasterPasscodeUsingMasterPasswd(sCurrentPWD, sMasterFilePath); if (baPasscode != null) { CASACrypto.EncryptAndStoreMasterPasscodeUsingString(baPasscode, sNewPWD, sMasterFilePath); return true; } return false; } internal string GetDesktopPasswd() { try { string keyChainId = ConstStrings.SSCS_SESSION_KEY_CHAIN_ID + "\0"; KeyChain keyChain = GetKeyChain(keyChainId); Secret secret = keyChain.GetSecret(ConstStrings.MICASA_DESKTOP_PASSWD); string passwd = secret.GetKeyValue(ConstStrings.MICASA_DESKTOP_PASSWD_KEYNAME).GetValue(); return passwd; } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); } return null; } internal string GetUserHomeDirectory() { return user.GetUserHomeDir(); } internal string GetKeyFilePath() { string homeDir = GetUserHomeDirectory(); return homeDir + ConstStrings.MICASA_KEY_FILE; } internal string GetPasscodeByDesktopFilePath() { string homeDir = GetUserHomeDirectory(); return homeDir + ConstStrings.MICASA_PASSCODE_BY_DESKTOP_FILE; } internal string GetPasscodeByMasterPasswdFilePath() { string homeDir = GetUserHomeDirectory(); return homeDir + ConstStrings.MICASA_PASSCODE_BY_MASTERPASSWD_FILE; } internal string GetPersistenceFilePath() { string homeDir = GetUserHomeDirectory(); return homeDir + ConstStrings.MICASA_PERSISTENCE_FILE; } internal string GetValidationFilePath() { string homeDir = GetUserHomeDirectory(); return homeDir + ConstStrings.MICASA_VALIDATION_FILE; } } }