780 lines
24 KiB
C#
780 lines
24 KiB
C#
/***********************************************************************
|
|
*
|
|
* 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.IO;
|
|
using System.Text;
|
|
using System.Collections;
|
|
using System.Threading;
|
|
using System.Security.Cryptography;
|
|
using System.Xml;
|
|
#if LINUX
|
|
using Mono.Unix.Native;
|
|
#endif
|
|
using sscs.cache;
|
|
using sscs.crypto;
|
|
using sscs.common;
|
|
using sscs.constants;
|
|
using Novell.CASA.MiCasa.Common;
|
|
using Novell.CASA.CASAPolicy;
|
|
|
|
namespace sscs.lss
|
|
{
|
|
/// <summary>
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
/// </summary>
|
|
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 Thread sPersistThread = null;
|
|
|
|
#if LINUX
|
|
Mono.Unix.UnixFileSystemInfo sockFileInfo;
|
|
Mono.Unix.UnixUserInfo sockFileOwner;
|
|
#endif
|
|
|
|
private static string LINUXID = "Unix";
|
|
|
|
internal LocalStorage(SecretStore store, byte[] baMasterPasscode, bool dummy) // Merge this with the next cons - RAJ
|
|
{
|
|
userStore = store;
|
|
m_baGeneratedKey = baMasterPasscode;
|
|
LoadPersistentStore(ConstStrings.SSCS_SERVER_KEY_CHAIN_ID);
|
|
//userStore.DumpSecretstore();
|
|
}
|
|
|
|
internal LocalStorage(SecretStore store,byte[] baMasterPasscode)
|
|
{
|
|
userStore = store;
|
|
m_baGeneratedKey = baMasterPasscode;
|
|
LoadPersistentStore(ConstStrings.SSCS_SESSION_KEY_CHAIN_ID);
|
|
userStore.DumpSecretstore();
|
|
}
|
|
|
|
~LocalStorage()
|
|
{
|
|
if(persistThread != null)
|
|
{
|
|
persistThread.Abort();
|
|
persistThread.Join();
|
|
}
|
|
|
|
if(sPersistThread != null)
|
|
{
|
|
sPersistThread.Abort();
|
|
sPersistThread.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(ConstStrings.SSCS_SESSION_KEY_CHAIN_ID);
|
|
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 void PersistServerStoreWithDelay()
|
|
{
|
|
if (sPersistThread == null)
|
|
{
|
|
sPersistThread = new Thread(new ThreadStart(PersistServerStoreDelayThreadFn));
|
|
sPersistThread.Start();
|
|
}
|
|
}
|
|
|
|
public bool StopPersistence()
|
|
{
|
|
if(persistThread != null)
|
|
{
|
|
persistThread.Abort();
|
|
persistThread.Join();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public bool StopServerPersistence()
|
|
{
|
|
if(sPersistThread != null)
|
|
{
|
|
sPersistThread.Abort();
|
|
sPersistThread.Join();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public bool IsOwnedByRoot(string fileName)
|
|
{
|
|
#if LINUX
|
|
sockFileInfo = new Mono.Unix.UnixFileInfo(fileName);
|
|
sockFileOwner = sockFileInfo.OwnerUser;
|
|
if(0==sockFileOwner.UserId)
|
|
return true;
|
|
else
|
|
return false;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
private string GetDecryptedServerSecretsXml()
|
|
{
|
|
try
|
|
{
|
|
string fileName = userStore.GetServerSecretsPersistenceFilePath();
|
|
string tempFile = fileName;
|
|
int count = 0;
|
|
if(!File.Exists(fileName))
|
|
{
|
|
while(true)
|
|
{
|
|
// check for tmp file
|
|
if (File.Exists(tempFile+".tmp"))
|
|
{
|
|
if(IsOwnedByRoot(tempFile+".tmp"))
|
|
{
|
|
File.Move(tempFile+".tmp", fileName);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
count++;
|
|
tempFile = fileName + count.ToString();
|
|
}
|
|
}
|
|
else
|
|
return null;
|
|
|
|
}
|
|
|
|
// delete tmp file if there
|
|
if (File.Exists(tempFile+".tmp"))
|
|
{
|
|
if(IsOwnedByRoot(tempFile+".tmp"))
|
|
File.Delete(tempFile+".tmp");
|
|
}
|
|
}
|
|
|
|
byte[] baPasscode = null;
|
|
if (null != m_baGeneratedKey)
|
|
baPasscode = m_baGeneratedKey;
|
|
else
|
|
baPasscode = CASACrypto.GetServerMasterPasscodeUsingSystemKey(userStore.GetServerPasscodeBySystemKeyFilePath());
|
|
|
|
if( null == baPasscode )
|
|
return null;
|
|
|
|
byte[] key = CASACrypto.GetKeySetFromFile(baPasscode,userStore.GetServerKeyFilePath());
|
|
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;
|
|
}
|
|
|
|
private string GetDecryptedXml()
|
|
{
|
|
try
|
|
{
|
|
string fileName = userStore.GetPersistenceFilePath();
|
|
string tempFile = fileName;
|
|
int count = 0;
|
|
if(!File.Exists(fileName))
|
|
{
|
|
while(true)
|
|
{
|
|
// check for tmp file
|
|
if (File.Exists(tempFile+".tmp"))
|
|
{
|
|
if(IsOwnedByRoot(tempFile+".tmp"))
|
|
{
|
|
File.Move(tempFile+".tmp", fileName);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
count++;
|
|
tempFile = fileName + count.ToString();
|
|
}
|
|
}
|
|
else
|
|
return null;
|
|
|
|
}
|
|
|
|
// delete tmp file if there
|
|
if (File.Exists(tempFile+".tmp"))
|
|
{
|
|
if(IsOwnedByRoot(tempFile+".tmp"))
|
|
File.Delete(tempFile+".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(string keyChainId)
|
|
{
|
|
try
|
|
{
|
|
//string xpath = "";
|
|
XmlDocument doc = new XmlDocument();
|
|
|
|
string xmlToLoad = null;
|
|
if ( keyChainId == ConstStrings.SSCS_SESSION_KEY_CHAIN_ID )
|
|
xmlToLoad = GetDecryptedXml();
|
|
else if ( keyChainId == ConstStrings.SSCS_SERVER_KEY_CHAIN_ID )
|
|
xmlToLoad = GetDecryptedServerSecretsXml();
|
|
|
|
if(xmlToLoad != null)
|
|
{
|
|
doc.LoadXml(xmlToLoad);
|
|
|
|
#if false
|
|
XmlTextWriter writer = new XmlTextWriter("d:/persist.xml",null);
|
|
writer.Formatting = Formatting.Indented;
|
|
doc.Save(writer);
|
|
writer.Close();
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// add these to the store
|
|
AddXMLSecretsToStore(userStore, doc);
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
CSSSLogger.ExpLog(e.ToString());
|
|
}
|
|
|
|
// collect now to remove old data from memory
|
|
GC.Collect();
|
|
|
|
return true;
|
|
}
|
|
|
|
internal static void AddXMLSecretsToStore(SecretStore userStore, XmlDocument doc)
|
|
{
|
|
string xpath = "";
|
|
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);
|
|
|
|
// set the created time if possible
|
|
XmlNode timeNode = node.SelectSingleNode("descendant::" + XmlConsts.timeNode);
|
|
if (timeNode != null)
|
|
{
|
|
XmlAttributeCollection timeAttribCol = timeNode.Attributes;
|
|
if (timeAttribCol != null)
|
|
{
|
|
XmlNode createdTimeNode = timeAttribCol.GetNamedItem(XmlConsts.createdTimeNode);
|
|
if (createdTimeNode != null)
|
|
{
|
|
//Console.WriteLine("KeyChain create time:" + new DateTime(long.Parse(createdTimeNode.Value)));
|
|
}
|
|
else
|
|
{
|
|
//Console.WriteLine("Create time not found");
|
|
}
|
|
XmlNode modifiedTimeNode = timeAttribCol.GetNamedItem(XmlConsts.modifiedTimeNode);
|
|
if (modifiedTimeNode != null)
|
|
{
|
|
//Console.WriteLine("KeyChain mod time:" + new DateTime(long.Parse(modifiedTimeNode.Value)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
|
|
// get time stamps for this secret
|
|
XmlNode timeNode = secretNode.SelectSingleNode("descendant::" + XmlConsts.timeNode);
|
|
if (timeNode != null)
|
|
{
|
|
//Console.WriteLine("Secret: " + secretId);
|
|
XmlAttributeCollection timeAttribCol = timeNode.Attributes;
|
|
if (timeAttribCol != null)
|
|
{
|
|
XmlNode createdTimeNode = timeAttribCol.GetNamedItem(XmlConsts.createdTimeNode);
|
|
if (createdTimeNode != null)
|
|
{
|
|
//Console.WriteLine("Secret create time:" + new DateTime(long.Parse(createdTimeNode.Value)));
|
|
}
|
|
else
|
|
{
|
|
//Console.WriteLine("Create time not found");
|
|
}
|
|
|
|
XmlNode modifiedTimeNode = timeAttribCol.GetNamedItem(XmlConsts.modifiedTimeNode);
|
|
if (modifiedTimeNode != null)
|
|
{
|
|
//Console.WriteLine("Secret mod time:" + new DateTime(long.Parse(modifiedTimeNode.Value)));
|
|
}
|
|
else
|
|
{
|
|
//Console.WriteLine("mod time not found");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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, true);
|
|
KeyValue kv = secret.GetKeyValue(key);
|
|
kv.AddLink(lki);
|
|
}
|
|
|
|
}
|
|
}//if ends
|
|
}
|
|
|
|
}//end of traversing keyChainNodeList
|
|
}
|
|
}
|
|
|
|
private void PersistStoreDelayThreadFn()
|
|
{
|
|
Thread.Sleep(15000);
|
|
PersistStore(ConstStrings.SSCS_SESSION_KEY_CHAIN_ID);
|
|
persistThread = null;
|
|
}
|
|
|
|
private void PersistServerStoreDelayThreadFn()
|
|
{
|
|
Thread.Sleep(15000);
|
|
PersistStore(ConstStrings.SSCS_SERVER_KEY_CHAIN_ID);
|
|
sPersistThread = null;
|
|
}
|
|
|
|
private void PersistStoreThreadFn()
|
|
{
|
|
while(true)
|
|
{
|
|
Thread.Sleep(persistThreadSleepTime);
|
|
PersistStore(ConstStrings.SSCS_SESSION_KEY_CHAIN_ID);
|
|
}
|
|
}
|
|
|
|
private void PersistServerStoreThreadFn()
|
|
{
|
|
while(true)
|
|
{
|
|
Thread.Sleep(persistThreadSleepTime);
|
|
PersistStore(ConstStrings.SSCS_SERVER_KEY_CHAIN_ID);
|
|
}
|
|
}
|
|
|
|
/* Persists the store to an xml file.
|
|
* TBD : Would we require any form of encoding?
|
|
*/
|
|
|
|
internal void PersistStore(string keyChainId)
|
|
{
|
|
string sPeristSecrets = null;
|
|
|
|
// is policy set to persist secrets
|
|
UIPol uiPolicy = (UIPol)ICASAPol.GetPolicy(CASAPolType.UI_POL, userStore.GetUserHomeDirectory());
|
|
if (uiPolicy != null)
|
|
{
|
|
sPeristSecrets = uiPolicy.GetConfigSetting(ConstStrings.CONFIG_PERSIST_SECRETS);
|
|
}
|
|
|
|
if ((sPeristSecrets != null) && (sPeristSecrets.Equals("0")))
|
|
{
|
|
// delete .miCASA file and .IV file
|
|
if ( keyChainId == ConstStrings.SSCS_SESSION_KEY_CHAIN_ID )
|
|
File.Delete(userStore.GetPersistenceFilePath());
|
|
else if ( keyChainId == ConstStrings.SSCS_SERVER_KEY_CHAIN_ID )
|
|
File.Delete(userStore.GetServerSecretsPersistenceFilePath());
|
|
return;
|
|
}
|
|
|
|
//userStore.DumpSecretstore();
|
|
try
|
|
{
|
|
MemoryStream ms1 = GetSecretsAsXMLStream(this.userStore, keyChainId);
|
|
byte[] key = null;
|
|
string fileName = null;
|
|
|
|
if ( keyChainId == ConstStrings.SSCS_SESSION_KEY_CHAIN_ID )
|
|
{
|
|
key = CASACrypto.GetKeySetFromFile(m_baGeneratedKey, userStore.GetKeyFilePath());
|
|
fileName = userStore.GetPersistenceFilePath();
|
|
}
|
|
else if ( keyChainId == ConstStrings.SSCS_SERVER_KEY_CHAIN_ID )
|
|
{
|
|
key = CASACrypto.GetKeySetFromFile(m_baGeneratedKey, userStore.GetServerKeyFilePath());
|
|
fileName = userStore.GetServerSecretsPersistenceFilePath();
|
|
}
|
|
|
|
string tempFile = fileName;
|
|
int count=0;
|
|
|
|
// rename existing file
|
|
if(File.Exists(fileName))
|
|
{
|
|
while(true)
|
|
{
|
|
if (File.Exists(tempFile+".tmp"))
|
|
{
|
|
if(IsOwnedByRoot(tempFile+".tmp"))
|
|
{
|
|
File.Delete(tempFile+".tmp");
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
count++;
|
|
tempFile = fileName + count.ToString();
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
File.Move(fileName, tempFile+".tmp");
|
|
}
|
|
|
|
CASACrypto.EncryptDataAndWriteToFile(ms1.ToArray(),key,fileName);
|
|
|
|
//remove temp
|
|
if(File.Exists(tempFile+".tmp"))
|
|
{
|
|
if(IsOwnedByRoot(tempFile+".tmp"))
|
|
File.Delete(tempFile+".tmp");
|
|
}
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
CSSSLogger.ExpLog(e.ToString());
|
|
}
|
|
}
|
|
|
|
internal static MemoryStream GetSecretsAsXMLStream(SecretStore userStore, string keyChainId)
|
|
{
|
|
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;
|
|
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);
|
|
|
|
if( ( keyChainId != null ) && ( keyChainId != sTmpId ))
|
|
continue;
|
|
|
|
writer.WriteStartElement(XmlConsts.keyChainNode);
|
|
writer.WriteAttributeString(XmlConsts.idAttr,sTmpId);
|
|
// If we need to store time
|
|
writer.WriteStartElement(XmlConsts.timeNode);
|
|
writer.WriteAttributeString(XmlConsts.createdTimeNode,kc.CreatedTime.Ticks.ToString());
|
|
writer.WriteAttributeString(XmlConsts.modifiedTimeNode,kc.ModifiedTime.Ticks.ToString());
|
|
writer.WriteEndElement();
|
|
|
|
PersistencePol policy = null;
|
|
|
|
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);
|
|
|
|
// TODO: Does Policy allow persisting this secret.
|
|
if (policy == null)
|
|
{
|
|
policy = (PersistencePol)ICASAPol.GetPolicy(CASAPolType.PERSISTENCE_POL, userStore.GetUserHomeDirectory());
|
|
}
|
|
|
|
bool bSaveValues = true;
|
|
if (policy != null)
|
|
{
|
|
if (policy.GetSecretPolicy(sTmpId, "Persistent", "True").Equals("False"))
|
|
{
|
|
//continue;
|
|
bSaveValues = false;
|
|
}
|
|
}
|
|
|
|
writer.WriteAttributeString(XmlConsts.idAttr,sTmpId);
|
|
// If we need to store time
|
|
writer.WriteStartElement(XmlConsts.timeNode);
|
|
writer.WriteAttributeString(XmlConsts.createdTimeNode,secret.CreatedTime.Ticks.ToString());
|
|
writer.WriteAttributeString("LazyTime",secret.CreatedTime.ToShortDateString());
|
|
writer.WriteAttributeString(XmlConsts.modifiedTimeNode,secret.ModifiedTime.Ticks.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);
|
|
|
|
if (bSaveValues)
|
|
writer.WriteString(value);
|
|
else
|
|
writer.WriteString("");
|
|
|
|
writer.WriteEndElement();
|
|
// If we need to store time
|
|
writer.WriteStartElement(XmlConsts.timeNode);
|
|
writer.WriteAttributeString(XmlConsts.createdTimeNode,(secret.GetKeyValueCreatedTime(sKey)).Ticks.ToString());
|
|
writer.WriteAttributeString(XmlConsts.modifiedTimeNode,(secret.GetKeyValueModifiedTime(sKey)).Ticks.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();
|
|
return ms1;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
}
|