/* KeePass Password Safe - The Open-Source Password Manager Copyright (C) 2003-2016 Dominik Reichl This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Text; using KeePassLib.Interfaces; using KeePassLib.Security; using KeePassLib.Utility; #if KeePassLibSD using KeePassLibSD; #endif namespace KeePassLib.Collections { /// /// A list of ProtectedString objects (dictionary). /// public sealed class ProtectedStringDictionary : IDeepCloneable, IEnumerable> { /*private SortedDictionary m_vStrings = new SortedDictionary();*/ private Dictionary m_vStrings = new Dictionary(); /// /// Get the number of strings in this entry. /// public uint UCount { get { return (uint)m_vStrings.Count; } } /// /// Construct a new list of protected strings. /// public ProtectedStringDictionary() { } IEnumerator IEnumerable.GetEnumerator() { return m_vStrings.GetEnumerator(); } public IEnumerator> GetEnumerator() { return m_vStrings.GetEnumerator(); } public void Clear() { m_vStrings.Clear(); } /// /// Clone the current ProtectedStringList object, including all /// stored protected strings. /// /// New ProtectedStringList object. public ProtectedStringDictionary CloneDeep() { ProtectedStringDictionary plNew = new ProtectedStringDictionary(); foreach(KeyValuePair kvpStr in m_vStrings) { // ProtectedString objects are immutable plNew.Set(kvpStr.Key, kvpStr.Value); } return plNew; } [Obsolete] public bool EqualsDictionary(ProtectedStringDictionary dict) { return EqualsDictionary(dict, PwCompareOptions.None, MemProtCmpMode.None); } [Obsolete] public bool EqualsDictionary(ProtectedStringDictionary dict, MemProtCmpMode mpCompare) { return EqualsDictionary(dict, PwCompareOptions.None, mpCompare); } public bool EqualsDictionary(ProtectedStringDictionary dict, PwCompareOptions pwOpt, MemProtCmpMode mpCompare) { if(dict == null) { Debug.Assert(false); return false; } bool bNeEqStd = ((pwOpt & PwCompareOptions.NullEmptyEquivStd) != PwCompareOptions.None); if(!bNeEqStd) { if(m_vStrings.Count != dict.m_vStrings.Count) return false; } foreach(KeyValuePair kvp in m_vStrings) { bool bStdField = PwDefs.IsStandardField(kvp.Key); ProtectedString ps = dict.Get(kvp.Key); if(bNeEqStd && (ps == null) && bStdField) ps = ProtectedString.Empty; if(ps == null) return false; if(mpCompare == MemProtCmpMode.Full) { if(ps.IsProtected != kvp.Value.IsProtected) return false; } else if(mpCompare == MemProtCmpMode.CustomOnly) { if(!bStdField && (ps.IsProtected != kvp.Value.IsProtected)) return false; } if(ps.ReadString() != kvp.Value.ReadString()) return false; } if(bNeEqStd) { foreach(KeyValuePair kvp in dict.m_vStrings) { ProtectedString ps = Get(kvp.Key); if(ps != null) continue; // Compared previously if(!PwDefs.IsStandardField(kvp.Key)) return false; if(!kvp.Value.IsEmpty) return false; } } return true; } /// /// Get one of the protected strings. /// /// String identifier. /// Protected string. If the string identified by /// cannot be found, the function /// returns null. /// Thrown if the input parameter /// is null. public ProtectedString Get(string strName) { Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName"); ProtectedString ps; if(m_vStrings.TryGetValue(strName, out ps)) return ps; return null; } /// /// Get one of the protected strings. The return value is never null. /// If the requested string cannot be found, an empty protected string /// object is returned. /// /// String identifier. /// Returns a protected string object. If the standard string /// has not been set yet, the return value is an empty string (""). /// Thrown if the input /// parameter is null. public ProtectedString GetSafe(string strName) { Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName"); ProtectedString ps; if(m_vStrings.TryGetValue(strName, out ps)) return ps; return ProtectedString.Empty; } /// /// Test if a named string exists. /// /// Name of the string to try. /// Returns true if the string exists, otherwise false. /// Thrown if /// is null. public bool Exists(string strName) { Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName"); return m_vStrings.ContainsKey(strName); } /// /// Get one of the protected strings. If the string doesn't exist, the /// return value is an empty string (""). /// /// Name of the requested string. /// Requested string value or an empty string, if the named /// string doesn't exist. /// Thrown if the input /// parameter is null. public string ReadSafe(string strName) { Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName"); ProtectedString ps; if(m_vStrings.TryGetValue(strName, out ps)) return ps.ReadString(); return string.Empty; } /// /// Get one of the entry strings. If the string doesn't exist, the /// return value is an empty string (""). If the string is /// in-memory protected, the return value is PwDefs.HiddenPassword. /// /// Name of the requested string. /// Returns the requested string in plain-text or /// PwDefs.HiddenPassword if the string cannot be found. /// Thrown if the input /// parameter is null. public string ReadSafeEx(string strName) { Debug.Assert(strName != null); if(strName == null) throw new ArgumentNullException("strName"); ProtectedString ps; if(m_vStrings.TryGetValue(strName, out ps)) { if(ps.IsProtected) return PwDefs.HiddenPassword; return ps.ReadString(); } return string.Empty; } /// /// Set a string. /// /// Identifier of the string field to modify. /// New value. This parameter must not be null. /// Thrown if one of the input /// parameters is null. public void Set(string strField, ProtectedString psNewValue) { Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField"); Debug.Assert(psNewValue != null); if(psNewValue == null) throw new ArgumentNullException("psNewValue"); m_vStrings[strField] = psNewValue; } /// /// Delete a string. /// /// Name of the string field to delete. /// Returns true if the field has been successfully /// removed, otherwise the return value is false. /// Thrown if the input /// parameter is null. public bool Remove(string strField) { Debug.Assert(strField != null); if(strField == null) throw new ArgumentNullException("strField"); return m_vStrings.Remove(strField); } public List GetKeys() { return new List(m_vStrings.Keys); } public void EnableProtection(string strField, bool bProtect) { ProtectedString ps = Get(strField); if(ps == null) return; // Nothing to do, no assert if(ps.IsProtected != bProtect) { byte[] pbData = ps.ReadUtf8(); Set(strField, new ProtectedString(bProtect, pbData)); if(bProtect) MemUtil.ZeroByteArray(pbData); } } } }