using System; using System.IO; using System.Text; using System.Collections; using System.Threading; using System.Security.Cryptography; using System.Xml; using sscs.cache; using sscs.crypto; using sscs.common; using sscs.constants; using Novell.CASA.MiCasa.Common; namespace sscs.lss { /// /* * This class is a service to store data persistently. * How it does this is determined by implementation within the * private methods (File system using file(s), database, etc) * The MasterPasscode can be used to generate the key for * encyption and decryption. * If encrpytion is used, the private methods will also manage * how the encyption key is to be stored and retrieved. * Each piece of data is located by a DataID. * This might be an individual credentail or * a complete store. */ /* We might not need this as a separate class. * Depending on the db changes, we can change this later. */ /// public class LocalStorage { private byte[] m_baGeneratedKey = null; private SecretStore userStore = null; private int persistThreadSleepTime = 1000 * 60 * 5; //1000 * 30; private Thread persistThread = null; private static string LINUXID = "Unix"; internal LocalStorage(SecretStore store,byte[] baMasterPasscode) { userStore = store; m_baGeneratedKey = baMasterPasscode; LoadPersistentStore(); userStore.DumpSecretstore(); } ~LocalStorage() { if(persistThread != null) { persistThread.Abort(); persistThread.Join(); } } // allowing a user to choose the storage location is not approved yet private LocalStorage(SecretStore store, byte[] baMasterPasscode, string sStorageDirectory) { userStore = store; m_baGeneratedKey = baMasterPasscode; LoadPersistentStore(); userStore.DumpSecretstore(); } private void StorePersistentData(string sDataID, byte[] baData) { } private byte[] RetrievePersistentData(string sDataID) { return null; } public void PersistStoreWithDelay() { if (persistThread == null) { persistThread = new Thread(new ThreadStart(PersistStoreDelayThreadFn)); persistThread.Start(); } } public bool StopPersistence() { if(persistThread != null) { persistThread.Abort(); persistThread.Join(); } return true; } private string GetDecryptedXml() { try { string fileName = userStore.GetPersistenceFilePath(); if(!File.Exists(fileName)) { // check for tmp file if (File.Exists(fileName+".tmp")) File.Move(fileName+".tmp", fileName); else return null; } // delete tmp file if there if (File.Exists(fileName+".tmp")) File.Delete(fileName+".tmp"); byte[] baPasscode = null; if (null != m_baGeneratedKey) baPasscode = m_baGeneratedKey; else baPasscode = CASACrypto.GetMasterPasscode(userStore.GetDesktopPasswd(),userStore.GetPasscodeByDesktopFilePath()); if( null == baPasscode ) return null; byte[] key = CASACrypto.GetKeySetFromFile(baPasscode,userStore.GetKeyFilePath()); if( null == key ) return null; byte[] decryptedBuffer = CASACrypto.ReadFileAndDecryptData(key,fileName); if( null == decryptedBuffer ) return null; string temp = Encoding.UTF8.GetString(decryptedBuffer, 0, decryptedBuffer.Length); return temp; } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); CSSSLogger.DbgLog("Unable to get persistent store"); } return null; } /* This method, uses the key to decrypt the persistent store * and populates userStore with the persistent data. */ private bool LoadPersistentStore() { try { string xpath = ""; XmlDocument doc = new XmlDocument(); string xmlToLoad = GetDecryptedXml(); if(xmlToLoad != null) { doc.LoadXml(xmlToLoad); #if false XmlTextWriter writer = new XmlTextWriter("/home/poorna/.miCASA.xml",null); writer.Formatting = Formatting.Indented; doc.Save(writer); writer.Close(); #endif } else { return false; } xpath = "//" + XmlConsts.miCASANode; XmlNode miCASANode = doc.SelectSingleNode(xpath); if(miCASANode != null) { xpath = "descendant::" + XmlConsts.keyChainNode; XmlNodeList keyChainNodeList = miCASANode.SelectNodes(xpath); foreach(XmlNode node in keyChainNodeList) { XmlAttributeCollection attrColl = node.Attributes; string keyChainId = (attrColl[XmlConsts.idAttr]).Value + "\0"; KeyChain keyChain = null; if( userStore.CheckIfKeyChainExists(keyChainId) == false ) { keyChain = new KeyChain(keyChainId); userStore.AddKeyChain(keyChain); } else { keyChain = userStore.GetKeyChain(keyChainId); } xpath = "descendant::" + XmlConsts.secretNode; XmlNodeList secretNodeList = node.SelectNodes(xpath); foreach(XmlNode secretNode in secretNodeList) { attrColl = secretNode.Attributes; string secretId = (attrColl[XmlConsts.idAttr]).Value + "\0"; xpath = "descendant::" + XmlConsts.valueNode; Secret secret = new Secret(secretId); if( keyChain.CheckIfSecretExists(secretId) == false) { keyChain.AddSecret(secret); XmlNode secretValNode = (secretNode.SelectSingleNode(xpath)); xpath = "descendant::" + XmlConsts.keyNode; XmlNodeList keyNodeList = secretValNode.SelectNodes(xpath); secret = keyChain.GetSecret(secretId); foreach(XmlNode keyNode in keyNodeList) { attrColl = keyNode.Attributes; string key; try { key = (attrColl[XmlConsts.idAttr]).Value; } catch (Exception) { // LinkedKey node, continue continue; } xpath = "descendant::" + XmlConsts.keyValueNode; XmlNode keyValNode = keyNode.SelectSingleNode(xpath); string keyValue = keyValNode.InnerText; secret.SetKeyValue(key,keyValue); // add linked keys xpath = "descendant::" + XmlConsts.linkedKeyNode; XmlNodeList linkNodeList = keyNode.SelectNodes(xpath); foreach(XmlNode linkNode in linkNodeList) { // get TargetSecretID xpath = "descendant::" + XmlConsts.linkedTargetSecretNode; XmlNode targetSecretNode = linkNode.SelectSingleNode(xpath); string sSecretID = targetSecretNode.InnerText + "\0"; // get TargetSecretKey xpath = "descendant::" + XmlConsts.linkedTargetKeyNode; XmlNode targetKeyNode = linkNode.SelectSingleNode(xpath); string sKeyID = targetKeyNode.InnerText; LinkedKeyInfo lki = new LinkedKeyInfo(sSecretID, sKeyID); KeyValue kv = secret.GetKeyValue(key); kv.AddLink(lki); } } }//if ends } }//end of traversing keyChainNodeList } } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); } return true; } private void PersistStoreDelayThreadFn() { Thread.Sleep(30000); PersistStore(); persistThread = null; } private void PersistStoreThreadFn() { while(true) { Thread.Sleep(persistThreadSleepTime); PersistStore(); } } /* Persists the store to an xml file. * TBD : Would we require any form of encoding? */ internal void PersistStore() { // userStore.DumpSecretstore(); try { MemoryStream ms1 = new MemoryStream(); XmlTextWriter writer = new XmlTextWriter(ms1,null); writer.Formatting = Formatting.Indented; writer.WriteStartDocument(); writer.WriteStartElement(XmlConsts.miCASANode); writer.WriteAttributeString(XmlConsts.versionAttr,"1.5"); { IDictionaryEnumerator iter = (IDictionaryEnumerator)userStore.GetKeyChainEnumerator(); char [] tmpId; string sTmpId; while( iter.MoveNext() ) { KeyChain kc = (KeyChain)iter.Value; writer.WriteStartElement(XmlConsts.keyChainNode); string kcId = kc.GetKey(); tmpId = new char[kcId.Length-1]; for(int i = 0; i < kcId.Length-1; i++ ) tmpId[i] = kcId[i]; sTmpId = new string(tmpId); writer.WriteAttributeString(XmlConsts.idAttr,sTmpId); /* If we need to store time writer.WriteStartElement(XmlConsts.timeNode); writer.WriteAttributeString(XmlConsts.createdTimeNode,kc.CreatedTime.ToString()); writer.WriteAttributeString(XmlConsts.modifiedTimeNode,kc.ModifiedTime.ToString()); writer.WriteEndElement(); */ IDictionaryEnumerator secIter = (IDictionaryEnumerator)(kc.GetAllSecrets()); while(secIter.MoveNext()) { Secret secret = (Secret)secIter.Value; writer.WriteStartElement(XmlConsts.secretNode); string secretId = secret.GetKey(); tmpId = new char[secretId.Length-1]; for(int i = 0; i < secretId.Length-1; i++ ) tmpId[i] = secretId[i]; sTmpId = new string(tmpId); writer.WriteAttributeString(XmlConsts.idAttr,sTmpId); /* If we need to store time writer.WriteStartElement(XmlConsts.timeNode); writer.WriteAttributeString(XmlConsts.createdTimeNode,secret.CreatedTime.ToString()); writer.WriteAttributeString(XmlConsts.modifiedTimeNode,secret.ModifiedTime.ToString()); writer.WriteEndElement(); */ writer.WriteStartElement(XmlConsts.valueNode); // byte[] byteArr = secret.GetValue(); IDictionaryEnumerator etor = (IDictionaryEnumerator)secret.GetKeyValueEnumerator(); while(etor.MoveNext()) { string sKey = (string)etor.Key; string value = secret.GetKeyValue(sKey).GetValue(); writer.WriteStartElement(XmlConsts.keyNode); writer.WriteAttributeString(XmlConsts.idAttr, sKey); writer.WriteStartElement(XmlConsts.keyValueNode); writer.WriteString(value); writer.WriteEndElement(); /* If we need to store time writer.WriteStartElement(XmlConsts.timeNode); writer.WriteAttributeString(XmlConsts.createdTimeNode,(secret.GetKeyValueCreatedTime(sKey)).ToString()); writer.WriteAttributeString(XmlConsts.modifiedTimeNode,(secret.GetKeyValueModifiedTime(sKey)).ToString()); writer.WriteEndElement(); */ // write all LinkKeys Hashtable htLinkedKeys = secret.GetLinkedKeys(sKey); if (htLinkedKeys != null) { IDictionaryEnumerator etorLinked = (IDictionaryEnumerator)htLinkedKeys.GetEnumerator(); while(etorLinked.MoveNext()) { LinkedKeyInfo lki = (LinkedKeyInfo)etorLinked.Value; writer.WriteStartElement(XmlConsts.linkedKeyNode); writer.WriteStartElement(XmlConsts.linkedTargetSecretNode); writer.WriteString(lki.GetLinkedSecretID().Substring(0, lki.GetLinkedSecretID().Length-1)); writer.WriteEndElement(); writer.WriteStartElement(XmlConsts.linkedTargetKeyNode); writer.WriteString(lki.GetLinkedKeyID()); writer.WriteEndElement(); writer.WriteEndElement(); } } writer.WriteEndElement(); } /* char[] chArr = new char[byteArr.Length]; for(int z = 0; z < byteArr.Length; z++) chArr[z] = (char)byteArr[z]; string stringToStore = new string(chArr); writer.WriteString(stringToStore); */ writer.WriteEndElement(); //end of value node writer.WriteEndElement(); } writer.WriteEndElement(); //keychain } } writer.WriteEndElement(); //miCASA node writer.WriteEndDocument(); writer.Flush(); writer.Close(); //byte[] key = CASACrypto.GetKeySetFromFile(CASACrypto.GetMasterPasscode(userStore.GetDesktopPasswd(),userStore.GetPasscodeByDesktopFilePath()),userStore.GetKeyFilePath()); byte[] key = CASACrypto.GetKeySetFromFile(m_baGeneratedKey, userStore.GetKeyFilePath()); string fileName = userStore.GetPersistenceFilePath(); // rename existing file if(File.Exists(fileName)) { if (File.Exists(fileName+".tmp")) File.Delete(fileName+".tmp"); File.Move(fileName, fileName+".tmp"); } CASACrypto.EncryptDataAndWriteToFile(ms1.ToArray(),key,fileName); //remove temp if(File.Exists(fileName+".tmp")) { File.Delete(fileName+".tmp"); } } catch(Exception e) { CSSSLogger.ExpLog(e.ToString()); } } } }