Files
duplicati/Duplicati/Winforms/Controls/PasswordControl.cs
T
kenneth.skovhede@gmail.com effdba0558 Update issue #134
Status: Fixed

I have now implemented a password protection control.
You can now only view passwords when you enter them.
After this, you may reset the passwords, by entering a
new one, but you cannot read the existing passwords.

To protect against people reading the database,
which contains the passwords in clear text, the
database is now encrypted by using the standard 
SQLite encryption method. This is different
than what I proposed, but is a much stronger
protection than merely scrambling the passwords,
and also protects all information (servername, etc).

It works by setting an environment variable
called DUPLICATI_DB_KEY. When no password
is set, Duplicati will use the key "Duplicati_Key_42",
ensuring that all databases are encrypted by default.

Using a pre-defined publicly known key is obviously
not a strong protection, but it protects from casual
users attempting to browse the database, and it also
protects from string scanners that scan a harddisk
for possible passwords.

On the first startup, Duplicati will encrypt the database,
using the mentioned key. You may want to delete any backup
databases that are placed in the same folder as the database.

Should you wish to decrypt the database, you can start 
Duplicati.exe with the commandline option "--unencrypted-database".
This will use the current key to decrypt the database, and save it
without encryption.

Should you wish to change the database password, simply decrypt the
database, then change the password via the environment variable,
and start Duplicati.exe as normal.

The SQLite documentation for the feature is here:
http://www.hwaci.com/sw/sqlite/see.html

Unfortunately, it does not seem as if any
of the free SQLite tools supports this.

The SQLite library found on Ubuntu
does not support encryption, so I have disabled 
this feature by default on Linux.

If a user has a special compiled version
of SQLite, they may use the environment
variables as mentioned above.

I will write this information into a wiki
page as soon asap.

git-svn-id: https://duplicati.googlecode.com/svn/trunk@498 59da171f-624f-0410-aa54-27559c288bec
2010-09-07 21:39:22 +00:00

219 lines
7.2 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
namespace Duplicati.Winforms.Controls
{
/// <summary>
/// This class implements the logic for a control that displays a hidden password.
/// It allows the user to toggle the password visibility, to prevent typing errors,
/// and can also prevent the underlying password from being revealed, while still
/// allowing the user to enter a new password.
/// </summary>
public partial class PasswordControl : UserControl
{
/// <summary>
/// The internal password storage
/// </summary>
private string m_password;
/// <summary>
/// A control field used to distinguish the code generated events from the user generated events
/// </summary>
private bool m_isUpdating = false;
/// <summary>
/// A variable that controls if the user is asked to clear the password
/// </summary>
private bool m_askToEnterNewPassword = false;
/// <summary>
/// An event that is raised when the password changed
/// </summary>
[Browsable(true)]
public new event EventHandler TextChanged;
/// <summary>
/// Simple delegate with no arguments, used to invoke the update function
/// </summary>
private delegate void EmptyDelegate();
/// <summary>
/// Constructs a new PasswordControl
/// </summary>
public PasswordControl()
{
InitializeComponent();
}
/// <summary>
/// The password that this control protects
/// </summary>
public override string Text
{
get { return m_password; }
set
{
bool changed = m_password != value;
m_password = value;
UpdateDisplay();
if (changed && TextChanged != null)
TextChanged(this, new EventArgs());
}
}
/// <summary>
/// A value that determines if the user can only clear the password,
/// and not reveal the original password
/// </summary>
public bool AskToEnterNewPassword
{
get { return m_askToEnterNewPassword; }
set
{
m_askToEnterNewPassword = value;
UpdateDisplay();
}
}
/// <summary>
/// Gets or sets a value that indicates if the password is visible to the user
/// </summary>
public bool IsPasswordVisible
{
get { return ShowPassword.Checked; }
set { ShowPassword.Checked = value; }
}
/// <summary>
/// Internal helper function that updates the user interface
/// </summary>
private void UpdateDisplay()
{
try
{
m_isUpdating = true;
TextBox.UseSystemPasswordChar = !ShowPassword.Checked;
if (TextBox.Focused)
TextBox.Text = m_askToEnterNewPassword ? "" : m_password;
else
TextBox.Text = m_askToEnterNewPassword ? "password" : m_password;
TextBox.SelectionStart = TextBox.Text.Length;
TextBox.SelectionLength = 0;
}
finally
{
m_isUpdating = false;
}
}
/// <summary>
/// Event handler for the TextChanged event
/// </summary>
/// <param name="sender">Unused sender argument</param>
/// <param name="e">Unused event argument</param>
private void TextBox_TextChanged(object sender, EventArgs e)
{
if (m_isUpdating)
return;
if (m_askToEnterNewPassword)
{
if (MessageBox.Show(this, Strings.PasswordControl.ConfirmClearingPassword, Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != DialogResult.Yes)
{
try
{
m_isUpdating = true;
TextBox.Text = "";
BeginInvoke(new EmptyDelegate(OutOfOrderFocus));
return;
}
finally
{
m_isUpdating = false;
}
}
}
m_password = TextBox.Text;
m_askToEnterNewPassword = false;
if (TextChanged != null)
TextChanged(this, e);
}
/// <summary>
/// A helper for providing out-of-order focusing
/// </summary>
private void OutOfOrderFocus()
{
try { ShowPassword.Focus(); }
catch { }
}
/// <summary>
/// Event handler for the CheckedChanged event
/// </summary>
/// <param name="sender">Unused sender argument</param>
/// <param name="e">Unused event argument</param>
private void ShowPassword_CheckedChanged(object sender, EventArgs e)
{
if (ShowPassword.Checked && m_askToEnterNewPassword)
{
if (MessageBox.Show(this, Strings.PasswordControl.ConfirmClearingPassword, Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != DialogResult.Yes)
{
ShowPassword.Checked = false;
BeginInvoke(new EmptyDelegate(OutOfOrderFocus));
return;
}
else
{
m_password = "";
m_askToEnterNewPassword = false;
}
}
UpdateDisplay();
}
/// <summary>
/// Event handler for the Enter event
/// </summary>
/// <param name="sender">Unused sender argument</param>
/// <param name="e">Unused event argument</param>
private void TextBox_Enter(object sender, EventArgs e)
{
this.BeginInvoke(new EmptyDelegate(UpdateDisplay));
}
/// <summary>
/// Event handler for the Leave event
/// </summary>
/// <param name="sender">Unused sender argument</param>
/// <param name="e">Unused event argument</param>
private void TextBox_Leave(object sender, EventArgs e)
{
this.BeginInvoke(new EmptyDelegate(UpdateDisplay));
}
/// <summary>
/// Event handler for the checkbox click event
/// </summary>
/// <param name="sender">Unused sender argument</param>
/// <param name="e">Unused event argument</param>
private void ShowPassword_Click(object sender, EventArgs e)
{
try { TextBox.Focus(); }
catch { }
}
}
}