File:  [truecrypt] / truecrypt / main / textuserinterface.cpp
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 16:49:09 2018 UTC (8 years, 3 months ago) by root
Branches: drWhax, MAIN
CVS tags: v5_0a, v5_0, HEAD
5.0

/*
 Copyright (c) 2008 TrueCrypt Foundation. All rights reserved.

 Governed by the TrueCrypt License 2.4 the full text of which is contained
 in the file License.txt included in TrueCrypt binary and source code
 distribution packages.
*/

#include "System.h"
#ifdef TC_UNIX
#include <signal.h>
#include <termios.h>
#include <unistd.h>
#endif

#include "Application.h"
#include "TextUserInterface.h"

namespace TrueCrypt
{
	TextUserInterface::TextUserInterface ()
	{
#ifdef TC_UNIX
		signal (SIGHUP, OnSignal);
		signal (SIGINT, OnSignal);
		signal (SIGQUIT, OnSignal);
		signal (SIGTERM, OnSignal);
#endif

		FInputStream.reset (new wxFFileInputStream (stdin));
		TextInputStream.reset (new wxTextInputStream (*FInputStream));
	}

	TextUserInterface::~TextUserInterface ()
	{
#ifdef TC_UNIX
		signal (SIGHUP, SIG_DFL);
		signal (SIGINT, SIG_DFL);
		signal (SIGQUIT, SIG_DFL);
		signal (SIGTERM, SIG_DFL);
#endif
	}

	shared_ptr <KeyfileList> TextUserInterface::AskKeyfiles (const wxString &message) const
	{
		wxString msg = _("Enter keyfile");
		if (!message.empty())
			msg = message;

		make_shared_auto (KeyfileList, keyfiles);

		wxString s;
		wxString m = msg + L" [" + _("none") + L"]: ";
		while (!(s = AskString (m)).empty())
		{
			keyfiles->push_back (make_shared <Keyfile> (wstring (s)));
			m = msg + L" [" + _("finish") + L"]: ";
		}

		return keyfiles;
	}

	shared_ptr <VolumePassword> TextUserInterface::AskPassword (const wxString &message, bool verify) const
	{
		wxString msg = LangString["ENTER_PASSWORD"] + L": ";
		if (!message.empty())
			msg = message + L": ";

		SetTerminalEcho (false);
		finally_do ({ TextUserInterface::SetTerminalEcho (true); });

		wchar_t passwordBuf[4096];
		finally_do_arg (BufferPtr, BufferPtr (reinterpret_cast <byte *> (passwordBuf), sizeof (passwordBuf)), { finally_arg.Erase(); });

		make_shared_auto (VolumePassword, password);

		bool verPhase = false;
		while (true)
		{
			ShowString (verPhase ? wxString (_("Re-enter password: ")) : msg);

			const wxString &passwordStr = TextInputStream->ReadLine();
			size_t length = passwordStr.size();

			ShowString (L"\n");

			if (length < 1)
			{
				throw_sys_if (ferror (stdin));

				password->Set (passwordBuf, 0);
				return password;
			}

			for (size_t i = 0; i < length && i < VolumePassword::MaxSize; ++i)
			{
				passwordBuf[i] = (wchar_t) passwordStr[i];
				const_cast <wchar_t *> (passwordStr.c_str())[i] = L'X';
			}

			if (verify && verPhase)
			{
				make_shared_auto (VolumePassword, verPassword);
				verPassword->Set (passwordBuf, length);

				if (*password != *verPassword)
				{
					ShowInfo (_("Passwords do not match."));
					verPhase = false;
					continue;
				}
			}

			password->Set (passwordBuf, length);

			if (!verPhase)
			{
				try
				{
					password->CheckPortability();
				}
				catch (UnportablePassword &e)
				{
					if (verify)
					{
						ShowError (e);
						verPhase = false;
						continue;
					}

					ShowWarning ("UNSUPPORTED_CHARS_IN_PWD_RECOM");
				}

				if (verify)
				{
					if (password->Size() < VolumePassword::WarningSizeThreshold
						&& !AskYesNo (LangString ["PASSWORD_LENGTH_WARNING"], false, true))
						continue;
				}
			}

			if (!verify || verPhase)
				return password;

			if (!verPhase)
				verPhase = true;
		}

		return password;
	}

	wstring TextUserInterface::AskString (const wxString &message) const
	{
		ShowString (message);

		return wstring (TextInputStream->ReadLine());
	}
	
	bool TextUserInterface::AskYesNo (const wxString &message, bool defaultYes, bool warning) const
	{
		while (true)
		{
			wxString s = AskString (StringFormatter (L"{0} (y={1}/n={2}) [{3}]: ",
				message, LangString["YES"], LangString["NO"], LangString[defaultYes ? "YES" : "NO"]));

			if (s.IsSameAs (L'n', false) || s.IsSameAs (L"no", false) || !defaultYes && s.empty())
				return false;

			if (s.IsSameAs (L'y', false) || s.IsSameAs (L"yes", false) || defaultYes && s.empty())
				return true;
		};
	}

	shared_ptr <VolumePath> TextUserInterface::AskVolumePath (const wxString &message) const
	{
		return make_shared <VolumePath> (AskString (message.empty() ? wxString (_("Enter volume path: ")) : message));
	}

	void TextUserInterface::ChangePassword (shared_ptr <VolumePath> volumePath, shared_ptr <VolumePassword> password, shared_ptr <KeyfileList> keyfiles, shared_ptr <VolumePassword> newPassword, shared_ptr <KeyfileList> newKeyfiles) const
	{
		shared_ptr <Volume> volume;

		// Volume path
		if (!volumePath.get())
		{
			if (Preferences.NonInteractive)
				throw MissingArgument (SRC_POS);

			volumePath = AskVolumePath ();
		}

		bool passwordInteractive = !password.get();
		bool keyfilesInteractive = !keyfiles.get();

		while (true)
		{
			// Current password
			if (!passwordInteractive)
			{
				try
				{
					password->CheckPortability();
				}
				catch (UnportablePassword &)
				{
					ShowWarning ("UNSUPPORTED_CHARS_IN_PWD_RECOM");
				}
			}
			else if (!Preferences.NonInteractive)
			{
				password = AskPassword ();
			}

			// Current keyfiles
			try
			{
				if (keyfilesInteractive)
				{
					// Ask for keyfiles only if required
					try
					{
						keyfiles.reset (new KeyfileList);
						volume = Core->OpenVolume (volumePath, Preferences.DefaultMountOptions.PreserveTimestamps, password, keyfiles);
					}
					catch (PasswordException&)
					{
						if (!Preferences.NonInteractive)
							keyfiles = AskKeyfiles ();
					}
				}	

				if (!volume.get())
					volume = Core->OpenVolume (volumePath, Preferences.DefaultMountOptions.PreserveTimestamps, password, keyfiles);
			}
			catch (PasswordException &e)
			{
				if (Preferences.NonInteractive || !passwordInteractive || !keyfilesInteractive)
					throw;

				ShowInfo (e);
				continue;
			}

			break;
		}

		// New password
		if (newPassword.get())
			newPassword->CheckPortability();
		else if (!Preferences.NonInteractive)
			newPassword = AskPassword (_("Enter new password"), true);

		// New keyfiles
		if (!newKeyfiles.get() && !Preferences.NonInteractive)
		{
			if (keyfiles.get() && keyfiles->size() > 0 && AskYesNo (_("Keep current keyfiles?"), true))
				newKeyfiles = keyfiles;
			else
				newKeyfiles = AskKeyfiles (_("Enter new keyfile"));
		}

		Core->ChangePassword (volume, newPassword, newKeyfiles);

		ShowInfo ("PASSWORD_CHANGED");
	}
	
	void TextUserInterface::DoShowError (const wxString &message) const
	{
		wcerr << L"Error: " << static_cast<wstring> (message) << endl;
	}

	void TextUserInterface::DoShowInfo (const wxString &message) const
	{
		wcout << static_cast<wstring> (message) << endl;
	}

	void TextUserInterface::DoShowString (const wxString &str) const
	{
		wcout << str.c_str();
	}

	void TextUserInterface::DoShowWarning (const wxString &message) const
	{
		wcerr << L"Warning: " << static_cast<wstring> (message) << endl;
	}

	shared_ptr <GetStringFunctor> TextUserInterface::GetAdminPasswordRequestHandler ()
	{
		struct AdminPasswordRequestHandler : public GetStringFunctor
		{
			AdminPasswordRequestHandler (TextUserInterface *userInterface) : UI (userInterface) { }
			virtual string operator() ()
			{
				UI->ShowString (_("Enter system administrator password: "));

				TextUserInterface::SetTerminalEcho (false);
				finally_do ({ TextUserInterface::SetTerminalEcho (true); });
				
				string password = StringConverter::ToSingle (wstring (UI->TextInputStream->ReadLine()));
				UI->ShowString (L"\n");
				return password;
			}
			TextUserInterface *UI;
		};
		
		return shared_ptr <GetStringFunctor> (new AdminPasswordRequestHandler (this));
	}

	VolumeInfoList TextUserInterface::MountAllDeviceHostedVolumes (MountOptions &options) const
	{
		while (true)
		{
			if (!options.Password)
				options.Password = AskPassword();

			if (!options.Keyfiles)
				options.Keyfiles = AskKeyfiles();

			VolumeInfoList mountedVolumes = UserInterface::MountAllDeviceHostedVolumes (options);
			
			if (!mountedVolumes.empty())
				return mountedVolumes;

			options.Password.reset();
		}
	}
	
	shared_ptr <VolumeInfo> TextUserInterface::MountVolume (MountOptions &options) const
	{
		shared_ptr <VolumeInfo> volume;

		// Volume path
		if (!options.Path)
			options.Path = AskVolumePath ();
		
		if (Core->IsVolumeMounted (*options.Path))
		{
			ShowInfo (StringFormatter (LangString["VOLUME_ALREADY_MOUNTED"], wstring (*options.Path)));
			return volume;
		}

		// Mount point
		if (!options.MountPoint && !options.NoFilesystem)
			options.MountPoint.reset (new DirectoryPath (AskString (_("Enter mount directory [default]: "))));
		
		VolumePassword password;
		KeyfileList keyfiles;

		if ((!options.Password || options.Password->IsEmpty())
			&& (!options.Keyfiles || options.Keyfiles->empty())
			&& !Core->IsPasswordCacheEmpty())
		{
			// Cached password
			try
			{
				volume = UserInterface::MountVolume (options);
			}
			catch (PasswordException&) { }
		}

		while (!volume)
		{
			// Password
			if (!options.Password)
			{
				options.Password = AskPassword (StringFormatter (_("Enter password for {0}"), wstring (*options.Path)));
			}
			else
			{
				try
				{
					if (options.Password)
						options.Password->CheckPortability();
				}
				catch (UnportablePassword &)
				{
					ShowWarning ("UNSUPPORTED_CHARS_IN_PWD_RECOM");
				}
			}

			// Keyfiles
			if (!options.Keyfiles)
				options.Keyfiles = AskKeyfiles();

			// Hidden volume protection
			if (options.Protection == VolumeProtection::None
				&& AskYesNo (_("Protect hidden volume?")))
				options.Protection = VolumeProtection::HiddenVolumeReadOnly;

			if (options.Protection == VolumeProtection::HiddenVolumeReadOnly)
			{
				if (!options.ProtectionPassword)
					options.ProtectionPassword = AskPassword (_("Enter password for hidden volume"));
				if (!options.ProtectionKeyfiles)
					options.ProtectionKeyfiles = AskKeyfiles (_("Enter keyfile for hidden volume"));
			}

			try
			{
				volume = UserInterface::MountVolume (options);
			}
			catch (ProtectionPasswordIncorrect &e)
			{
				ShowInfo (e);
				options.ProtectionPassword.reset();
			}
			catch (PasswordException &e)
			{
				ShowInfo (e);
				options.Password.reset();
			}
		}

		return volume;
	}

	bool TextUserInterface::OnInit ()
	{
		try
		{
			DefaultMessageOutput = new wxMessageOutputStderr;
			wxMessageOutput::Set (DefaultMessageOutput);

			InterfaceType = UserInterfaceType::Text;
			Init();

			SetExitOnFrameDelete (false);
		}
		catch (exception &e)
		{
			ShowError (e);
			return false;
		}
		return true;
	}

	int TextUserInterface::OnRun()
	{ 
		try
		{
			if (ProcessCommandLine ())
			{
				Application::SetExitCode (0);
				return 0;
			}
		}
		catch (exception &e)
		{
			ShowError (e);
		}

		Application::SetExitCode (1);
		return 1;
	}

	void TextUserInterface::OnSignal (int signal)
	{
#ifdef TC_UNIX
		try
		{
			SetTerminalEcho (true);
		}
		catch (...) { }
		_exit (1);
#endif
	}

	void TextUserInterface::SetTerminalEcho (bool enable)
	{
#ifdef TC_UNIX
		struct termios termAttr;
		if (tcgetattr (0, &termAttr) == 0)
		{
			if (!enable)
			{
				termAttr.c_lflag &= ~ECHO;
				throw_sys_if (tcsetattr (0, TCSANOW, &termAttr) != 0);
			}
			else
			{
				termAttr.c_lflag |= ECHO;
				throw_sys_if (tcsetattr (0, TCSANOW, &termAttr) != 0);
			}
		}
#endif
	}

	wxMessageOutput *DefaultMessageOutput;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.