2006-01-19 00:34:21 +01:00
|
|
|
/***********************************************************************
|
|
|
|
*
|
2006-02-01 18:48:29 +01:00
|
|
|
* Copyright (C) 2005-2006 Novell, Inc. All Rights Reserved.
|
2006-01-19 00:34:21 +01:00
|
|
|
*
|
|
|
|
* 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
|
2006-01-31 23:01:47 +01:00
|
|
|
* Library Lesser General Public License for more details.
|
2006-01-19 00:34:21 +01:00
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2006-01-31 23:01:47 +01:00
|
|
|
* License along with this library; if not, Novell, Inc.
|
2006-01-19 00:34:21 +01:00
|
|
|
*
|
|
|
|
* To contact Novell about this file by physical or electronic mail,
|
|
|
|
* you may find current contact information at www.novell.com.
|
|
|
|
*
|
|
|
|
***********************************************************************/
|
|
|
|
|
2005-10-11 21:51:00 +02:00
|
|
|
using System;
|
|
|
|
using System.Collections;
|
2006-01-29 04:25:06 +01:00
|
|
|
using System.Collections.Specialized;
|
2005-10-11 21:51:00 +02:00
|
|
|
using System.Text;
|
|
|
|
using System.Threading;
|
|
|
|
using sscs.verbs;
|
|
|
|
using sscs.cache;
|
|
|
|
using sscs.common;
|
|
|
|
using sscs.constants;
|
2006-05-08 04:38:42 +02:00
|
|
|
using sscs.lss;
|
2005-10-11 21:51:00 +02:00
|
|
|
|
|
|
|
using System.Runtime.Serialization.Formatters.Binary;
|
|
|
|
using System.IO;
|
|
|
|
|
|
|
|
using Novell.CASA.MiCasa.Common;
|
|
|
|
using Novell.CASA.MiCasa.Communication;
|
|
|
|
|
|
|
|
namespace sscs.verbs
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There will be one instance existing for every call made by the client.
|
|
|
|
*/
|
|
|
|
|
|
|
|
internal class ObjectSerialization : SSVerb
|
|
|
|
{
|
|
|
|
|
|
|
|
private ushort msgId = 0;
|
|
|
|
private uint inMsgLen = 0;
|
|
|
|
|
|
|
|
private byte[] inBuf;
|
|
|
|
private byte[] outBuf;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This method sets the class member with the byte array received.
|
|
|
|
*/
|
|
|
|
|
|
|
|
public void SetMessageContent(byte[] ipcBytes)
|
|
|
|
{
|
|
|
|
CSSSLogger.ExecutionTrace(this);
|
|
|
|
inBuf = ipcBytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This method does the actual implementation of ReadSecret
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
public byte[] ProcessRequest(UserIdentifier userId)
|
|
|
|
{
|
|
|
|
CSSSLogger.ExecutionTrace(this);
|
|
|
|
|
|
|
|
/* If an exception occurs in message format decoding,
|
|
|
|
* it is handled by AppHandler
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Message Format decipher - Start
|
|
|
|
msgId = BitConverter.ToUInt16(inBuf,0);
|
|
|
|
inMsgLen = BitConverter.ToUInt32(inBuf,2);
|
|
|
|
|
2006-04-26 18:29:13 +02:00
|
|
|
// check inMsgLen
|
|
|
|
if ((inMsgLen < 6) || (inMsgLen > 65535))
|
|
|
|
{
|
|
|
|
throw new FormatException(" MsgLen invalid.");
|
|
|
|
}
|
2005-10-11 21:51:00 +02:00
|
|
|
|
|
|
|
// deserialize the data
|
|
|
|
BinaryFormatter formatter = new BinaryFormatter();
|
2006-04-26 18:29:13 +02:00
|
|
|
MemoryStream ms = new MemoryStream(inBuf, 6, (int)inMsgLen - 6);
|
2005-10-11 21:51:00 +02:00
|
|
|
|
|
|
|
WrappedObject request;
|
|
|
|
WrappedObject reply;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
request = (WrappedObject)formatter.Deserialize(ms);
|
|
|
|
reply = ProcessMessage(request, userId);
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
reply = new WrappedObject(-1, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Serialize the WrappedObject and send the reply
|
|
|
|
ms = new MemoryStream();
|
|
|
|
formatter.Serialize(ms, reply);
|
|
|
|
|
|
|
|
int msLen = (int)ms.Length;
|
|
|
|
outBuf = new byte[4+msLen];
|
|
|
|
byte[] t = new byte[10];
|
|
|
|
|
|
|
|
t = BitConverter.GetBytes(ms.Length);
|
|
|
|
Array.Copy(t,0,outBuf,0,4);
|
|
|
|
Array.Copy(ms.GetBuffer(), 0, outBuf, 4, msLen);
|
|
|
|
SessionManager.RemoveUserSession(userId, false);
|
|
|
|
return outBuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
internal WrappedObject ProcessMessage(WrappedObject wo, UserIdentifier userId)
|
|
|
|
{
|
2006-04-26 18:29:13 +02:00
|
|
|
|
2005-10-11 21:51:00 +02:00
|
|
|
SecretStore ssStore = SessionManager.CreateUserSession(userId);
|
|
|
|
|
2006-04-26 18:29:13 +02:00
|
|
|
try
|
|
|
|
{
|
|
|
|
int action = wo.GetAction();
|
|
|
|
switch (action)
|
|
|
|
{
|
|
|
|
case MiCasaRequestReply.VERB_PING_MICASAD:
|
2005-10-11 21:51:00 +02:00
|
|
|
{
|
2006-04-26 18:29:13 +02:00
|
|
|
return DoPing(wo);
|
|
|
|
}
|
2005-10-11 21:51:00 +02:00
|
|
|
|
2006-04-26 18:29:13 +02:00
|
|
|
case MiCasaRequestReply.VERB_SET_LINKED_KEY:
|
|
|
|
{
|
|
|
|
return DoSetLinkedKey(ssStore, wo);
|
|
|
|
}
|
2005-10-11 21:51:00 +02:00
|
|
|
|
2006-04-26 18:29:13 +02:00
|
|
|
case MiCasaRequestReply.VERB_REMOVE_LINKED_KEY:
|
|
|
|
{
|
|
|
|
return DoRemoveLinkedKey(ssStore, wo);
|
|
|
|
}
|
2005-10-11 21:51:00 +02:00
|
|
|
|
2006-04-26 18:29:13 +02:00
|
|
|
case MiCasaRequestReply.VERB_GET_LINKED_KEYS:
|
|
|
|
{
|
|
|
|
return DoGetLinkedKeys(ssStore, wo);
|
|
|
|
}
|
2005-10-11 21:51:00 +02:00
|
|
|
|
2006-04-26 18:29:13 +02:00
|
|
|
case MiCasaRequestReply.VERB_CREATE_TEST_SECRETS:
|
|
|
|
{
|
|
|
|
return DoCreateTestSecrets(ssStore, wo);
|
|
|
|
}
|
2005-10-11 21:51:00 +02:00
|
|
|
|
2006-04-26 18:29:13 +02:00
|
|
|
case MiCasaRequestReply.VERB_REMOVE_TEST_SECRETS:
|
|
|
|
{
|
|
|
|
return DoRemoveTestSecrets(ssStore, wo);
|
|
|
|
}
|
2005-10-11 21:51:00 +02:00
|
|
|
|
2006-04-26 18:29:13 +02:00
|
|
|
case MiCasaRequestReply.VERB_DUMP_LINKED_KEYS:
|
|
|
|
{
|
|
|
|
return DoDumpLinkedKeys(ssStore, wo);
|
|
|
|
}
|
2005-10-11 21:51:00 +02:00
|
|
|
|
2006-04-26 18:29:13 +02:00
|
|
|
case MiCasaRequestReply.VERB_WRITE_KEY:
|
|
|
|
{
|
|
|
|
return DoWriteKey(ssStore, wo);
|
|
|
|
}
|
2005-10-11 21:51:00 +02:00
|
|
|
|
2006-04-26 18:29:13 +02:00
|
|
|
case MiCasaRequestReply.VERB_LOCK_STORE:
|
|
|
|
{
|
|
|
|
ssStore.LockStore();
|
|
|
|
return wo;
|
|
|
|
}
|
2005-10-11 21:51:00 +02:00
|
|
|
|
2006-04-26 18:29:13 +02:00
|
|
|
case MiCasaRequestReply.VERB_UNLOCK_STORE:
|
|
|
|
{
|
|
|
|
return DoUnlockStore(ssStore, wo);
|
|
|
|
}
|
2005-10-11 21:51:00 +02:00
|
|
|
|
2006-04-26 18:29:13 +02:00
|
|
|
case MiCasaRequestReply.VERB_REMOVE_ALL_SECRETS:
|
|
|
|
{
|
|
|
|
// stop persistence
|
|
|
|
//ssStore.StopPersistence();
|
2005-10-11 21:51:00 +02:00
|
|
|
|
2006-04-26 18:29:13 +02:00
|
|
|
// remove secrets
|
|
|
|
return DoRemoveAllSecrets(ssStore, wo);
|
|
|
|
}
|
2005-10-11 21:51:00 +02:00
|
|
|
|
2006-04-26 18:29:13 +02:00
|
|
|
case MiCasaRequestReply.VERB_GET_STORE_STATUS:
|
|
|
|
{
|
|
|
|
wo.SetObject(ssStore.GetSecretStoreState());
|
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
case MiCasaRequestReply.VERB_REMOVE_KEY:
|
|
|
|
{
|
|
|
|
return DoRemoveKey(ssStore, wo);
|
|
|
|
}
|
|
|
|
case MiCasaRequestReply.VERB_READ_KEY:
|
|
|
|
{
|
|
|
|
return DoReadKey(ssStore, wo);
|
|
|
|
}
|
|
|
|
case MiCasaRequestReply.VERB_GET_KEY_LIST:
|
|
|
|
{
|
|
|
|
return DoGetKeyList(ssStore, wo);
|
|
|
|
}
|
|
|
|
case MiCasaRequestReply.VERB_RESET_MASTER_PASSWORD:
|
|
|
|
{
|
|
|
|
return DoResetMasterPassword(ssStore, wo);
|
|
|
|
}
|
|
|
|
case MiCasaRequestReply.VERB_GET_SECRETIDS:
|
|
|
|
{
|
|
|
|
return DoGetSecretIDs(ssStore, wo);
|
|
|
|
}
|
2006-05-08 04:38:42 +02:00
|
|
|
case MiCasaRequestReply.VERB_VALIDATE_DESKTOP_PWD:
|
|
|
|
{
|
|
|
|
return DoValidateDesktopPwd(ssStore, wo);
|
|
|
|
}
|
2005-11-03 21:22:45 +01:00
|
|
|
|
2006-04-26 18:29:13 +02:00
|
|
|
default:
|
|
|
|
{
|
|
|
|
wo.SetError(constants.RetCodes.FAILURE, "Verb Not Supported");
|
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
wo.SetError(constants.RetCodes.FAILURE, e.ToString());
|
|
|
|
}
|
2005-10-11 21:51:00 +02:00
|
|
|
|
|
|
|
|
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
|
2006-05-08 04:38:42 +02:00
|
|
|
private WrappedObject DoValidateDesktopPwd(SecretStore ssStore, WrappedObject wo)
|
|
|
|
{
|
|
|
|
// let's validate the Desktop pwd
|
|
|
|
|
|
|
|
String sDesktopPwd = (String)wo.GetObject();
|
|
|
|
bool bIsValid = ssStore.IsDesktopPassword(sDesktopPwd);
|
|
|
|
if (bIsValid)
|
|
|
|
{
|
|
|
|
wo.SetObject("true");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wo.SetObject("false");
|
|
|
|
}
|
|
|
|
|
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
|
2006-01-29 04:25:06 +01:00
|
|
|
private WrappedObject DoGetSecretIDs(SecretStore ssStore, WrappedObject wo)
|
|
|
|
{
|
|
|
|
if (!ssStore.IsStoreLocked())
|
|
|
|
{
|
|
|
|
// look up keychain
|
|
|
|
string sKeyChainID = wo.GetKeychainID();
|
|
|
|
if (sKeyChainID != null)
|
|
|
|
{
|
|
|
|
KeyChain kc = ssStore.GetKeyChain(sKeyChainID);
|
|
|
|
if (kc != null)
|
|
|
|
{
|
|
|
|
StringCollection sc = (StringCollection)wo.GetObject();
|
|
|
|
if (sc != null)
|
|
|
|
{
|
2006-04-26 18:29:13 +02:00
|
|
|
IDictionaryEnumerator etor = (IDictionaryEnumerator)kc.GetAllSecrets();
|
|
|
|
while(etor.MoveNext())
|
|
|
|
{
|
|
|
|
string sID = (string)etor.Key;
|
|
|
|
sID = sID.Substring(0, sID.Length - 1);
|
|
|
|
sc.Add(UnescapeID(sID));
|
2006-01-29 04:25:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wo.SetError(constants.RetCodes.FAILURE, "KeyChain not found");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wo.SetError(constants.RetCodes.FAILURE, "Store locked");
|
|
|
|
}
|
|
|
|
|
|
|
|
return wo;
|
|
|
|
|
|
|
|
}
|
2005-10-11 21:51:00 +02:00
|
|
|
|
|
|
|
private WrappedObject DoRemoveAllSecrets(SecretStore ssStore, WrappedObject wo)
|
|
|
|
{
|
2005-12-20 18:41:46 +01:00
|
|
|
|
|
|
|
if (!ssStore.IsStoreLocked())
|
|
|
|
{
|
|
|
|
string sKeyChainID = wo.GetKeychainID();
|
|
|
|
if (sKeyChainID != null)
|
|
|
|
{
|
|
|
|
KeyChain kc = ssStore.GetKeyChain(sKeyChainID);
|
|
|
|
kc.RemoveAllSecrets();
|
|
|
|
ssStore.UpdatePersistentStore();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2005-10-11 21:51:00 +02:00
|
|
|
{
|
2005-12-20 18:41:46 +01:00
|
|
|
wo.SetError(constants.RetCodes.FAILURE, "Store locked");
|
2005-10-11 21:51:00 +02:00
|
|
|
}
|
2005-10-27 21:24:13 +02:00
|
|
|
|
2005-10-11 21:51:00 +02:00
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
|
|
|
|
private WrappedObject DoUnlockStore(SecretStore ssStore, WrappedObject wo)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2005-10-13 20:22:04 +02:00
|
|
|
string sMasterPassword = (string)wo.GetObject();
|
|
|
|
ssStore.UnlockStore(null, sMasterPassword);
|
2005-10-11 21:51:00 +02:00
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
wo.SetError(constants.RetCodes.FAILURE, e.ToString());
|
|
|
|
}
|
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
private WrappedObject DoRemoveKey(SecretStore ssStore, WrappedObject wo)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
string keychainID = wo.GetKeychainID();
|
|
|
|
string secretID = wo.GetSecretID();
|
|
|
|
string keyID = wo.GetKeyID();
|
|
|
|
|
|
|
|
KeyChain keyChain = ssStore.GetKeyChain(keychainID);
|
|
|
|
Secret secret = null;
|
|
|
|
if( keyChain.CheckIfSecretExists(secretID) == false)
|
|
|
|
{
|
2006-04-26 18:29:13 +02:00
|
|
|
wo.SetError(constants.RetCodes.FAILURE,"Secret does not exist");
|
2005-10-11 21:51:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-10-21 01:57:26 +02:00
|
|
|
secret = keyChain.GetSecret(secretID);
|
2006-04-26 18:29:13 +02:00
|
|
|
secret.RemoveKeyValue(keyChain, keyID);
|
|
|
|
wo.SetError(constants.RetCodes.SUCCESS, null);
|
2005-10-27 21:24:13 +02:00
|
|
|
ssStore.UpdatePersistentStore();
|
2005-10-11 21:51:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
wo.SetError(constants.RetCodes.FAILURE, e.ToString());
|
|
|
|
}
|
|
|
|
|
|
|
|
return wo;
|
|
|
|
}
|
2005-10-21 01:57:26 +02:00
|
|
|
|
2005-10-11 21:51:00 +02:00
|
|
|
private WrappedObject DoReadKey(SecretStore ssStore, WrappedObject wo)
|
|
|
|
{
|
2005-11-24 00:27:29 +01:00
|
|
|
if (!ssStore.IsStoreLocked())
|
2005-10-11 21:51:00 +02:00
|
|
|
{
|
2005-11-24 00:27:29 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
string keychainID = wo.GetKeychainID();
|
|
|
|
string secretID = wo.GetSecretID();
|
|
|
|
string keyID = wo.GetKeyID();
|
2005-10-11 21:51:00 +02:00
|
|
|
|
|
|
|
|
2005-11-24 00:27:29 +01:00
|
|
|
// string sValue = (String)wo.GetObject();
|
2005-10-11 21:51:00 +02:00
|
|
|
|
2005-11-24 00:27:29 +01:00
|
|
|
KeyChain keyChain = ssStore.GetKeyChain(keychainID);
|
|
|
|
Secret secret = null;
|
|
|
|
if( keyChain.CheckIfSecretExists(secretID) == false)
|
|
|
|
{
|
|
|
|
wo.SetError(constants.RetCodes.FAILURE,"Secret does not exist");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
secret = keyChain.GetSecret(secretID);
|
|
|
|
KeyValue kv = secret.GetKeyValue(keyID);
|
|
|
|
string value = kv.GetValue();
|
|
|
|
wo.SetObject(value);
|
|
|
|
wo.SetError(constants.RetCodes.SUCCESS, null);
|
|
|
|
}
|
2005-10-11 21:51:00 +02:00
|
|
|
}
|
2005-11-24 00:27:29 +01:00
|
|
|
catch (Exception e)
|
2005-10-11 21:51:00 +02:00
|
|
|
{
|
2005-11-24 00:27:29 +01:00
|
|
|
wo.SetError(constants.RetCodes.FAILURE, e.ToString());
|
2005-10-11 21:51:00 +02:00
|
|
|
}
|
|
|
|
}
|
2005-11-24 00:27:29 +01:00
|
|
|
else
|
|
|
|
wo.SetError(constants.RetCodes.FAILURE, "Store locked");
|
2005-10-11 21:51:00 +02:00
|
|
|
|
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
private WrappedObject DoGetKeyList(SecretStore ssStore, WrappedObject wo)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
string keychainID = wo.GetKeychainID();
|
|
|
|
string secretID = wo.GetSecretID();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
KeyChain keyChain = ssStore.GetKeyChain(keychainID);
|
|
|
|
Secret secret = null;
|
|
|
|
if( keyChain.CheckIfSecretExists(secretID) == false)
|
|
|
|
{
|
2006-04-26 18:29:13 +02:00
|
|
|
wo.SetError(constants.RetCodes.FAILURE,"Secret does not exist");
|
2005-10-11 21:51:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
secret = keyChain.GetSecret(secretID);
|
2006-04-26 18:29:13 +02:00
|
|
|
if( null != secret )
|
|
|
|
{
|
|
|
|
ArrayList keyList = secret.GetKeyList();
|
|
|
|
wo.SetObject(keyList);
|
|
|
|
wo.SetError(constants.RetCodes.SUCCESS, null);
|
|
|
|
}
|
2005-10-11 21:51:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
2006-04-26 18:29:13 +02:00
|
|
|
wo.SetError(constants.RetCodes.FAILURE, e.ToString());
|
2005-10-11 21:51:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
|
|
|
|
private WrappedObject DoWriteKey(SecretStore ssStore, WrappedObject wo)
|
|
|
|
{
|
2005-11-24 00:27:29 +01:00
|
|
|
if (!ssStore.IsStoreLocked())
|
2005-10-11 21:51:00 +02:00
|
|
|
{
|
2005-11-24 00:27:29 +01:00
|
|
|
try
|
2005-10-11 21:51:00 +02:00
|
|
|
{
|
2005-11-24 00:27:29 +01:00
|
|
|
string keychainID = wo.GetKeychainID();
|
|
|
|
string secretID = wo.GetSecretID();
|
|
|
|
string keyID = wo.GetKeyID();
|
|
|
|
string sValue = (String)wo.GetObject();
|
|
|
|
|
|
|
|
if (secretID.IndexOf("*") < 0)
|
2005-10-26 16:31:58 +02:00
|
|
|
{
|
2005-11-24 00:27:29 +01:00
|
|
|
|
|
|
|
KeyChain keyChain = ssStore.GetKeyChain(keychainID);
|
|
|
|
Secret secret;
|
|
|
|
if( keyChain.CheckIfSecretExists(secretID) == false)
|
|
|
|
{
|
|
|
|
secret = new Secret(secretID);
|
|
|
|
keyChain.AddSecret(secret);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
secret = keyChain.GetSecret(secretID);
|
|
|
|
}
|
|
|
|
secret.SetKeyValue(keyID, sValue);
|
|
|
|
|
|
|
|
ChangeLinkedKeys(keyChain, secret, keyID, sValue);
|
|
|
|
wo.SetError(constants.RetCodes.SUCCESS, null);
|
|
|
|
ssStore.UpdatePersistentStore();
|
2005-10-26 16:31:58 +02:00
|
|
|
}
|
|
|
|
else
|
2005-11-24 00:27:29 +01:00
|
|
|
wo.SetError(constants.RetCodes.FAILURE, null);
|
2005-10-11 21:51:00 +02:00
|
|
|
}
|
2005-11-24 00:27:29 +01:00
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
wo.SetError(constants.RetCodes.FAILURE, e.ToString());
|
|
|
|
}
|
|
|
|
|
2005-10-11 21:51:00 +02:00
|
|
|
}
|
2005-11-24 00:27:29 +01:00
|
|
|
else
|
2006-04-26 18:29:13 +02:00
|
|
|
wo.SetError(constants.RetCodes.FAILURE, "Store locked");
|
2005-10-11 21:51:00 +02:00
|
|
|
|
|
|
|
return wo;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private void ChangeLinkedKeys(KeyChain keyChain, Secret secret, string key, string valStr)
|
|
|
|
{
|
|
|
|
Hashtable htLinkedkeys = secret.GetLinkedKeys(key);
|
|
|
|
if (htLinkedkeys != null)
|
|
|
|
{
|
|
|
|
// enumerate the hashtable, getting each secret/key and change it's value
|
|
|
|
ICollection coll = htLinkedkeys.Values;
|
|
|
|
IDictionaryEnumerator ienum = (IDictionaryEnumerator)coll.GetEnumerator();
|
|
|
|
|
|
|
|
LinkedKeyInfo linkedInfo; // = (LinkedKeyInfo)ienum.Current;
|
|
|
|
|
|
|
|
while (ienum.MoveNext())
|
|
|
|
{
|
|
|
|
linkedInfo = (LinkedKeyInfo)ienum.Value;
|
|
|
|
|
|
|
|
// Get the target Secret
|
|
|
|
Secret targetSecret = keyChain.GetSecret(linkedInfo.GetLinkedSecretID());
|
|
|
|
if (targetSecret != null)
|
|
|
|
{
|
|
|
|
// get target key value
|
2006-01-28 19:47:48 +01:00
|
|
|
string targetKey = linkedInfo.GetLinkedKeyID();
|
|
|
|
string targetkv = targetSecret.GetKeyValue(targetKey).GetValue();
|
2005-10-11 21:51:00 +02:00
|
|
|
|
|
|
|
// if a change is required in the target, then call this method recursively using the TargetSecret
|
|
|
|
if (!targetkv.Equals(valStr))
|
|
|
|
{
|
|
|
|
// NOTE: ORDER IS IMPORTANT
|
|
|
|
// first change this one
|
|
|
|
targetSecret.SetKeyValue(linkedInfo.GetLinkedKeyID(), valStr);
|
|
|
|
|
|
|
|
// now call the traget to change it's linked ones
|
2006-01-28 19:47:48 +01:00
|
|
|
ChangeLinkedKeys(keyChain, targetSecret, targetKey, valStr);
|
2005-10-27 21:24:13 +02:00
|
|
|
|
2005-10-11 21:51:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private WrappedObject DoDumpLinkedKeys(SecretStore ssStore, WrappedObject wo)
|
|
|
|
{
|
|
|
|
// create 2 secrets
|
|
|
|
string keychainID = wo.GetKeychainID();
|
|
|
|
KeyChain keyChain;
|
|
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("\r\n*********Dumping Linked Keys");
|
|
|
|
try
|
|
|
|
{
|
|
|
|
keyChain = ssStore.GetKeyChain(keychainID);
|
|
|
|
}
|
|
|
|
catch (Exception)
|
|
|
|
{
|
|
|
|
keyChain = new KeyChain(keychainID);
|
|
|
|
ssStore.AddKeyChain(keyChain);
|
|
|
|
}
|
|
|
|
|
|
|
|
// enumerate all secrets
|
|
|
|
IDictionaryEnumerator ienum = (IDictionaryEnumerator)keyChain.GetAllSecrets();
|
|
|
|
ienum.Reset();
|
|
|
|
while (ienum.MoveNext())
|
|
|
|
{
|
|
|
|
string secretID = (string)ienum.Key;
|
|
|
|
Secret secret = keyChain.GetSecret(secretID);
|
|
|
|
IDictionaryEnumerator idenum = secret.GetKeyValueEnumerator();
|
|
|
|
idenum.Reset();
|
|
|
|
while (idenum.MoveNext())
|
|
|
|
{
|
|
|
|
KeyValue kv = (KeyValue)idenum.Value;
|
|
|
|
Hashtable htLKI = kv.GetLinkedKeys();
|
|
|
|
|
|
|
|
if (htLKI != null)
|
|
|
|
{
|
|
|
|
Console.WriteLine(secret.GetKey() + ":" + kv.Key + " is Linked to");
|
|
|
|
IDictionaryEnumerator htienum = (IDictionaryEnumerator)htLKI.GetEnumerator();
|
|
|
|
htienum.Reset();
|
|
|
|
while (htienum.MoveNext())
|
|
|
|
{
|
|
|
|
LinkedKeyInfo lki = (LinkedKeyInfo) htienum.Value;
|
|
|
|
Console.WriteLine(" " + lki.GetLinkID());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return wo;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string GW = "SS_CredSet:TestGroupwise\0";
|
|
|
|
string IFOLDER = "SS_CredSet:TestiFolder\0";
|
|
|
|
string GWIM = "SS_CredSet:TestGWIM\0";
|
|
|
|
|
|
|
|
private WrappedObject DoRemoveTestSecrets(SecretStore ssStore, WrappedObject wo)
|
|
|
|
{
|
|
|
|
|
|
|
|
string keychainID = wo.GetKeychainID();
|
|
|
|
KeyChain keyChain;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
keyChain = ssStore.GetKeyChain(keychainID);
|
|
|
|
}
|
|
|
|
catch (Exception)
|
|
|
|
{
|
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
keyChain.RemoveSecret(GW);
|
|
|
|
}
|
|
|
|
catch (Exception)
|
|
|
|
{}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
keyChain.RemoveSecret(GWIM);
|
|
|
|
}
|
|
|
|
catch (Exception)
|
|
|
|
{}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
keyChain.RemoveSecret(IFOLDER);
|
|
|
|
}
|
|
|
|
catch (Exception)
|
|
|
|
{}
|
|
|
|
|
|
|
|
return wo;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private WrappedObject DoCreateTestSecrets(SecretStore ssStore, WrappedObject wo)
|
|
|
|
{
|
|
|
|
// create 2 secrets
|
|
|
|
string keychainID = wo.GetKeychainID();
|
|
|
|
KeyChain keyChain;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
keyChain = ssStore.GetKeyChain(keychainID);
|
|
|
|
}
|
|
|
|
catch (Exception)
|
|
|
|
{
|
|
|
|
keyChain = new KeyChain(keychainID);
|
|
|
|
ssStore.AddKeyChain(keyChain);
|
|
|
|
}
|
|
|
|
|
2005-10-21 01:57:26 +02:00
|
|
|
Secret secret1 = GetOrCreateSecret(keyChain, GW);
|
2005-10-11 21:51:00 +02:00
|
|
|
secret1.SetKeyValue("CN", "User1");
|
|
|
|
secret1.SetKeyValue("Password", "GWPass");
|
|
|
|
try
|
|
|
|
{
|
|
|
|
keyChain.AddSecret(secret1);
|
|
|
|
}
|
|
|
|
catch (Exception)
|
|
|
|
{}
|
|
|
|
|
2005-10-21 01:57:26 +02:00
|
|
|
Secret secret2 = GetOrCreateSecret(keyChain, IFOLDER);
|
2005-10-11 21:51:00 +02:00
|
|
|
secret2.SetKeyValue("CN", "User2");
|
|
|
|
secret2.SetKeyValue("Password", "IfolderPass");
|
|
|
|
secret2.SetKeyValue("EmployeeID", "123456");
|
|
|
|
try
|
|
|
|
{
|
|
|
|
keyChain.AddSecret(secret2);
|
|
|
|
}
|
|
|
|
catch (Exception)
|
|
|
|
{}
|
|
|
|
|
2005-10-21 01:57:26 +02:00
|
|
|
Secret secret3 = GetOrCreateSecret(keyChain,GWIM);
|
2005-10-11 21:51:00 +02:00
|
|
|
secret3.SetKeyValue("CN", "User3");
|
|
|
|
secret3.SetKeyValue("Password", "GwimPass");
|
|
|
|
try
|
|
|
|
{
|
|
|
|
keyChain.AddSecret(secret3);
|
|
|
|
}
|
|
|
|
catch (Exception)
|
|
|
|
{}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// link password fields
|
|
|
|
KeyValue kv = secret1.GetKeyValue("Password");
|
|
|
|
KeyValue kv2 = secret2.GetKeyValue("Password");
|
|
|
|
KeyValue kv3 = secret3.GetKeyValue("Password");
|
|
|
|
|
|
|
|
kv.AddLink(new LinkedKeyInfo(IFOLDER, "Password"));
|
|
|
|
kv2.AddLink(new LinkedKeyInfo(GW, "Password"));
|
|
|
|
|
|
|
|
kv2.AddLink(new LinkedKeyInfo(GWIM, "Password"));
|
|
|
|
kv3.AddLink(new LinkedKeyInfo(IFOLDER, "Password"));
|
|
|
|
|
|
|
|
kv3.AddLink(new LinkedKeyInfo(GW, "Password"));
|
|
|
|
kv.AddLink(new LinkedKeyInfo(GWIM, "Password"));
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
2005-10-21 02:44:48 +02:00
|
|
|
//Console.WriteLine(e.ToString());
|
2005-10-11 21:51:00 +02:00
|
|
|
}
|
|
|
|
|
2005-10-21 02:44:48 +02:00
|
|
|
#if DEBUG
|
2005-10-11 21:51:00 +02:00
|
|
|
DoDumpLinkedKeys(ssStore, wo);
|
2005-10-21 02:44:48 +02:00
|
|
|
#endif
|
2005-10-11 21:51:00 +02:00
|
|
|
|
|
|
|
return wo;
|
|
|
|
}
|
2005-10-21 01:57:26 +02:00
|
|
|
|
|
|
|
private Secret GetOrCreateSecret(KeyChain kc, string sID)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
return kc.GetSecret(sID);
|
|
|
|
}
|
|
|
|
catch (Exception)
|
|
|
|
{
|
|
|
|
return new Secret(sID);
|
|
|
|
}
|
|
|
|
}
|
2005-10-11 21:51:00 +02:00
|
|
|
|
|
|
|
|
|
|
|
private WrappedObject DoGetLinkedKeys(SecretStore ssStore, WrappedObject wo)
|
|
|
|
{
|
|
|
|
string keychainID = wo.GetKeychainID();
|
|
|
|
string secretID = wo.GetSecretID();
|
|
|
|
string keyID = wo.GetKeyID();
|
|
|
|
|
|
|
|
KeyChain keyChain = ssStore.GetKeyChain(keychainID);
|
|
|
|
Secret secret = keyChain.GetSecret(secretID);
|
|
|
|
KeyValue kv = secret.GetKeyValue(keyID);
|
|
|
|
|
|
|
|
wo.SetObject(kv.GetLinkedKeys());
|
|
|
|
wo.SetError(constants.RetCodes.SUCCESS, null);
|
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
|
|
|
|
private WrappedObject DoRemoveLinkedKey(SecretStore ssStore, WrappedObject wo)
|
|
|
|
{
|
|
|
|
|
|
|
|
string keychainID = wo.GetKeychainID();
|
|
|
|
string secretID = wo.GetSecretID();
|
|
|
|
string keyID = wo.GetKeyID();
|
|
|
|
|
|
|
|
KeyChain keyChain = ssStore.GetKeyChain(keychainID);
|
|
|
|
Secret secret = keyChain.GetSecret(secretID);
|
|
|
|
KeyValue kv = secret.GetKeyValue(keyID);
|
|
|
|
|
|
|
|
LinkedKeyInfo lki = (LinkedKeyInfo)wo.GetObject();
|
|
|
|
kv.RemoveLink(lki.GetLinkID());
|
|
|
|
|
|
|
|
// remove reverse link as well
|
|
|
|
Secret targetSecret = keyChain.GetSecret(lki.GetLinkedSecretID());
|
|
|
|
KeyValue targetkv = targetSecret.GetKeyValue(lki.GetLinkedKeyID());
|
|
|
|
targetkv.RemoveLink(secretID+":"+keyID);
|
2005-10-27 21:24:13 +02:00
|
|
|
ssStore.UpdatePersistentStore();
|
2005-10-11 21:51:00 +02:00
|
|
|
|
|
|
|
return wo;
|
|
|
|
}
|
2005-11-24 00:27:29 +01:00
|
|
|
|
2005-10-11 21:51:00 +02:00
|
|
|
private WrappedObject DoSetLinkedKey(SecretStore ssStore, WrappedObject wo)
|
|
|
|
{
|
|
|
|
|
|
|
|
string keychainID = wo.GetKeychainID();
|
|
|
|
string secretID = wo.GetSecretID();
|
|
|
|
string keyID = wo.GetKeyID();
|
|
|
|
|
|
|
|
KeyChain keyChain = ssStore.GetKeyChain(keychainID);
|
|
|
|
Secret secret = keyChain.GetSecret(secretID);
|
|
|
|
KeyValue kv = secret.GetKeyValue(keyID);
|
|
|
|
|
|
|
|
LinkedKeyInfo lki = (LinkedKeyInfo)wo.GetObject();
|
|
|
|
kv.AddLink(lki);
|
|
|
|
|
|
|
|
// add reverse link
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Secret target = keyChain.GetSecret(lki.GetLinkedSecretID());
|
|
|
|
KeyValue targetkv = target.GetKeyValue(lki.GetLinkedKeyID());
|
2006-02-17 22:05:10 +01:00
|
|
|
targetkv.AddLink(new LinkedKeyInfo(secretID, keyID, true));
|
2005-10-27 21:24:13 +02:00
|
|
|
ssStore.UpdatePersistentStore();
|
2005-10-11 21:51:00 +02:00
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
2006-02-17 22:05:10 +01:00
|
|
|
//Console.WriteLine("Reverse Link error: " + e.ToString());
|
2005-10-11 21:51:00 +02:00
|
|
|
wo.SetError(constants.RetCodes.FAILURE, "Reverse Link: " + e.ToString());
|
|
|
|
}
|
|
|
|
|
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
|
2005-11-03 21:22:45 +01:00
|
|
|
|
|
|
|
private WrappedObject DoResetMasterPassword(SecretStore ssStore, WrappedObject wo)
|
|
|
|
{
|
|
|
|
ResetMasterPassword rmp = (ResetMasterPassword)wo.GetObject();
|
|
|
|
// verify current master password
|
|
|
|
try
|
|
|
|
{
|
|
|
|
string sMasterPassword = rmp.m_currentPassword;
|
|
|
|
ssStore.UnlockStore(null, sMasterPassword);
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
wo.SetError(constants.RetCodes.FAILURE, e.ToString());
|
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
|
|
|
|
// change master master password
|
|
|
|
string sNewPassword = rmp.m_newPassword;
|
|
|
|
if (sNewPassword == null || sNewPassword.Length < 8)
|
|
|
|
{
|
|
|
|
wo.SetError(constants.RetCodes.FAILURE, null);
|
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ssStore.ChangeMasterPassword(rmp.m_currentPassword, rmp.m_newPassword))
|
|
|
|
wo.SetError(constants.RetCodes.FAILURE, null);
|
|
|
|
else
|
|
|
|
wo.SetError(constants.RetCodes.SUCCESS, null);
|
|
|
|
|
|
|
|
|
|
|
|
return wo;
|
|
|
|
}
|
2005-11-24 00:27:29 +01:00
|
|
|
|
2005-10-11 21:51:00 +02:00
|
|
|
private WrappedObject DoPing(WrappedObject wo)
|
|
|
|
{
|
2005-10-31 21:21:32 +01:00
|
|
|
//Console.WriteLine("MICASAD received Ping from Client");
|
2005-10-11 21:51:00 +02:00
|
|
|
wo.SetError(IPCRetCodes.SSCS_REPLY_SUCCESS, null);
|
|
|
|
|
|
|
|
Ping ping = (Ping)wo.GetObject();
|
|
|
|
ping.servermessage = "MICASAD echos client message: <" + ping.clientmessage + ">";
|
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
internal WrappedObject ProcessMessage(WrappedObject wo, UserIdentifier userId)
|
|
|
|
{
|
|
|
|
|
|
|
|
Console.WriteLine("ObjectSerialization Called");
|
|
|
|
SecretStore ssStore = SessionManager.CreateUserSession(userId);
|
|
|
|
Secret secret;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
keyChainId = wo.GetKeychainID();
|
|
|
|
secretId = wo.GetSecretID();
|
|
|
|
keyId = wo.GetKeyID();
|
|
|
|
|
|
|
|
int action = wo.GetAction();
|
|
|
|
|
|
|
|
if (action == WrappedObject.VERB_PING_MICASAD)
|
|
|
|
{
|
|
|
|
Console.WriteLine("MICASAD received Ping from Client");
|
|
|
|
wo.SetError(IPCRetCodes.SSCS_REPLY_SUCCESS, null);
|
|
|
|
|
|
|
|
Ping ping = (Ping)wo.GetObject();
|
|
|
|
ping.servermessage = "MICASAD echos client message: <" + ping.clientmessage + ">";
|
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (action <= WrappedObject.VERB_REMOVE_LINKED_KEY)
|
|
|
|
{
|
|
|
|
|
|
|
|
wo.SetError(IPCRetCodes.SSCS_REPLY_SUCCESS, null);
|
|
|
|
if (action == WrappedObject.VERB_GET_STORE)
|
|
|
|
{
|
|
|
|
wo.SetObject(ssStore);
|
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
|
|
|
|
// look up Keychain
|
|
|
|
KeyChain keyChain = new KeyChain(keyChainId);
|
|
|
|
|
|
|
|
// temporary
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (true)
|
|
|
|
ssStore.AddKeyChain(keyChain);
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ssStore.CheckIfKeyChainExists(keyChainId) )
|
|
|
|
{
|
|
|
|
keyChain = ssStore.GetKeyChain(keyChainId);
|
|
|
|
if (action == WrappedObject.VERB_GET_KEYCHAIN)
|
|
|
|
{
|
|
|
|
wo.SetObject(keyChain);
|
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
|
|
|
|
if((action == WrappedObject.VERB_GET_SECRET) && (keyChain.CheckIfSecretExists(secretId) == false))
|
|
|
|
{
|
|
|
|
wo.SetError(IPCRetCodes.SSCS_E_SECRETID_DOES_NOT_EXIST, null);
|
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
secret = keyChain.GetSecret(secretId);
|
|
|
|
|
|
|
|
if (action == WrappedObject.VERB_GET_SECRET)
|
|
|
|
{
|
|
|
|
wo.SetObject(secret);
|
|
|
|
return wo;
|
|
|
|
}
|
|
|
|
else if ((action == WrappedObject.VERB_SET_SECRET) && (null != wo.GetObject()))
|
|
|
|
{
|
|
|
|
keyChain.AddSecret((Secret)wo.GetObject());
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (action == WrappedObject.VERB_SET_LINKED_KEY)
|
|
|
|
{
|
|
|
|
KeyValue kv = secret.GetKeyValue(keyId);
|
|
|
|
LinkedKeyInfo lki = (LinkedKeyInfo)wo.GetObject();
|
|
|
|
kv.AddLink(lki);
|
|
|
|
|
|
|
|
// add reverse link
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Secret target = keyChain.GetSecret(lki.GetLinkedSecretID());
|
|
|
|
KeyValue targetkv = target.GetKeyValue(lki.GetLinkedKeyID());
|
|
|
|
targetkv.AddLink(new LinkedKeyInfo(secretId, keyId));
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Console.WriteLine("Reverse Link error: " + e.ToString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wo.SetError(IPCRetCodes.SSCS_E_KEYCHAIN_DOES_NOT_EXIST, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wo.SetError(IPCRetCodes.SSCS_E_SYSTEM_ERROR, "Action not supported");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
String error = e.ToString();
|
|
|
|
Console.WriteLine(error);
|
|
|
|
wo = new WrappedObject(constants.RetCodes.FAILURE, error);
|
|
|
|
}
|
|
|
|
return wo;
|
|
|
|
}
|
2006-04-26 18:29:13 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
private static string UnescapeID(string sOrig)
|
|
|
|
{
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
for (int i = 0; i < sOrig.Length; i++)
|
|
|
|
{
|
|
|
|
if (sOrig[i] == ('\\'))
|
|
|
|
{
|
|
|
|
if (i + 1 < sOrig.Length)
|
|
|
|
{
|
|
|
|
if (sOrig[i + 1] == (':')
|
|
|
|
|| sOrig[i + 1] == ('\\')
|
|
|
|
|| sOrig[i + 1] == ('='))
|
|
|
|
{
|
|
|
|
sb.Append(sOrig[i + 1]);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
sb.Append(sOrig[i]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
sb.Append(sOrig[i]);
|
|
|
|
}
|
|
|
|
return sb.ToString();
|
|
|
|
}
|
2005-10-11 21:51:00 +02:00
|
|
|
public string GetVerbName()
|
|
|
|
{
|
|
|
|
CSSSLogger.ExecutionTrace(this);
|
|
|
|
return this.ToString();
|
|
|
|
}
|
2005-10-26 16:31:58 +02:00
|
|
|
|
2005-10-11 21:51:00 +02:00
|
|
|
}
|
|
|
|
}
|