"Fossies" - the Fresh Open Source Software Archive

Member "src/Main/TextUserInterface.cpp" (10 Oct 2018, 45653 Bytes) of package /windows/misc/VeraCrypt_1.23-Hotfix-2_Source.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "TextUserInterface.cpp" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.22_Source_vs_1.23_Source.

    1 /*
    2  Derived from source code of TrueCrypt 7.1a, which is
    3  Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed
    4  by the TrueCrypt License 3.0.
    5 
    6  Modifications and additions to the original source code (contained in this file)
    7  and all other portions of this file are Copyright (c) 2013-2017 IDRIX
    8  and are governed by the Apache License 2.0 the full text of which is
    9  contained in the file License.txt included in VeraCrypt binary and source
   10  code distribution packages.
   11 */
   12 
   13 #include "System.h"
   14 #ifdef TC_UNIX
   15 #include <signal.h>
   16 #include <termios.h>
   17 #include <sys/types.h>
   18 #include <sys/stat.h>
   19 #include <unistd.h>
   20 #include "Platform/Unix/Process.h"
   21 #endif
   22 
   23 #include "Common/SecurityToken.h"
   24 #include "Core/RandomNumberGenerator.h"
   25 #include "Application.h"
   26 #include "TextUserInterface.h"
   27 
   28 namespace VeraCrypt
   29 {
   30     TextUserInterface::TextUserInterface ()
   31     {
   32 #ifdef TC_UNIX
   33         signal (SIGHUP, OnSignal);
   34         signal (SIGINT, OnSignal);
   35         signal (SIGQUIT, OnSignal);
   36         signal (SIGTERM, OnSignal);
   37 
   38         struct stat statBuf;
   39         if (fstat (0, &statBuf) != -1)
   40 #endif
   41         {
   42             FInputStream.reset (new wxFFileInputStream (stdin));
   43             TextInputStream.reset (new wxTextInputStream (*FInputStream));
   44         }
   45     }
   46 
   47     TextUserInterface::~TextUserInterface ()
   48     {
   49         try
   50         {
   51             if (RandomNumberGenerator::IsRunning())
   52                 RandomNumberGenerator::Stop();
   53         }
   54         catch (...) { }
   55 
   56 #ifdef TC_UNIX
   57         signal (SIGHUP, SIG_DFL);
   58         signal (SIGINT, SIG_DFL);
   59         signal (SIGQUIT, SIG_DFL);
   60         signal (SIGTERM, SIG_DFL);
   61 #endif
   62     }
   63 
   64     FilePath TextUserInterface::AskFilePath (const wxString &message) const
   65     {
   66         return AskString (!message.empty() ? message : wxString (_("Enter filename: ")));
   67     }
   68 
   69     shared_ptr <KeyfileList> TextUserInterface::AskKeyfiles (const wxString &message) const
   70     {
   71         wxString msg = _("Enter keyfile");
   72         if (!message.empty())
   73             msg = message;
   74 
   75         make_shared_auto (KeyfileList, keyfiles);
   76 
   77         wxString s;
   78         wxString m = msg + L" [" + _("none") + L"]: ";
   79         while (!(s = AskString (m)).empty())
   80         {
   81             keyfiles->push_back (make_shared <Keyfile> (wstring (s)));
   82             m = msg + L" [" + _("finish") + L"]: ";
   83         }
   84 
   85         return keyfiles;
   86     }
   87 
   88     shared_ptr <VolumePassword> TextUserInterface::AskPassword (const wxString &message, bool verify) const
   89     {
   90         wxString msg = LangString["ENTER_PASSWORD"] + L": ";
   91         if (!message.empty())
   92             msg = message + L": ";
   93 
   94         SetTerminalEcho (false);
   95         finally_do ({ TextUserInterface::SetTerminalEcho (true); });
   96 
   97         wchar_t passwordBuf[4096];
   98         finally_do_arg (BufferPtr, BufferPtr (reinterpret_cast <byte *> (passwordBuf), sizeof (passwordBuf)), { finally_arg.Erase(); });
   99 
  100         shared_ptr<VolumePassword> password;
  101 
  102         bool verPhase = false;
  103         while (true)
  104         {
  105             ShowString (verPhase ? wxString (_("Re-enter password: ")) : msg);
  106 
  107             wxString passwordStr;
  108             ReadInputStreamLine (passwordStr);
  109 
  110             size_t length = passwordStr.size();
  111 
  112             ShowString (L"\n");
  113 
  114             if (!verPhase && length < 1)
  115             {
  116                 return shared_ptr <VolumePassword>(new VolumePassword ());
  117             }
  118 
  119             for (size_t i = 0; i < length && i < VolumePassword::MaxSize; ++i)
  120             {
  121                 passwordBuf[i] = (wchar_t) passwordStr[i];
  122                 const_cast <wchar_t *> (passwordStr.wc_str())[i] = L'X';
  123             }
  124 
  125             if (verify && verPhase)
  126             {
  127                 shared_ptr <VolumePassword> verPassword = ToUTF8Password (passwordBuf, length);
  128 
  129                 if (*password != *verPassword)
  130                 {
  131                     ShowInfo (_("Passwords do not match."));
  132                     ShowString (L"\n");
  133                     verPhase = false;
  134                     continue;
  135                 }
  136             }
  137 
  138             password = ToUTF8Password (passwordBuf, length);
  139 
  140             if (!verPhase)
  141             {
  142                 if (verify)
  143                 {
  144                     if (password->Size() < VolumePassword::WarningSizeThreshold)
  145                     {
  146                         SetTerminalEcho (true);
  147                         finally_do ({ TextUserInterface::SetTerminalEcho (false); });
  148 
  149                         if (!AskYesNo (LangString ["PASSWORD_LENGTH_WARNING"], false, true))
  150                         {
  151                             ShowString (L"\n");
  152                             continue;
  153                         }
  154                         ShowString (L"\n");
  155                     }
  156                 }
  157             }
  158 
  159             if (!verify || verPhase)
  160                 return password;
  161 
  162             if (!verPhase)
  163                 verPhase = true;
  164         }
  165 
  166         return password;
  167     }
  168 
  169     int TextUserInterface::AskPim (const wxString &message) const
  170     {
  171         int pim = -1;
  172         wxString msg = _("Enter new PIM: ");
  173         if (!message.empty())
  174             msg = message + L": ";
  175         while (pim < 0)
  176         {
  177             wstring pimStr = AskString (msg);
  178             if (pimStr.empty())
  179                 pim = 0;
  180             else
  181             {
  182                 try
  183                 {
  184                     pim = (int) StringConverter::ToUInt32 (pimStr);
  185                     if (pim > MAX_PIM_VALUE)
  186                     {
  187                         pim = -1;
  188                         ShowError ("PIM_TOO_BIG");
  189                         continue;
  190                     }
  191                 }
  192                 catch (...)
  193                 {
  194                     pim = -1;
  195                     continue;
  196                 }
  197             }
  198         }
  199 
  200         return pim;
  201     }
  202 
  203     ssize_t TextUserInterface::AskSelection (ssize_t optionCount, ssize_t defaultOption) const
  204     {
  205         while (true)
  206         {
  207             wstring selectionStr = AskString (defaultOption == -1 ? wxString (_("Select: ")) : wxString (wstring (StringFormatter (_("Select [{0}]: "), (uint32) defaultOption))));
  208             ssize_t selection;
  209 
  210             if (selectionStr.empty() && defaultOption != -1)
  211                 return defaultOption;
  212 
  213             try
  214             {
  215                 selection = StringConverter::ToUInt32 (selectionStr);
  216             }
  217             catch (...)
  218             {
  219                 continue;
  220             }
  221 
  222             if (selection > 0 && selection <= optionCount)
  223                 return selection;
  224         }
  225     }
  226 
  227     wstring TextUserInterface::AskString (const wxString &message) const
  228     {
  229         ShowString (message);
  230         return wstring (ReadInputStreamLine());
  231     }
  232 
  233     bool TextUserInterface::AskYesNo (const wxString &message, bool defaultYes, bool warning) const
  234     {
  235         while (true)
  236         {
  237             wxString s = AskString (StringFormatter (L"{0} (y={1}/n={2}) [{3}]: ",
  238                 message, LangString["YES"], LangString["NO"], LangString[defaultYes ? "YES" : "NO"]));
  239 
  240             if (s.IsSameAs (L'n', false) || s.IsSameAs (L"no", false) || (!defaultYes && s.empty()))
  241                 return false;
  242 
  243             if (s.IsSameAs (L'y', false) || s.IsSameAs (L"yes", false) || (defaultYes && s.empty()))
  244                 return true;
  245         };
  246     }
  247 
  248     shared_ptr <VolumePath> TextUserInterface::AskVolumePath (const wxString &message) const
  249     {
  250         return make_shared <VolumePath> (AskString (message.empty() ? wxString (_("Enter volume path: ")) : message));
  251     }
  252 
  253     void TextUserInterface::BackupVolumeHeaders (shared_ptr <VolumePath> volumePath) const
  254     {
  255         if (!volumePath)
  256             volumePath = AskVolumePath();
  257 
  258         if (!volumePath)
  259             throw UserAbort (SRC_POS);
  260 
  261 #ifdef TC_WINDOWS
  262         if (Core->IsVolumeMounted (*volumePath))
  263             throw_err (LangString["DISMOUNT_FIRST"]);
  264 #endif
  265 
  266         ShowInfo ("EXTERNAL_VOL_HEADER_BAK_FIRST_INFO");
  267 
  268         shared_ptr <Pkcs5Kdf> kdf;
  269         if (CmdLine->ArgHash)
  270         {
  271             kdf = Pkcs5Kdf::GetAlgorithm (*CmdLine->ArgHash, false);
  272         }
  273 
  274         shared_ptr <Volume> normalVolume;
  275         shared_ptr <Volume> hiddenVolume;
  276 
  277         MountOptions normalVolumeMountOptions;
  278         MountOptions hiddenVolumeMountOptions;
  279 
  280         normalVolumeMountOptions.Path = volumePath;
  281         hiddenVolumeMountOptions.Path = volumePath;
  282 
  283         VolumeType::Enum volumeType = VolumeType::Normal;
  284 
  285         // Open both types of volumes
  286         while (true)
  287         {
  288             shared_ptr <Volume> volume;
  289             MountOptions *options = (volumeType == VolumeType::Hidden ? &hiddenVolumeMountOptions : &normalVolumeMountOptions);
  290 
  291             while (!volume)
  292             {
  293                 ShowString (L"\n");
  294                 options->Password = AskPassword (LangString[volumeType == VolumeType::Hidden ? "ENTER_HIDDEN_VOL_PASSWORD" : "ENTER_NORMAL_VOL_PASSWORD"]);
  295                 options->Pim = AskPim (volumeType == VolumeType::Hidden ?_("Enter PIM for the hidden volume") : _("Enter PIM for the normal/outer volume"));
  296                 options->Keyfiles = AskKeyfiles();
  297 
  298                 try
  299                 {
  300                     volume = Core->OpenVolume (
  301                         options->Path,
  302                         options->PreserveTimestamps,
  303                         options->Password,
  304                         options->Pim,
  305                         kdf,
  306                         false,
  307                         options->Keyfiles,
  308                         options->Protection,
  309                         options->ProtectionPassword,
  310                         options->ProtectionPim,
  311                         options->ProtectionKdf,
  312                         options->ProtectionKeyfiles,
  313                         true,
  314                         volumeType,
  315                         options->UseBackupHeaders
  316                         );
  317                 }
  318                 catch (PasswordException &e)
  319                 {
  320                     bool bFailed = true;
  321                     if (!options->UseBackupHeaders)
  322                     {
  323                         try
  324                         {
  325                             volume = Core->OpenVolume (
  326                                 options->Path,
  327                                 options->PreserveTimestamps,
  328                                 options->Password,
  329                                 options->Pim,
  330                                 kdf,
  331                                 false,
  332                                 options->Keyfiles,
  333                                 options->Protection,
  334                                 options->ProtectionPassword,
  335                                 options->ProtectionPim,
  336                                 options->ProtectionKdf,
  337                                 options->ProtectionKeyfiles,
  338                                 true,
  339                                 volumeType,
  340                                 true
  341                                 );
  342                             
  343                             bFailed = false;
  344                         }
  345                         catch (...)
  346                         {
  347                         }
  348                     }
  349                     
  350                     if (bFailed)
  351                         ShowInfo (e);
  352                     else
  353                         ShowInfo ("HEADER_DAMAGED_AUTO_USED_HEADER_BAK");
  354                 }
  355             }
  356 
  357             if (volumeType == VolumeType::Hidden)
  358                 hiddenVolume = volume;
  359             else
  360                 normalVolume = volume;
  361 
  362             // Ask whether a hidden volume is present
  363             if (volumeType == VolumeType::Normal && AskYesNo (L"\n" + LangString["DOES_VOLUME_CONTAIN_HIDDEN"]))
  364             {
  365                 volumeType = VolumeType::Hidden;
  366                 continue;
  367             }
  368 
  369             break;
  370         }
  371 
  372         if (hiddenVolume)
  373         {
  374             if (typeid (*normalVolume->GetLayout()) == typeid (VolumeLayoutV1Normal))
  375                 throw ParameterIncorrect (SRC_POS);
  376 
  377             if (typeid (*normalVolume->GetLayout()) == typeid (VolumeLayoutV2Normal) && typeid (*hiddenVolume->GetLayout()) != typeid (VolumeLayoutV2Hidden))
  378                 throw ParameterIncorrect (SRC_POS);
  379         }
  380 
  381         // Ask user to select backup file path
  382         wxString confirmMsg = L"\n" + LangString["CONFIRM_VOL_HEADER_BAK"] + L"\n";
  383 
  384         if (!AskYesNo (wxString::Format (confirmMsg, wstring (*volumePath).c_str()), true))
  385             return;
  386 
  387         ShowString (L"\n");
  388 
  389         FilePath filePath = AskFilePath();
  390         if (filePath.IsEmpty())
  391             throw UserAbort (SRC_POS);
  392 
  393         File backupFile;
  394         backupFile.Open (filePath, File::CreateWrite);
  395 
  396         RandomNumberGenerator::Start();
  397         /* force the display of the random enriching interface */
  398         RandomNumberGenerator::SetEnrichedByUserStatus (false);
  399         UserEnrichRandomPool();
  400 
  401         // Re-encrypt volume header
  402         SecureBuffer newHeaderBuffer (normalVolume->GetLayout()->GetHeaderSize());
  403         Core->ReEncryptVolumeHeaderWithNewSalt (newHeaderBuffer, normalVolume->GetHeader(), normalVolumeMountOptions.Password, normalVolumeMountOptions.Pim, normalVolumeMountOptions.Keyfiles);
  404 
  405         backupFile.Write (newHeaderBuffer);
  406 
  407         if (hiddenVolume)
  408         {
  409             // Re-encrypt hidden volume header
  410             Core->ReEncryptVolumeHeaderWithNewSalt (newHeaderBuffer, hiddenVolume->GetHeader(), hiddenVolumeMountOptions.Password, hiddenVolumeMountOptions.Pim, hiddenVolumeMountOptions.Keyfiles);
  411         }
  412         else
  413         {
  414             // Store random data in place of hidden volume header
  415             shared_ptr <EncryptionAlgorithm> ea = normalVolume->GetEncryptionAlgorithm();
  416             Core->RandomizeEncryptionAlgorithmKey (ea);
  417             ea->Encrypt (newHeaderBuffer);
  418         }
  419 
  420         backupFile.Write (newHeaderBuffer);
  421 
  422         ShowString (L"\n");
  423         ShowInfo ("VOL_HEADER_BACKED_UP");
  424     }
  425 
  426     void TextUserInterface::ChangePassword (shared_ptr <VolumePath> volumePath, shared_ptr <VolumePassword> password, int pim, shared_ptr <Hash> currentHash, bool truecryptMode, shared_ptr <KeyfileList> keyfiles, shared_ptr <VolumePassword> newPassword, int newPim, shared_ptr <KeyfileList> newKeyfiles, shared_ptr <Hash> newHash) const
  427     {
  428         shared_ptr <Volume> volume;
  429 
  430         // Volume path
  431         if (!volumePath.get())
  432         {
  433             if (Preferences.NonInteractive)
  434                 throw MissingArgument (SRC_POS);
  435 
  436             volumePath = AskVolumePath ();
  437         }
  438 
  439         if (volumePath->IsEmpty())
  440             throw UserAbort (SRC_POS);
  441 
  442         bool passwordInteractive = !password.get();
  443         bool keyfilesInteractive = !keyfiles.get();
  444 
  445         shared_ptr<Pkcs5Kdf> kdf;
  446         if (currentHash)
  447         {
  448             kdf = Pkcs5Kdf::GetAlgorithm (*currentHash, truecryptMode);
  449         }
  450 
  451         while (true)
  452         {
  453             // Current password
  454             if (!passwordInteractive)
  455             {
  456 
  457             }
  458             else if (!Preferences.NonInteractive)
  459             {
  460                 password = AskPassword ();
  461             }
  462 
  463             // current PIM
  464             if (!truecryptMode && !Preferences.NonInteractive && (pim < 0))
  465             {
  466                 pim = AskPim (_("Enter current PIM"));
  467             }
  468 
  469             // Current keyfiles
  470             try
  471             {
  472                 if (keyfilesInteractive)
  473                 {
  474                     // Ask for keyfiles only if required
  475                     try
  476                     {
  477                         keyfiles.reset (new KeyfileList);
  478                         volume = Core->OpenVolume (volumePath, Preferences.DefaultMountOptions.PreserveTimestamps, password, pim, kdf, truecryptMode, keyfiles);
  479                     }
  480                     catch (PasswordException&)
  481                     {
  482                         if (!Preferences.NonInteractive)
  483                             keyfiles = AskKeyfiles ();
  484                     }
  485                 }
  486 
  487                 if (!volume.get())
  488                     volume = Core->OpenVolume (volumePath, Preferences.DefaultMountOptions.PreserveTimestamps, password, pim, kdf, truecryptMode, keyfiles);
  489             }
  490             catch (PasswordException &e)
  491             {
  492                 if (Preferences.NonInteractive || !passwordInteractive || !keyfilesInteractive)
  493                     throw;
  494 
  495                 ShowInfo (e);
  496                 continue;
  497             }
  498 
  499             break;
  500         }
  501 
  502         // New password
  503         if (!newPassword.get() && !Preferences.NonInteractive)
  504             newPassword = AskPassword (_("Enter new password"), true);
  505 
  506         // New PIM
  507         if ((newPim < 0) && !Preferences.NonInteractive)
  508             newPim = AskPim (_("Enter new PIM"));
  509 
  510         // New keyfiles
  511         if (!newKeyfiles.get() && !Preferences.NonInteractive)
  512         {
  513             if (keyfiles.get() && keyfiles->size() > 0 && AskYesNo (_("Keep current keyfiles?"), true))
  514                 newKeyfiles = keyfiles;
  515             else
  516                 newKeyfiles = AskKeyfiles (_("Enter new keyfile"));
  517         }
  518 
  519         /* force the display of the random enriching interface */
  520         RandomNumberGenerator::SetEnrichedByUserStatus (false);
  521         UserEnrichRandomPool();
  522 
  523         Core->ChangePassword (volume, newPassword, newPim, newKeyfiles,
  524             newHash ? Pkcs5Kdf::GetAlgorithm (*newHash, false) : shared_ptr <Pkcs5Kdf>());
  525 
  526         ShowInfo ("PASSWORD_CHANGED");
  527     }
  528 
  529     void TextUserInterface::CreateKeyfile (shared_ptr <FilePath> keyfilePath) const
  530     {
  531         FilePath path;
  532 
  533         RandomNumberGenerator::Start();
  534         /* force the display of the random enriching interface */
  535         RandomNumberGenerator::SetEnrichedByUserStatus (false);
  536         UserEnrichRandomPool();
  537 
  538         if (keyfilePath)
  539         {
  540             Core->CreateKeyfile (*keyfilePath);
  541         }
  542         else
  543         {
  544             wstring fileName = AskFilePath();
  545             if (fileName.empty())
  546                 return;
  547 
  548             Core->CreateKeyfile (fileName);
  549         }
  550 
  551         ShowInfo ("KEYFILE_CREATED");
  552     }
  553 
  554     void TextUserInterface::CreateVolume (shared_ptr <VolumeCreationOptions> options) const
  555     {
  556         // Volume type
  557         if (options->Type == VolumeType::Unknown)
  558         {
  559             if (Preferences.NonInteractive)
  560             {
  561                 options->Type = VolumeType::Normal;
  562             }
  563             else
  564             {
  565                 ShowString (_("Volume type:\n 1) Normal\n 2) Hidden\n"));
  566 
  567                 switch (AskSelection (2, 1))
  568                 {
  569                 case 1:
  570                     options->Type = VolumeType::Normal;
  571                     break;
  572 
  573                 case 2:
  574                     options->Type = VolumeType::Hidden;
  575                     break;
  576                 }
  577             }
  578         }
  579 
  580         shared_ptr <VolumeLayout> layout;
  581         if (options->Type == VolumeType::Hidden)
  582             layout.reset (new VolumeLayoutV2Hidden);
  583         else
  584             layout.reset (new VolumeLayoutV2Normal);
  585 
  586         if (!Preferences.NonInteractive && options->Type == VolumeType::Hidden)
  587             ShowInfo (_("\nIMPORTANT: Inexperienced users should use the graphical user interface to create a hidden volume. When using the text interface, the procedure described in the command line help must be followed to create a hidden volume."));
  588 
  589         // Volume path
  590         if (options->Path.IsEmpty())
  591         {
  592             if (Preferences.NonInteractive)
  593                 throw MissingArgument (SRC_POS);
  594 
  595             do
  596             {
  597                 ShowString (L"\n");
  598                 options->Path = VolumePath (*AskVolumePath());
  599             } while (options->Path.IsEmpty());
  600         }
  601 
  602         // Sector size
  603         if (options->Path.IsDevice())
  604             options->SectorSize = Core->GetDeviceSectorSize (options->Path);
  605         else
  606             options->SectorSize = TC_SECTOR_SIZE_FILE_HOSTED_VOLUME;
  607 
  608         // Volume size
  609         uint64 hostSize = 0;
  610 
  611         if (options->Type == VolumeType::Hidden)
  612         {
  613             FilesystemPath fsPath (wstring (options->Path));
  614 
  615             if (fsPath.IsFile())
  616             {
  617                 File file;
  618                 file.Open (fsPath);
  619                 hostSize = file.Length();
  620             }
  621             else if (fsPath.IsDevice())
  622             {
  623                 hostSize = Core->GetDeviceSize (fsPath);
  624             }
  625             else
  626             {
  627                 throw_err (_("Hidden volume can be created only in an existing file or device."));
  628             }
  629 
  630             if (hostSize < TC_MIN_HIDDEN_VOLUME_HOST_SIZE)
  631                 throw_err (StringFormatter (_("Minimum outer volume size is {0}."), SizeToString (TC_MIN_HIDDEN_VOLUME_HOST_SIZE)));
  632         }
  633 
  634         uint64 minVolumeSize = options->Type == VolumeType::Hidden ? TC_MIN_HIDDEN_VOLUME_SIZE : TC_MIN_VOLUME_SIZE;
  635         uint64 maxVolumeSize = options->Type == VolumeType::Hidden ? VolumeLayoutV2Normal().GetMaxDataSize (hostSize) - TC_MIN_FAT_FS_SIZE : TC_MAX_VOLUME_SIZE_GENERAL;
  636 
  637         if (options->Path.IsDevice() && options->Type != VolumeType::Hidden)
  638         {
  639             if (options->Size != 0)
  640                 throw_err (_("Volume size cannot be changed for device-hosted volumes."));
  641 
  642             options->Size = Core->GetDeviceSize (options->Path);
  643         }
  644         else
  645         {
  646             options->Quick = false;
  647 
  648             uint32 sectorSizeRem = options->Size % options->SectorSize;
  649             if (sectorSizeRem != 0)
  650                 options->Size += options->SectorSize - sectorSizeRem;
  651 
  652             while (options->Size == 0)
  653             {
  654                 if (Preferences.NonInteractive)
  655                     throw MissingArgument (SRC_POS);
  656 
  657                 wstring sizeStr = AskString (options->Type == VolumeType::Hidden ? _("\nEnter hidden volume size (sizeK/size[M]/sizeG): ") : _("\nEnter volume size (sizeK/size[M]/sizeG): "));
  658                 uint64 multiplier = 1024 * 1024;
  659 
  660                 if (sizeStr.find (L"K") != string::npos)
  661                 {
  662                     multiplier = 1024;
  663                     sizeStr.resize (sizeStr.size() - 1);
  664                 }
  665                 else if (sizeStr.find (L"M") != string::npos)
  666                 {
  667                     sizeStr.resize (sizeStr.size() - 1);
  668                 }
  669                 else if (sizeStr.find (L"G") != string::npos)
  670                 {
  671                     multiplier = 1024 * 1024 * 1024;
  672                     sizeStr.resize (sizeStr.size() - 1);
  673                 }
  674                 else if (sizeStr.find (L"T") != string::npos)
  675                 {
  676                     multiplier = (uint64) 1024 * 1024 * 1024 * 1024;
  677                     sizeStr.resize (sizeStr.size() - 1);
  678                 }
  679 
  680                 try
  681                 {
  682                     options->Size = StringConverter::ToUInt64 (sizeStr);
  683                     options->Size *= multiplier;
  684 
  685                     sectorSizeRem = options->Size % options->SectorSize;
  686                     if (sectorSizeRem != 0)
  687                         options->Size += options->SectorSize - sectorSizeRem;
  688                 }
  689                 catch (...)
  690                 {
  691                     options->Size = 0;
  692                     continue;
  693                 }
  694 
  695                 if (options->Size < minVolumeSize)
  696                 {
  697                     ShowError (StringFormatter (_("Minimum volume size is {0}."), SizeToString (minVolumeSize)));
  698                     options->Size = 0;
  699                 }
  700 
  701                 if (options->Size > maxVolumeSize)
  702                 {
  703                     ShowError (StringFormatter (_("Maximum volume size is {0}."), SizeToString (maxVolumeSize)));
  704                     options->Size = 0;
  705                 }
  706             }
  707         }
  708 
  709         if (options->Size < minVolumeSize || options->Size > maxVolumeSize)
  710             throw_err (_("Incorrect volume size"));
  711 
  712         if (options->Type == VolumeType::Hidden)
  713             options->Quick = true;
  714 
  715         // Encryption algorithm
  716         if (!options->EA)
  717         {
  718             if (Preferences.NonInteractive)
  719                 throw MissingArgument (SRC_POS);
  720 
  721             ShowInfo (wxString (L"\n") + LangString["ENCRYPTION_ALGORITHM_LV"] + L":");
  722 
  723             vector < shared_ptr <EncryptionAlgorithm> > encryptionAlgorithms;
  724             foreach (shared_ptr <EncryptionAlgorithm> ea, EncryptionAlgorithm::GetAvailableAlgorithms())
  725             {
  726                 if (!ea->IsDeprecated())
  727                 {
  728                     ShowString (StringFormatter (L" {0}) {1}\n", (uint32) encryptionAlgorithms.size() + 1, ea->GetName(true)));
  729                     encryptionAlgorithms.push_back (ea);
  730                 }
  731             }
  732 
  733 
  734             options->EA = encryptionAlgorithms[AskSelection (encryptionAlgorithms.size(), 1) - 1];
  735         }
  736 
  737         // Hash algorithm
  738         if (!options->VolumeHeaderKdf)
  739         {
  740             if (Preferences.NonInteractive)
  741                 throw MissingArgument (SRC_POS);
  742 
  743             ShowInfo (_("\nHash algorithm:"));
  744 
  745             vector < shared_ptr <Hash> > hashes;
  746             foreach (shared_ptr <Hash> hash, Hash::GetAvailableAlgorithms())
  747             {
  748                 if (!hash->IsDeprecated())
  749                 {
  750                     ShowString (StringFormatter (L" {0}) {1}\n", (uint32) hashes.size() + 1, hash->GetName()));
  751                     hashes.push_back (hash);
  752                 }
  753             }
  754 
  755             shared_ptr <Hash> selectedHash = hashes[AskSelection (hashes.size(), 1) - 1];
  756             RandomNumberGenerator::SetHash (selectedHash);
  757             options->VolumeHeaderKdf = Pkcs5Kdf::GetAlgorithm (*selectedHash, false);
  758 
  759         }
  760 
  761         // Filesystem
  762         options->FilesystemClusterSize = 0;
  763 
  764         if (options->Filesystem == VolumeCreationOptions::FilesystemType::Unknown)
  765         {
  766             if (Preferences.NonInteractive)
  767             {
  768                 options->Filesystem = VolumeCreationOptions::FilesystemType::GetPlatformNative();
  769             }
  770             else
  771             {
  772                 ShowInfo (_("\nFilesystem:"));
  773 
  774                 vector <VolumeCreationOptions::FilesystemType::Enum> filesystems;
  775 
  776                 ShowInfo (L" 1) " + LangString["NONE"]); filesystems.push_back (VolumeCreationOptions::FilesystemType::None);
  777                 ShowInfo (L" 2) FAT"); filesystems.push_back (VolumeCreationOptions::FilesystemType::FAT);
  778 
  779 #if defined (TC_LINUX)
  780                 ShowInfo (L" 3) Linux Ext2"); filesystems.push_back (VolumeCreationOptions::FilesystemType::Ext2);
  781                 ShowInfo (L" 4) Linux Ext3"); filesystems.push_back (VolumeCreationOptions::FilesystemType::Ext3);
  782                 ShowInfo (L" 5) Linux Ext4"); filesystems.push_back (VolumeCreationOptions::FilesystemType::Ext4);
  783                 ShowInfo (L" 6) NTFS");       filesystems.push_back (VolumeCreationOptions::FilesystemType::NTFS);
  784                 ShowInfo (L" 7) exFAT");      filesystems.push_back (VolumeCreationOptions::FilesystemType::exFAT);
  785 #elif defined (TC_MACOSX)
  786                 ShowInfo (L" 3) Mac OS Extended"); filesystems.push_back (VolumeCreationOptions::FilesystemType::MacOsExt);
  787                 ShowInfo (L" 4) exFAT");      filesystems.push_back (VolumeCreationOptions::FilesystemType::exFAT);
  788 #elif defined (TC_FREEBSD) || defined (TC_SOLARIS)
  789                 ShowInfo (L" 3) UFS"); filesystems.push_back (VolumeCreationOptions::FilesystemType::UFS);
  790 #endif
  791 
  792                 options->Filesystem = filesystems[AskSelection (filesystems.size(), 2) - 1];
  793             }
  794         }
  795 
  796         uint64 filesystemSize = layout->GetMaxDataSize (options->Size);
  797 
  798         if (options->Filesystem == VolumeCreationOptions::FilesystemType::FAT
  799             && (filesystemSize < TC_MIN_FAT_FS_SIZE || filesystemSize > TC_MAX_FAT_SECTOR_COUNT * options->SectorSize))
  800         {
  801             throw_err (_("Specified volume size cannot be used with FAT filesystem."));
  802         }
  803 
  804         // Password
  805         if (!options->Password && !Preferences.NonInteractive)
  806         {
  807             ShowString (L"\n");
  808             options->Password = AskPassword (_("Enter password"), true);
  809         }
  810 
  811         // PIM
  812         if ((options->Pim < 0) && !Preferences.NonInteractive)
  813         {
  814             ShowString (L"\n");
  815             options->Pim = AskPim (_("Enter PIM"));
  816         }
  817 
  818         // Keyfiles
  819         if (!options->Keyfiles && !Preferences.NonInteractive)
  820         {
  821             ShowString (L"\n");
  822             options->Keyfiles = AskKeyfiles (_("Enter keyfile path"));
  823         }
  824 
  825         if ((!options->Keyfiles || options->Keyfiles->empty())
  826             && (!options->Password || options->Password->IsEmpty()))
  827         {
  828             throw_err (_("Password cannot be empty when no keyfile is specified"));
  829         }
  830 
  831         // Random data
  832         RandomNumberGenerator::Start();
  833         /* force the display of the random enriching interface */
  834         RandomNumberGenerator::SetEnrichedByUserStatus (false);
  835         UserEnrichRandomPool();
  836 
  837         ShowString (L"\n");
  838         wxLongLong startTime = wxGetLocalTimeMillis();
  839 
  840         VolumeCreator creator;
  841         creator.CreateVolume (options);
  842 
  843         bool volumeCreated = false;
  844         while (!volumeCreated)
  845         {
  846             VolumeCreator::ProgressInfo progress = creator.GetProgressInfo();
  847 
  848             wxLongLong timeDiff = wxGetLocalTimeMillis() - startTime;
  849             if (timeDiff.GetValue() > 0)
  850             {
  851                 uint64 speed = progress.SizeDone * 1000 / timeDiff.GetValue();
  852 
  853                 volumeCreated = !progress.CreationInProgress;
  854 
  855                 ShowString (wxString::Format (L"\rDone: %7.3f%%  Speed: %9s  Left: %s         ",
  856                     100.0 - double (options->Size - progress.SizeDone) / (double (options->Size) / 100.0),
  857                     speed > 0 ? (const wchar_t*) SpeedToString (speed).c_str() : L" ",
  858                     speed > 0 ? (const wchar_t*) TimeSpanToString ((options->Size - progress.SizeDone) / speed).c_str() : L""));
  859             }
  860 
  861             Thread::Sleep (100);
  862         }
  863 
  864         ShowString (L"\n\n");
  865         creator.CheckResult();
  866 
  867 #ifdef TC_UNIX
  868         if (options->Filesystem != VolumeCreationOptions::FilesystemType::None
  869             && options->Filesystem != VolumeCreationOptions::FilesystemType::FAT)
  870         {
  871             const char *fsFormatter = nullptr;
  872 
  873             switch (options->Filesystem)
  874             {
  875 #if defined (TC_LINUX)
  876             case VolumeCreationOptions::FilesystemType::Ext2:       fsFormatter = "mkfs.ext2"; break;
  877             case VolumeCreationOptions::FilesystemType::Ext3:       fsFormatter = "mkfs.ext3"; break;
  878             case VolumeCreationOptions::FilesystemType::Ext4:       fsFormatter = "mkfs.ext4"; break;
  879             case VolumeCreationOptions::FilesystemType::NTFS:       fsFormatter = "mkfs.ntfs"; break;
  880             case VolumeCreationOptions::FilesystemType::exFAT:      fsFormatter = "mkfs.exfat"; break;
  881 #elif defined (TC_MACOSX)
  882             case VolumeCreationOptions::FilesystemType::MacOsExt:   fsFormatter = "newfs_hfs"; break;
  883             case VolumeCreationOptions::FilesystemType::exFAT:      fsFormatter = "newfs_exfat"; break;
  884 #elif defined (TC_FREEBSD) || defined (TC_SOLARIS)
  885             case VolumeCreationOptions::FilesystemType::UFS:        fsFormatter = "newfs" ; break;
  886 #endif
  887             default: throw ParameterIncorrect (SRC_POS);
  888             }
  889 
  890             MountOptions mountOptions (GetPreferences().DefaultMountOptions);
  891             mountOptions.Path = make_shared <VolumePath> (options->Path);
  892             mountOptions.NoFilesystem = true;
  893             mountOptions.Protection = VolumeProtection::None;
  894             mountOptions.Password = options->Password;
  895             mountOptions.Pim = options->Pim;
  896             mountOptions.Keyfiles = options->Keyfiles;
  897 
  898             shared_ptr <VolumeInfo> volume = Core->MountVolume (mountOptions);
  899             finally_do_arg (shared_ptr <VolumeInfo>, volume, { Core->DismountVolume (finally_arg, true); });
  900 
  901             Thread::Sleep (2000);   // Try to prevent race conditions caused by OS
  902 
  903             // Temporarily take ownership of the device if the user is not an administrator
  904             UserId origDeviceOwner ((uid_t) -1);
  905 
  906             DevicePath virtualDevice = volume->VirtualDevice;
  907 #ifdef TC_MACOSX
  908             string virtualDeviceStr = virtualDevice;
  909             if (virtualDeviceStr.find ("/dev/rdisk") != 0)
  910                 virtualDevice = "/dev/r" + virtualDeviceStr.substr (5);
  911 #endif
  912             try
  913             {
  914                 File file;
  915                 file.Open (virtualDevice, File::OpenReadWrite);
  916             }
  917             catch (...)
  918             {
  919                 if (!Core->HasAdminPrivileges())
  920                 {
  921                     origDeviceOwner = virtualDevice.GetOwner();
  922                     Core->SetFileOwner (virtualDevice, UserId (getuid()));
  923                 }
  924             }
  925 
  926             finally_do_arg2 (FilesystemPath, virtualDevice, UserId, origDeviceOwner,
  927             {
  928                 if (finally_arg2.SystemId != (uid_t) -1)
  929                     Core->SetFileOwner (finally_arg, finally_arg2);
  930             });
  931 
  932             // Create filesystem
  933             list <string> args;
  934 
  935             if (options->Filesystem == VolumeCreationOptions::FilesystemType::MacOsExt && options->Size >= 10 * BYTES_PER_MB)
  936                 args.push_back ("-J");
  937 
  938             // Perform a quick NTFS formatting
  939             if (options->Filesystem == VolumeCreationOptions::FilesystemType::NTFS)
  940                 args.push_back ("-f");
  941 
  942             args.push_back (string (virtualDevice));
  943 
  944             Process::Execute (fsFormatter, args);
  945         }
  946 #endif // TC_UNIX
  947 
  948         ShowInfo (options->Type == VolumeType::Hidden ? "HIDVOL_FORMAT_FINISHED_HELP" : "FORMAT_FINISHED_INFO");
  949     }
  950 
  951     void TextUserInterface::DeleteSecurityTokenKeyfiles () const
  952     {
  953         shared_ptr <KeyfileList> keyfiles = AskKeyfiles();
  954         if (keyfiles->empty())
  955             throw UserAbort();
  956 
  957         foreach_ref (const Keyfile &keyfile, *keyfiles)
  958         {
  959             SecurityToken::DeleteKeyfile (SecurityTokenKeyfilePath (FilePath (keyfile)));
  960         }
  961     }
  962 
  963     void TextUserInterface::DoShowError (const wxString &message) const
  964     {
  965         wcerr << L"Error: " << static_cast<wstring> (message) << endl;
  966     }
  967 
  968     void TextUserInterface::DoShowInfo (const wxString &message) const
  969     {
  970         wcout << static_cast<wstring> (message) << endl;
  971     }
  972 
  973     void TextUserInterface::DoShowString (const wxString &str) const
  974     {
  975         wcout << str.c_str();
  976     }
  977 
  978     void TextUserInterface::DoShowWarning (const wxString &message) const
  979     {
  980         wcerr << L"Warning: " << static_cast<wstring> (message) << endl;
  981     }
  982 
  983     void TextUserInterface::ExportSecurityTokenKeyfile () const
  984     {
  985         wstring keyfilePath = AskString (_("Enter security token keyfile path: "));
  986 
  987         if (keyfilePath.empty())
  988             throw UserAbort (SRC_POS);
  989 
  990         SecurityTokenKeyfile tokenKeyfile (keyfilePath);
  991 
  992         vector <byte> keyfileData;
  993         SecurityToken::GetKeyfileData (tokenKeyfile, keyfileData);
  994 
  995         BufferPtr keyfileDataBuf (&keyfileData.front(), keyfileData.size());
  996         finally_do_arg (BufferPtr, keyfileDataBuf, { finally_arg.Erase(); });
  997 
  998         FilePath exportFilePath = AskFilePath();
  999 
 1000         if (exportFilePath.IsEmpty())
 1001             throw UserAbort (SRC_POS);
 1002 
 1003         File keyfile;
 1004         keyfile.Open (exportFilePath, File::CreateWrite);
 1005         keyfile.Write (keyfileDataBuf);
 1006     }
 1007 
 1008     shared_ptr <GetStringFunctor> TextUserInterface::GetAdminPasswordRequestHandler ()
 1009     {
 1010         struct AdminPasswordRequestHandler : public GetStringFunctor
 1011         {
 1012             AdminPasswordRequestHandler (TextUserInterface *userInterface) : UI (userInterface) { }
 1013             virtual void operator() (string &passwordStr)
 1014             {
 1015                 UI->ShowString (_("Enter your user password or administrator password: "));
 1016 
 1017                 TextUserInterface::SetTerminalEcho (false);
 1018                 finally_do ({ TextUserInterface::SetTerminalEcho (true); });
 1019 
 1020                 wstring wPassword (UI->ReadInputStreamLine());
 1021                 finally_do_arg (wstring *, &wPassword, { StringConverter::Erase (*finally_arg); });
 1022 
 1023                 UI->ShowString (L"\n");
 1024 
 1025                 StringConverter::ToSingle (wPassword, passwordStr);
 1026             }
 1027             TextUserInterface *UI;
 1028         };
 1029 
 1030         return shared_ptr <GetStringFunctor> (new AdminPasswordRequestHandler (this));
 1031     }
 1032 
 1033     void TextUserInterface::ImportSecurityTokenKeyfiles () const
 1034     {
 1035         list <SecurityTokenInfo> tokens = SecurityToken::GetAvailableTokens();
 1036 
 1037         if (tokens.empty())
 1038             throw_err (LangString ["NO_TOKENS_FOUND"]);
 1039 
 1040         CK_SLOT_ID slotId;
 1041 
 1042         if (tokens.size() == 1)
 1043         {
 1044             slotId = tokens.front().SlotId;
 1045         }
 1046         else
 1047         {
 1048             foreach (const SecurityTokenInfo &token, tokens)
 1049             {
 1050                 wstringstream tokenLabel;
 1051                 tokenLabel << L"[" << token.SlotId << L"] " << LangString["TOKEN_SLOT_ID"].c_str() << L" " << token.SlotId << L"  " << token.Label;
 1052 
 1053                 ShowInfo (tokenLabel.str());
 1054             }
 1055 
 1056             slotId = (CK_SLOT_ID) AskSelection (tokens.back().SlotId, tokens.front().SlotId);
 1057         }
 1058 
 1059         shared_ptr <KeyfileList> keyfiles = AskKeyfiles();
 1060         if (keyfiles->empty())
 1061             throw UserAbort();
 1062 
 1063         foreach_ref (const Keyfile &keyfilePath, *keyfiles)
 1064         {
 1065             File keyfile;
 1066             keyfile.Open (keyfilePath, File::OpenRead, File::ShareReadWrite, File::PreserveTimestamps);
 1067 
 1068             if (keyfile.Length() > 0)
 1069             {
 1070                 vector <byte> keyfileData (keyfile.Length());
 1071                 BufferPtr keyfileDataBuf (&keyfileData.front(), keyfileData.size());
 1072 
 1073                 keyfile.ReadCompleteBuffer (keyfileDataBuf);
 1074                 finally_do_arg (BufferPtr, keyfileDataBuf, { finally_arg.Erase(); });
 1075 
 1076                 SecurityToken::CreateKeyfile (slotId, keyfileData, string (FilePath (keyfilePath).ToBaseName()));
 1077             }
 1078             else
 1079                 throw InsufficientData (SRC_POS, FilePath (keyfilePath));
 1080         }
 1081     }
 1082 
 1083     void TextUserInterface::InitSecurityTokenLibrary () const
 1084     {
 1085         if (Preferences.SecurityTokenModule.IsEmpty())
 1086             throw_err (LangString ["NO_PKCS11_MODULE_SPECIFIED"]);
 1087 
 1088         struct PinRequestHandler : public GetPinFunctor
 1089         {
 1090             PinRequestHandler (const TextUserInterface *userInterface) : UI (userInterface) { }
 1091 
 1092             virtual void operator() (string &passwordStr)
 1093             {
 1094                 if (CmdLine->ArgTokenPin && CmdLine->ArgTokenPin->IsAllocated ())
 1095                 {
 1096                     passwordStr.clear();
 1097                     passwordStr.insert (0, (char*) CmdLine->ArgTokenPin->Ptr (), CmdLine->ArgTokenPin->Size());
 1098                     return;
 1099                 }
 1100 
 1101                 if (UI->GetPreferences().NonInteractive)
 1102                     throw MissingArgument (SRC_POS);
 1103 
 1104                 UI->ShowString (wxString::Format (LangString["ENTER_TOKEN_PASSWORD"], StringConverter::ToWide (passwordStr).c_str()) + L" ");
 1105 
 1106                 TextUserInterface::SetTerminalEcho (false);
 1107                 finally_do ({ TextUserInterface::SetTerminalEcho (true); });
 1108 
 1109                 wstring wPassword (UI->ReadInputStreamLine());
 1110                 finally_do_arg (wstring *, &wPassword, { StringConverter::Erase (*finally_arg); });
 1111 
 1112                 UI->ShowString (L"\n");
 1113 
 1114                 StringConverter::ToSingle (wPassword, passwordStr);
 1115             }
 1116 
 1117             virtual void notifyIncorrectPin ()
 1118             {
 1119                 if (CmdLine->ArgTokenPin && CmdLine->ArgTokenPin->IsAllocated ())
 1120                 {
 1121                     CmdLine->ArgTokenPin->Free ();
 1122                 }
 1123             }
 1124 
 1125             const TextUserInterface *UI;
 1126         };
 1127 
 1128         struct WarningHandler : public SendExceptionFunctor
 1129         {
 1130             WarningHandler (const TextUserInterface *userInterface) : UI (userInterface) { }
 1131 
 1132             virtual void operator() (const Exception &e)
 1133             {
 1134                 UI->ShowError (e);
 1135             }
 1136 
 1137             const TextUserInterface *UI;
 1138         };
 1139 
 1140         try
 1141         {
 1142             SecurityToken::InitLibrary (Preferences.SecurityTokenModule, auto_ptr <GetPinFunctor> (new PinRequestHandler (this)), auto_ptr <SendExceptionFunctor> (new WarningHandler (this)));
 1143         }
 1144         catch (Exception &e)
 1145         {
 1146             ShowError (e);
 1147             throw_err (LangString ["PKCS11_MODULE_INIT_FAILED"]);
 1148         }
 1149     }
 1150 
 1151     void TextUserInterface::ListSecurityTokenKeyfiles () const
 1152     {
 1153         foreach (const SecurityTokenKeyfile &keyfile, SecurityToken::GetAvailableKeyfiles())
 1154         {
 1155             ShowString (wstring (SecurityTokenKeyfilePath (keyfile)));
 1156             ShowString (L"\n");
 1157         }
 1158     }
 1159 
 1160     VolumeInfoList TextUserInterface::MountAllDeviceHostedVolumes (MountOptions &options) const
 1161     {
 1162         while (true)
 1163         {
 1164             if (!options.Password)
 1165                 options.Password = AskPassword();
 1166 
 1167             if (!options.TrueCryptMode && (options.Pim < 0))
 1168                 options.Pim = AskPim (_("Enter PIM"));
 1169 
 1170             if (!options.Keyfiles)
 1171                 options.Keyfiles = AskKeyfiles();
 1172 
 1173             VolumeInfoList mountedVolumes = UserInterface::MountAllDeviceHostedVolumes (options);
 1174 
 1175             if (!mountedVolumes.empty())
 1176                 return mountedVolumes;
 1177 
 1178             options.Password.reset();
 1179             options.Pim = -1;
 1180         }
 1181     }
 1182 
 1183     shared_ptr <VolumeInfo> TextUserInterface::MountVolume (MountOptions &options) const
 1184     {
 1185         shared_ptr <VolumeInfo> volume;
 1186 
 1187         CheckRequirementsForMountingVolume();
 1188 
 1189         // Volume path
 1190         while (!options.Path || options.Path->IsEmpty())
 1191         {
 1192             if (Preferences.NonInteractive)
 1193                 throw MissingArgument (SRC_POS);
 1194 
 1195             options.Path = AskVolumePath ();
 1196         }
 1197 
 1198         if (Core->IsVolumeMounted (*options.Path))
 1199         {
 1200             ShowInfo (StringFormatter (LangString["VOLUME_ALREADY_MOUNTED"], wstring (*options.Path)));
 1201             return volume;
 1202         }
 1203 
 1204         // Mount point
 1205         if (!options.MountPoint && !options.NoFilesystem)
 1206             options.MountPoint.reset (new DirectoryPath (AskString (_("Enter mount directory [default]: "))));
 1207 
 1208         VolumePassword password;
 1209         KeyfileList keyfiles;
 1210 
 1211         if ((!options.Password || options.Password->IsEmpty())
 1212             && (!options.Keyfiles || options.Keyfiles->empty())
 1213             && !Core->IsPasswordCacheEmpty())
 1214         {
 1215             // Cached password
 1216             try
 1217             {
 1218                 volume = UserInterface::MountVolume (options);
 1219             }
 1220             catch (PasswordException&) { }
 1221         }
 1222 
 1223         int incorrectPasswordCount = 0;
 1224 
 1225         while (!volume)
 1226         {
 1227             // Password
 1228             if (!options.Password)
 1229             {
 1230                 options.Password = AskPassword (StringFormatter (_("Enter password for {0}"), wstring (*options.Path)));
 1231             }
 1232 
 1233             if (!options.TrueCryptMode && (options.Pim < 0))
 1234             {
 1235                 options.Pim = AskPim (StringFormatter (_("Enter PIM for {0}"), wstring (*options.Path)));
 1236             }
 1237 
 1238             // Keyfiles
 1239             if (!options.Keyfiles)
 1240                 options.Keyfiles = AskKeyfiles();
 1241 
 1242             // Hidden volume protection
 1243             if (options.Protection == VolumeProtection::None
 1244                 && !CmdLine->ArgNoHiddenVolumeProtection
 1245                 && AskYesNo (_("Protect hidden volume (if any)?")))
 1246                 options.Protection = VolumeProtection::HiddenVolumeReadOnly;
 1247 
 1248             if (options.Protection == VolumeProtection::HiddenVolumeReadOnly)
 1249             {
 1250                 if (!options.ProtectionPassword)
 1251                     options.ProtectionPassword = AskPassword (_("Enter password for hidden volume"));
 1252                 if (!options.TrueCryptMode && (options.ProtectionPim < 0))
 1253                     options.ProtectionPim = AskPim (_("Enter PIM for hidden volume"));
 1254                 if (!options.ProtectionKeyfiles)
 1255                     options.ProtectionKeyfiles = AskKeyfiles (_("Enter keyfile for hidden volume"));
 1256             }
 1257 
 1258             try
 1259             {
 1260                 volume = UserInterface::MountVolume (options);
 1261             }
 1262             catch (ProtectionPasswordIncorrect &e)
 1263             {
 1264                 ShowInfo (e);
 1265                 options.ProtectionPassword.reset();
 1266                 options.ProtectionPim = -1;
 1267             }
 1268             catch (PasswordIncorrect &e)
 1269             {
 1270                 if (++incorrectPasswordCount > 2 && !options.UseBackupHeaders)
 1271                 {
 1272                     // Try to mount the volume using the backup header
 1273                     options.UseBackupHeaders = true;
 1274 
 1275                     try
 1276                     {
 1277                         volume = UserInterface::MountVolume (options);
 1278                         ShowWarning ("HEADER_DAMAGED_AUTO_USED_HEADER_BAK");
 1279                     }
 1280                     catch (...)
 1281                     {
 1282                         options.UseBackupHeaders = false;
 1283                         ShowInfo (e);
 1284                         options.Password.reset();
 1285                     }
 1286                 }
 1287                 else
 1288                 {
 1289                     ShowInfo (e);
 1290                     options.Password.reset();
 1291                 }
 1292 
 1293                 ShowString (L"\n");
 1294             }
 1295             catch (PasswordException &e)
 1296             {
 1297                 ShowInfo (e);
 1298                 options.Password.reset();
 1299             }
 1300         }
 1301 
 1302 #ifdef TC_LINUX
 1303         if (!Preferences.NonInteractive && !Preferences.DisableKernelEncryptionModeWarning
 1304             && volume->EncryptionModeName != L"XTS")
 1305         {
 1306             ShowWarning (LangString["ENCRYPTION_MODE_NOT_SUPPORTED_BY_KERNEL"]);
 1307         }
 1308 #endif
 1309 
 1310         return volume;
 1311     }
 1312 
 1313     bool TextUserInterface::OnInit ()
 1314     {
 1315         try
 1316         {
 1317             DefaultMessageOutput = new wxMessageOutputStderr;
 1318             wxMessageOutput::Set (DefaultMessageOutput);
 1319 
 1320             InterfaceType = UserInterfaceType::Text;
 1321             Init();
 1322         }
 1323         catch (exception &e)
 1324         {
 1325             ShowError (e);
 1326             return false;
 1327         }
 1328         return true;
 1329     }
 1330 
 1331     int TextUserInterface::OnRun()
 1332     {
 1333         try
 1334         {
 1335             if (ProcessCommandLine ())
 1336             {
 1337                 Application::SetExitCode (0);
 1338                 return 0;
 1339             }
 1340         }
 1341         catch (exception &e)
 1342         {
 1343             ShowError (e);
 1344         }
 1345 
 1346         Application::SetExitCode (1);
 1347         return 1;
 1348     }
 1349 
 1350     void TextUserInterface::OnSignal (int signal)
 1351     {
 1352 #ifdef TC_UNIX
 1353         try
 1354         {
 1355             SetTerminalEcho (true);
 1356         }
 1357         catch (...) { }
 1358         _exit (1);
 1359 #endif
 1360     }
 1361 
 1362     void TextUserInterface::ReadInputStreamLine (wxString &line) const
 1363     {
 1364         if (!TextInputStream.get() || feof (stdin) || ferror (stdin))
 1365             throw UserAbort (SRC_POS);
 1366 
 1367         line = TextInputStream->ReadLine();
 1368 
 1369         if (ferror (stdin) || (line.empty() && feof (stdin)))
 1370             throw UserAbort (SRC_POS);
 1371     }
 1372 
 1373     wxString TextUserInterface::ReadInputStreamLine () const
 1374     {
 1375         wxString line;
 1376         ReadInputStreamLine (line);
 1377         return line;
 1378     }
 1379 
 1380     void TextUserInterface::RestoreVolumeHeaders (shared_ptr <VolumePath> volumePath) const
 1381     {
 1382         if (!volumePath)
 1383             volumePath = AskVolumePath();
 1384 
 1385         if (!volumePath)
 1386             throw UserAbort (SRC_POS);
 1387 
 1388 #ifdef TC_WINDOWS
 1389         if (Core->IsVolumeMounted (*volumePath))
 1390             throw_err (LangString["DISMOUNT_FIRST"]);
 1391 #endif
 1392 
 1393         // Ask whether to restore internal or external backup
 1394         bool restoreInternalBackup;
 1395         shared_ptr <Pkcs5Kdf> kdf;
 1396         if (CmdLine->ArgHash)
 1397         {
 1398             kdf = Pkcs5Kdf::GetAlgorithm (*CmdLine->ArgHash, false);
 1399         }
 1400 
 1401         ShowInfo (LangString["HEADER_RESTORE_EXTERNAL_INTERNAL"]);
 1402         ShowInfo (L"\n1) " + LangString["HEADER_RESTORE_INTERNAL"]);
 1403         ShowInfo (L"2) " + LangString["HEADER_RESTORE_EXTERNAL"] + L"\n");
 1404 
 1405         switch (AskSelection (2))
 1406         {
 1407         case 1:
 1408             restoreInternalBackup = true;
 1409             break;
 1410 
 1411         case 2:
 1412             restoreInternalBackup = false;
 1413             break;
 1414 
 1415         default:
 1416             throw UserAbort (SRC_POS);
 1417         }
 1418 
 1419         /* force the display of the random enriching interface */
 1420         RandomNumberGenerator::SetEnrichedByUserStatus (false);
 1421 
 1422         if (restoreInternalBackup)
 1423         {
 1424             // Restore header from the internal backup
 1425             shared_ptr <Volume> volume;
 1426             MountOptions options;
 1427             options.Path = volumePath;
 1428 
 1429             while (!volume)
 1430             {
 1431                 ShowString (L"\n");
 1432                 options.Password = AskPassword();
 1433                 options.Pim = AskPim();
 1434                 options.Keyfiles = AskKeyfiles();
 1435 
 1436                 try
 1437                 {
 1438                     volume = Core->OpenVolume (
 1439                         options.Path,
 1440                         options.PreserveTimestamps,
 1441                         options.Password,
 1442                         options.Pim,
 1443                         kdf,
 1444                         false,
 1445                         options.Keyfiles,
 1446                         options.Protection,
 1447                         options.ProtectionPassword,
 1448                         options.ProtectionPim,
 1449                         options.ProtectionKdf,
 1450                         options.ProtectionKeyfiles,
 1451                         options.SharedAccessAllowed,
 1452                         VolumeType::Unknown,
 1453                         true
 1454                         );
 1455                 }
 1456                 catch (PasswordException &e)
 1457                 {
 1458                     ShowInfo (e);
 1459                 }
 1460             }
 1461 
 1462             shared_ptr <VolumeLayout> layout = volume->GetLayout();
 1463             if (typeid (*layout) == typeid (VolumeLayoutV1Normal))
 1464             {
 1465                 throw_err (LangString ["VOLUME_HAS_NO_BACKUP_HEADER"]);
 1466             }
 1467 
 1468             RandomNumberGenerator::Start();
 1469             UserEnrichRandomPool();
 1470 
 1471             // Re-encrypt volume header
 1472             SecureBuffer newHeaderBuffer (volume->GetLayout()->GetHeaderSize());
 1473             Core->ReEncryptVolumeHeaderWithNewSalt (newHeaderBuffer, volume->GetHeader(), options.Password, options.Pim,  options.Keyfiles);
 1474 
 1475             // Write volume header
 1476             int headerOffset = volume->GetLayout()->GetHeaderOffset();
 1477             shared_ptr <File> volumeFile = volume->GetFile();
 1478 
 1479             if (headerOffset >= 0)
 1480                 volumeFile->SeekAt (headerOffset);
 1481             else
 1482                 volumeFile->SeekEnd (headerOffset);
 1483 
 1484             volumeFile->Write (newHeaderBuffer);
 1485         }
 1486         else
 1487         {
 1488             // Restore header from an external backup
 1489 
 1490             wxString confirmMsg = L"\n\n" + LangString["CONFIRM_VOL_HEADER_RESTORE"];
 1491 
 1492             if (!AskYesNo (wxString::Format (confirmMsg, wstring (*volumePath).c_str()), true, true))
 1493                 return;
 1494 
 1495             ShowString (L"\n");
 1496 
 1497             FilePath filePath = AskFilePath();
 1498             if (filePath.IsEmpty())
 1499                 throw UserAbort (SRC_POS);
 1500 
 1501             File backupFile;
 1502             backupFile.Open (filePath, File::OpenRead);
 1503 
 1504             bool legacyBackup;
 1505 
 1506             // Determine the format of the backup file
 1507             switch (backupFile.Length())
 1508             {
 1509             case TC_VOLUME_HEADER_GROUP_SIZE:
 1510                 legacyBackup = false;
 1511                 break;
 1512 
 1513             case TC_VOLUME_HEADER_SIZE_LEGACY * 2:
 1514                 legacyBackup = true;
 1515                 break;
 1516 
 1517             default:
 1518                 throw_err (LangString ["HEADER_BACKUP_SIZE_INCORRECT"]);
 1519             }
 1520 
 1521             // Open the volume header stored in the backup file
 1522             MountOptions options;
 1523 
 1524             shared_ptr <VolumeLayout> decryptedLayout;
 1525 
 1526             while (!decryptedLayout)
 1527             {
 1528                 options.Password = AskPassword (L"\n" + LangString["ENTER_HEADER_BACKUP_PASSWORD"]);
 1529                 options.Pim = AskPim (_("Enter PIM"));
 1530                 options.Keyfiles = AskKeyfiles();
 1531 
 1532                 try
 1533                 {
 1534                     // Test volume layouts
 1535                     foreach (shared_ptr <VolumeLayout> layout, VolumeLayout::GetAvailableLayouts ())
 1536                     {
 1537                         if (layout->HasDriveHeader())
 1538                             continue;
 1539 
 1540                         if (!legacyBackup && (typeid (*layout) == typeid (VolumeLayoutV1Normal)))
 1541                             continue;
 1542 
 1543                         if (legacyBackup && (typeid (*layout) == typeid (VolumeLayoutV2Normal) || typeid (*layout) == typeid (VolumeLayoutV2Hidden)))
 1544                             continue;
 1545 
 1546                         SecureBuffer headerBuffer (layout->GetHeaderSize());
 1547                         backupFile.ReadAt (headerBuffer, layout->GetType() == VolumeType::Hidden ? layout->GetHeaderSize() : 0);
 1548 
 1549                         // Decrypt header
 1550                         shared_ptr <VolumePassword> passwordKey = Keyfile::ApplyListToPassword (options.Keyfiles, options.Password);
 1551                         if (layout->GetHeader()->Decrypt (headerBuffer, *passwordKey, options.Pim, kdf, false, layout->GetSupportedKeyDerivationFunctions(false), layout->GetSupportedEncryptionAlgorithms(), layout->GetSupportedEncryptionModes()))
 1552                         {
 1553                             decryptedLayout = layout;
 1554                             break;
 1555                         }
 1556                     }
 1557 
 1558                     if (!decryptedLayout)
 1559                         throw PasswordIncorrect (SRC_POS);
 1560                 }
 1561                 catch (PasswordException &e)
 1562                 {
 1563                     ShowWarning (e);
 1564                 }
 1565             }
 1566 
 1567             File volumeFile;
 1568             volumeFile.Open (*volumePath, File::OpenReadWrite, File::ShareNone, File::PreserveTimestamps);
 1569 
 1570             RandomNumberGenerator::Start();
 1571             UserEnrichRandomPool();
 1572 
 1573             // Re-encrypt volume header
 1574             SecureBuffer newHeaderBuffer (decryptedLayout->GetHeaderSize());
 1575             Core->ReEncryptVolumeHeaderWithNewSalt (newHeaderBuffer, decryptedLayout->GetHeader(), options.Password, options.Pim, options.Keyfiles);
 1576 
 1577             // Write volume header
 1578             int headerOffset = decryptedLayout->GetHeaderOffset();
 1579             if (headerOffset >= 0)
 1580                 volumeFile.SeekAt (headerOffset);
 1581             else
 1582                 volumeFile.SeekEnd (headerOffset);
 1583 
 1584             volumeFile.Write (newHeaderBuffer);
 1585 
 1586             if (decryptedLayout->HasBackupHeader())
 1587             {
 1588                 // Re-encrypt backup volume header
 1589                 Core->ReEncryptVolumeHeaderWithNewSalt (newHeaderBuffer, decryptedLayout->GetHeader(), options.Password, options.Pim, options.Keyfiles);
 1590 
 1591                 // Write backup volume header
 1592                 headerOffset = decryptedLayout->GetBackupHeaderOffset();
 1593                 if (headerOffset >= 0)
 1594                     volumeFile.SeekAt (headerOffset);
 1595                 else
 1596                     volumeFile.SeekEnd (headerOffset);
 1597 
 1598                 volumeFile.Write (newHeaderBuffer);
 1599             }
 1600         }
 1601 
 1602         ShowString (L"\n");
 1603         ShowInfo ("VOL_HEADER_RESTORED");
 1604     }
 1605 
 1606     void TextUserInterface::SetTerminalEcho (bool enable)
 1607     {
 1608         if (CmdLine->ArgDisplayPassword)
 1609             return;
 1610 
 1611 #ifdef TC_UNIX
 1612         struct termios termAttr;
 1613         if (tcgetattr (0, &termAttr) == 0)
 1614         {
 1615             if (!enable)
 1616             {
 1617                 termAttr.c_lflag &= ~ECHO;
 1618                 throw_sys_if (tcsetattr (0, TCSANOW, &termAttr) != 0);
 1619             }
 1620             else
 1621             {
 1622                 termAttr.c_lflag |= ECHO;
 1623                 throw_sys_if (tcsetattr (0, TCSANOW, &termAttr) != 0);
 1624             }
 1625         }
 1626 #endif
 1627     }
 1628 
 1629     void TextUserInterface::UserEnrichRandomPool () const
 1630     {
 1631         RandomNumberGenerator::Start();
 1632 
 1633         if (RandomNumberGenerator::IsEnrichedByUser())
 1634             return;
 1635 
 1636         if (CmdLine->ArgHash)
 1637             RandomNumberGenerator::SetHash (CmdLine->ArgHash);
 1638 
 1639         if (!CmdLine->ArgRandomSourcePath.IsEmpty())
 1640         {
 1641             SecureBuffer buffer (RandomNumberGenerator::PoolSize);
 1642             File randSourceFile;
 1643 
 1644             randSourceFile.Open (CmdLine->ArgRandomSourcePath, File::OpenRead);
 1645 
 1646             for (size_t i = 0; i < buffer.Size(); ++i)
 1647             {
 1648                 if (randSourceFile.Read (buffer.GetRange (i, 1)) < 1)
 1649                     break;
 1650             }
 1651 
 1652             RandomNumberGenerator::AddToPool (buffer);
 1653             RandomNumberGenerator::SetEnrichedByUserStatus (true);
 1654         }
 1655         else if (!Preferences.NonInteractive)
 1656         {
 1657             int randCharsRequired = RandomNumberGenerator::PoolSize;
 1658             ShowInfo (StringFormatter (_("\nPlease type at least {0} randomly chosen characters and then press Enter:"), randCharsRequired));
 1659 
 1660             SetTerminalEcho (false);
 1661             finally_do ({ TextUserInterface::SetTerminalEcho (true); });
 1662 
 1663             while (randCharsRequired > 0)
 1664             {
 1665                 wstring randStr = AskString();
 1666                 RandomNumberGenerator::AddToPool (ConstBufferPtr ((byte *) randStr.c_str(), randStr.size() * sizeof (wchar_t)));
 1667 
 1668                 randCharsRequired -= randStr.size();
 1669 
 1670                 if (randCharsRequired > 0)
 1671                     ShowInfo (StringFormatter (_("Characters remaining: {0}"), randCharsRequired));
 1672             }
 1673 
 1674             ShowString (L"\n");
 1675             RandomNumberGenerator::SetEnrichedByUserStatus (true);
 1676         }
 1677     }
 1678 
 1679     wxMessageOutput *DefaultMessageOutput;
 1680 }