"Fossies" - the Fresh Open Source Software Archive 
Member "encfs-1.9.5/encfs/FileUtils.cpp" (27 Apr 2018, 55684 Bytes) of package /linux/misc/encfs-1.9.5.tar.gz:
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 "FileUtils.cpp" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
1.9.4_vs_1.9.5.
1 /*****************************************************************************
2 * Author: Valient Gough <vgough@pobox.com>
3 *
4 *****************************************************************************
5 * Copyright (c) 2004, Valient Gough
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
15 * for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 // defines needed for RedHat 7.3...
22 #ifdef __linux__
23 #define _XOPEN_SOURCE 500 // make sure pwrite() is pulled in
24 #endif
25 #define _BSD_SOURCE // pick up setenv on RH7.3
26 #define _DEFAULT_SOURCE // Replaces _BSD_SOURCE
27
28 #include "easylogging++.h"
29 #include <cctype>
30 #include <cerrno>
31 #include <cstdio>
32 #include <cstdlib>
33 #include <cstring>
34 #include <fcntl.h>
35 #include <fstream>
36 #include <iostream>
37 #include <list>
38 #ifdef __APPLE__
39 #include <sys/mount.h>
40 #include <sys/param.h>
41 #endif
42 #include <sys/socket.h>
43 #include <sys/stat.h>
44 #include <sys/wait.h>
45 #include <tinyxml2.h>
46 #include <unistd.h>
47 #include <vector>
48
49 #include "BlockNameIO.h"
50 #include "Cipher.h"
51 #include "CipherKey.h"
52 #include "ConfigReader.h"
53 #include "ConfigVar.h"
54 #include "Context.h"
55 #include "DirNode.h"
56 #include "Error.h"
57 #include "FSConfig.h"
58 #include "FileUtils.h"
59 #include "Interface.h"
60 #include "NameIO.h"
61 #include "Range.h"
62 #include "XmlReader.h"
63 #include "autosprintf.h"
64 #include "base64.h"
65 #include "config.h"
66 #include "i18n.h"
67 #include "intl/gettext.h"
68 #include "readpassphrase.h"
69
70 using namespace std;
71 using gnu::autosprintf;
72
73 namespace encfs {
74
75 static const int DefaultBlockSize = 1024;
76 // The maximum length of text passwords. If longer are needed,
77 // use the extpass option, as extpass can return arbitrary length binary data.
78 static const int MaxPassBuf = 512;
79
80 static const int NormalKDFDuration = 500; // 1/2 a second
81 static const int ParanoiaKDFDuration = 3000; // 3 seconds
82
83 // environment variable names for values encfs stores in the environment when
84 // calling an external password program.
85 static const char ENCFS_ENV_ROOTDIR[] = "encfs_root";
86 static const char ENCFS_ENV_STDOUT[] = "encfs_stdout";
87 static const char ENCFS_ENV_STDERR[] = "encfs_stderr";
88
89 // static int V5SubVersion = 20040518;
90 // static int V5SubVersion = 20040621; // add external IV chaining
91 static int V5SubVersion = 20040813; // fix MACFileIO block size issues
92 static int V5SubVersionDefault = 0;
93
94 // 20080813 was really made on 20080413 -- typo on date..
95 // const int V6SubVersion = 20080813; // switch to v6/XML, add allowHoles option
96 // const int V6SubVersion = 20080816; // add salt and iteration count
97 /*
98 * In boost 1.42+, serial numbers change to 8 bit, which means the date
99 * numbering scheme does not work any longer.
100 * boost-versioning.h implements a workaround that sets the version to
101 * 20 for boost 1.42+. */
102 const int V6SubVersion = 20100713; // add version field for boost 1.42+
103
104 struct ConfigInfo {
105 const char *fileName;
106 ConfigType type;
107 const char *environmentOverride;
108 bool (*loadFunc)(const char *fileName, EncFSConfig *config, ConfigInfo *cfg);
109 bool (*saveFunc)(const char *fileName, const EncFSConfig *config);
110 int currentSubVersion;
111 int defaultSubVersion;
112 } ConfigFileMapping[] = {
113 // current format
114 {".encfs6.xml", Config_V6, "ENCFS6_CONFIG", readV6Config, writeV6Config,
115 V6SubVersion, 0},
116 // backward compatible support for older versions
117 {".encfs5", Config_V5, "ENCFS5_CONFIG", readV5Config, writeV5Config,
118 V5SubVersion, V5SubVersionDefault},
119 {".encfs4", Config_V4, nullptr, readV4Config, writeV4Config, 0, 0},
120 // no longer support earlier versions
121 {".encfs3", Config_V3, nullptr, nullptr, nullptr, 0, 0},
122 {".encfs2", Config_Prehistoric, nullptr, nullptr, nullptr, 0, 0},
123 {".encfs", Config_Prehistoric, nullptr, nullptr, nullptr, 0, 0},
124 {nullptr, Config_None, nullptr, nullptr, nullptr, 0, 0}};
125
126 EncFS_Root::EncFS_Root() = default;
127
128 EncFS_Root::~EncFS_Root() = default;
129
130 bool fileExists(const char *fileName) {
131 struct stat buf;
132 return lstat(fileName, &buf) == 0;
133 }
134
135 bool isDirectory(const char *fileName) {
136 struct stat buf;
137 if (lstat(fileName, &buf) == 0) {
138 return S_ISDIR(buf.st_mode);
139 }
140 return false;
141 }
142
143 bool isAbsolutePath(const char *fileName) {
144 return (fileName != nullptr) && fileName[0] != '\0' && fileName[0] == '/';
145 }
146
147 const char *lastPathElement(const char *name) {
148 const char *loc = strrchr(name, '/');
149 return loc != nullptr ? loc + 1 : name;
150 }
151
152 std::string parentDirectory(const std::string &path) {
153 size_t last = path.find_last_of('/');
154 if (last == string::npos) {
155 return string("");
156 }
157 return path.substr(0, last);
158 }
159
160 bool userAllowMkdir(const char *path, mode_t mode) {
161 return userAllowMkdir(0, path, mode);
162 }
163
164 bool userAllowMkdir(int promptno, const char *path, mode_t mode) {
165 // TODO: can we internationalize the y/n names? Seems strange to prompt in
166 // their own language but then have to respond 'y' or 'n'.
167 // xgroup(setup)
168 cerr << autosprintf(
169 _("The directory \"%s\" does not exist. Should it be created? "
170 "(y,N) "),
171 path);
172 char answer[10];
173 char *res;
174
175 switch (promptno) {
176 case 1:
177 cerr << endl << "$PROMPT$ create_root_dir" << endl;
178 break;
179 case 2:
180 cerr << endl << "$PROMPT$ create_mount_point" << endl;
181 break;
182 default:
183 break;
184 }
185 res = fgets(answer, sizeof(answer), stdin);
186
187 if (res != nullptr && toupper(answer[0]) == 'Y') {
188 int result = mkdir(path, mode);
189 if (result < 0) {
190 perror(_("Unable to create directory: "));
191 return false;
192 }
193 return true;
194 }
195 // Directory not created, by user request
196 cerr << _("Directory not created.") << "\n";
197 return false;
198 }
199
200 /**
201 * Load config file by calling the load function on the filename
202 */
203 ConfigType readConfig_load(ConfigInfo *nm, const char *path,
204 EncFSConfig *config) {
205 if (nm->loadFunc != nullptr) {
206 try {
207 if ((*nm->loadFunc)(path, config, nm)) {
208 config->cfgType = nm->type;
209 return nm->type;
210 }
211 } catch (encfs::Error &err) {
212 RLOG(ERROR) << "readConfig error: " << err.what();
213 }
214
215 RLOG(ERROR) << "Found config file " << path
216 << ", but failed to load - exiting";
217 exit(1);
218 } else {
219 // No load function - must be an unsupported type..
220 config->cfgType = nm->type;
221 return nm->type;
222 }
223 }
224
225 /**
226 * Try to locate the config file
227 * Tries the most recent format first, then looks for older versions
228 */
229 ConfigType readConfig(const string &rootDir, EncFSConfig *config, const string &cmdConfig) {
230 ConfigInfo *nm = ConfigFileMapping;
231 while (nm->fileName != nullptr) {
232 // allow command line argument to override default config path
233 if (!cmdConfig.empty()) {
234 if (!fileExists(cmdConfig.c_str())) {
235 RLOG(ERROR)
236 << "fatal: config file specified on command line does not exist: "
237 << cmdConfig;
238 exit(1);
239 }
240 return readConfig_load(nm, cmdConfig.c_str(), config);
241 } // allow environment variable to override default config path
242 if (nm->environmentOverride != nullptr) {
243 char *envFile = getenv(nm->environmentOverride);
244 if (envFile != nullptr) {
245 if (!fileExists(envFile)) {
246 RLOG(ERROR)
247 << "fatal: config file specified by environment does not exist: "
248 << envFile;
249 exit(1);
250 }
251 return readConfig_load(nm, envFile, config);
252 }
253 }
254 // the standard place to look is in the root directory
255 string path = rootDir + nm->fileName;
256 if (fileExists(path.c_str())) {
257 return readConfig_load(nm, path.c_str(), config);
258 }
259
260 ++nm;
261 }
262
263 return Config_None;
264 }
265
266 /**
267 * Read config file in current "V6" XML format, normally named ".encfs6.xml"
268 * This format is in use since Apr 13, 2008 (commit 6d081f5c)
269 */
270 // Read a boost::serialization config file using an Xml reader..
271 bool readV6Config(const char *configFile, EncFSConfig *cfg, ConfigInfo *info) {
272 (void)info;
273
274 XmlReader rdr;
275 if (!rdr.load(configFile)) {
276 RLOG(ERROR) << "Failed to load config file " << configFile;
277 return false;
278 }
279
280 XmlValuePtr serialization = rdr["boost_serialization"];
281 XmlValuePtr config = (*serialization)["cfg"];
282 if (!config) {
283 config = (*serialization)["config"];
284 }
285 if (!config) {
286 RLOG(ERROR) << "Unable to find XML configuration in file " << configFile;
287 return false;
288 }
289
290 int version;
291 if (!config->read("version", &version) &&
292 !config->read("@version", &version)) {
293 RLOG(ERROR) << "Unable to find version in config file";
294 return false;
295 }
296
297 // version numbering was complicated by boost::archive
298 if (version == 20 || version >= 20100713) {
299 VLOG(1) << "found new serialization format";
300 cfg->subVersion = version;
301 } else if (version == 26800) {
302 VLOG(1) << "found 20080816 version";
303 cfg->subVersion = 20080816;
304 } else if (version == 26797) {
305 VLOG(1) << "found 20080813";
306 cfg->subVersion = 20080813;
307 } else if (version < V5SubVersion) {
308 RLOG(ERROR) << "Invalid version " << version << " - please fix config file";
309 } else {
310 VLOG(1) << "Boost <= 1.41 compatibility mode";
311 cfg->subVersion = version;
312 }
313 VLOG(1) << "subVersion = " << cfg->subVersion;
314
315 config->read("creator", &cfg->creator);
316 config->read("cipherAlg", &cfg->cipherIface);
317 config->read("nameAlg", &cfg->nameIface);
318
319 config->read("keySize", &cfg->keySize);
320
321 config->read("blockSize", &cfg->blockSize);
322 config->read("plainData", &cfg->plainData);
323 config->read("uniqueIV", &cfg->uniqueIV);
324 config->read("chainedNameIV", &cfg->chainedNameIV);
325 config->read("externalIVChaining", &cfg->externalIVChaining);
326 config->read("blockMACBytes", &cfg->blockMACBytes);
327 config->read("blockMACRandBytes", &cfg->blockMACRandBytes);
328 config->read("allowHoles", &cfg->allowHoles);
329
330 int encodedSize;
331 config->read("encodedKeySize", &encodedSize);
332 auto *key = new unsigned char[encodedSize];
333 config->readB64("encodedKeyData", key, encodedSize);
334 cfg->assignKeyData(key, encodedSize);
335 delete[] key;
336
337 if (cfg->subVersion >= 20080816) {
338 int saltLen;
339 config->read("saltLen", &saltLen);
340 auto *salt = new unsigned char[saltLen];
341 config->readB64("saltData", salt, saltLen);
342 cfg->assignSaltData(salt, saltLen);
343 delete[] salt;
344
345 config->read("kdfIterations", &cfg->kdfIterations);
346 config->read("desiredKDFDuration", &cfg->desiredKDFDuration);
347 } else {
348 cfg->kdfIterations = 16;
349 cfg->desiredKDFDuration = NormalKDFDuration;
350 }
351
352 return true;
353 }
354
355 /**
356 * Read config file in deprecated "V5" format, normally named ".encfs5"
357 * This format has been used before Apr 13, 2008
358 */
359 bool readV5Config(const char *configFile, EncFSConfig *config,
360 ConfigInfo *info) {
361 bool ok = false;
362
363 // use Config to parse the file and query it..
364 ConfigReader cfgRdr;
365 if (cfgRdr.load(configFile)) {
366 try {
367 config->subVersion =
368 cfgRdr["subVersion"].readInt(info->defaultSubVersion);
369 if (config->subVersion > info->currentSubVersion) {
370 /* config file specifies a version outside our supported
371 range.. */
372 RLOG(WARNING) << "Config subversion " << config->subVersion
373 << " found, which is newer than supported version "
374 << info->currentSubVersion;
375 return false;
376 }
377 if (config->subVersion < 20040813) {
378 RLOG(ERROR) << "This version of EncFS doesn't support "
379 "filesystems created before 2004-08-13";
380 return false;
381 }
382
383 cfgRdr["creator"] >> config->creator;
384 cfgRdr["cipher"] >> config->cipherIface;
385 cfgRdr["naming"] >> config->nameIface;
386 cfgRdr["keySize"] >> config->keySize;
387 cfgRdr["blockSize"] >> config->blockSize;
388
389 string data;
390 cfgRdr["keyData"] >> data;
391 config->assignKeyData(data);
392 config->uniqueIV = cfgRdr["uniqueIV"].readBool(false);
393 config->chainedNameIV = cfgRdr["chainedIV"].readBool(false);
394 config->externalIVChaining = cfgRdr["externalIV"].readBool(false);
395 config->blockMACBytes = cfgRdr["blockMACBytes"].readInt(0);
396 config->blockMACRandBytes = cfgRdr["blockMACRandBytes"].readInt(0);
397
398 ok = true;
399 } catch (encfs::Error &err) {
400 RLOG(WARNING) << err.what();
401 VLOG(1) << "Error parsing data in config file " << configFile;
402 ok = false;
403 }
404 }
405
406 return ok;
407 }
408
409 /**
410 * Read config file in deprecated "V4" format, normally named ".encfs4"
411 * This format has been used before Jan 7, 2008
412 */
413 bool readV4Config(const char *configFile, EncFSConfig *config,
414 ConfigInfo *info) {
415 bool ok = false;
416
417 // use Config to parse the file and query it..
418 ConfigReader cfgRdr;
419 if (cfgRdr.load(configFile)) {
420 try {
421 cfgRdr["cipher"] >> config->cipherIface;
422 cfgRdr["keySize"] >> config->keySize;
423 cfgRdr["blockSize"] >> config->blockSize;
424 string data;
425 cfgRdr["keyData"] >> data;
426 config->assignKeyData(data);
427
428 // fill in default for V4
429 config->nameIface = Interface("nameio/stream", 1, 0, 0);
430 config->creator = "EncFS 1.0.x";
431 config->subVersion = info->defaultSubVersion;
432 config->blockMACBytes = 0;
433 config->blockMACRandBytes = 0;
434 config->uniqueIV = false;
435 config->externalIVChaining = false;
436 config->chainedNameIV = false;
437
438 ok = true;
439 } catch (encfs::Error &err) {
440 RLOG(WARNING) << err.what();
441 VLOG(1) << "Error parsing config file " << configFile;
442 ok = false;
443 }
444 }
445
446 return ok;
447 }
448
449 bool saveConfig(ConfigType type, const string &rootDir,
450 const EncFSConfig *config, const string &cmdConfig) {
451 bool ok = false;
452
453 ConfigInfo *nm = ConfigFileMapping;
454 while (nm->fileName != nullptr) {
455 if (nm->type == type && (nm->saveFunc != nullptr)) {
456 string path = rootDir + nm->fileName;
457 if (!cmdConfig.empty()) {
458 // use command line argument if specified
459 path.assign(cmdConfig);
460 }
461 else if (nm->environmentOverride != nullptr) {
462 // use environment file if specified..
463 const char *envFile = getenv(nm->environmentOverride);
464 if (envFile != nullptr) {
465 path.assign(envFile);
466 }
467 }
468
469 try {
470 ok = (*nm->saveFunc)(path.c_str(), config);
471 } catch (encfs::Error &err) {
472 RLOG(WARNING) << err.what();
473 ok = false;
474 }
475 break;
476 }
477 ++nm;
478 }
479
480 return ok;
481 }
482
483 template <typename T>
484 tinyxml2::XMLElement *addEl(tinyxml2::XMLDocument &doc,
485 tinyxml2::XMLNode *parent, const char *name,
486 const T &value) {
487 auto el = doc.NewElement(name);
488 el->SetText(value);
489 parent->InsertEndChild(el);
490 return el;
491 }
492
493 template <>
494 tinyxml2::XMLElement *addEl<>(tinyxml2::XMLDocument &doc,
495 tinyxml2::XMLNode *parent, const char *name,
496 const Interface &iface) {
497 auto el = doc.NewElement(name);
498
499 auto n = doc.NewElement("name");
500 n->SetText(iface.name().c_str());
501 el->InsertEndChild(n);
502
503 auto major = doc.NewElement("major");
504 major->SetText(iface.current());
505 el->InsertEndChild(major);
506
507 auto minor = doc.NewElement("minor");
508 minor->SetText(iface.revision());
509 el->InsertEndChild(minor);
510
511 parent->InsertEndChild(el);
512 return el;
513 }
514
515 template <>
516 tinyxml2::XMLElement *addEl<>(tinyxml2::XMLDocument &doc,
517 tinyxml2::XMLNode *parent, const char *name,
518 const std::vector<unsigned char> &data) {
519 string v = string("\n") + B64StandardEncode(data) + "\n";
520 return addEl(doc, parent, name, v.c_str());
521 }
522
523 bool writeV6Config(const char *configFile, const EncFSConfig *cfg) {
524 tinyxml2::XMLDocument doc;
525
526 // Various static tags are included to make the output compatible with
527 // older boost-based readers.
528 doc.InsertEndChild(doc.NewDeclaration(nullptr));
529 doc.InsertEndChild(doc.NewUnknown("DOCTYPE boost_serialization"));
530
531 auto header = doc.NewElement("boost_serialization");
532 header->SetAttribute("signature", "serialization::archive");
533 header->SetAttribute("version", "7");
534 doc.InsertEndChild(header);
535
536 auto config = doc.NewElement("cfg");
537 config->SetAttribute("class_id", "0");
538 config->SetAttribute("tracking_level", "0");
539 config->SetAttribute("version", "20");
540 header->InsertEndChild(config);
541
542 addEl(doc, config, "version", V6SubVersion);
543 addEl(doc, config, "creator", cfg->creator.c_str());
544 auto cipherAlg = addEl(doc, config, "cipherAlg", cfg->cipherIface);
545 cipherAlg->SetAttribute("class_id", "1");
546 cipherAlg->SetAttribute("tracking_level", "0");
547 cipherAlg->SetAttribute("version", "0");
548 addEl(doc, config, "nameAlg", cfg->nameIface);
549 addEl(doc, config, "keySize", cfg->keySize);
550 addEl(doc, config, "blockSize", cfg->blockSize);
551 addEl(doc, config, "plainData", (int)cfg->plainData);
552 addEl(doc, config, "uniqueIV", (int)cfg->uniqueIV);
553 addEl(doc, config, "chainedNameIV", (int)cfg->chainedNameIV);
554 addEl(doc, config, "externalIVChaining", (int)cfg->externalIVChaining);
555 addEl(doc, config, "blockMACBytes", cfg->blockMACBytes);
556 addEl(doc, config, "blockMACRandBytes", cfg->blockMACRandBytes);
557 addEl(doc, config, "allowHoles", (int)cfg->allowHoles);
558 addEl(doc, config, "encodedKeySize", (int)cfg->keyData.size());
559 addEl(doc, config, "encodedKeyData", cfg->keyData);
560 addEl(doc, config, "saltLen", (int)cfg->salt.size());
561 addEl(doc, config, "saltData", cfg->salt);
562 addEl(doc, config, "kdfIterations", cfg->kdfIterations);
563 addEl(doc, config, "desiredKDFDuration", (int)cfg->desiredKDFDuration);
564
565 auto err = doc.SaveFile(configFile, false);
566 return err == tinyxml2::XML_SUCCESS;
567 }
568
569 bool writeV5Config(const char *configFile, const EncFSConfig *config) {
570 ConfigReader cfg;
571
572 cfg["creator"] << config->creator;
573 cfg["subVersion"] << config->subVersion;
574 cfg["cipher"] << config->cipherIface;
575 cfg["naming"] << config->nameIface;
576 cfg["keySize"] << config->keySize;
577 cfg["blockSize"] << config->blockSize;
578 string key;
579 key.assign((char *)config->getKeyData(), config->keyData.size());
580 cfg["keyData"] << key;
581 cfg["blockMACBytes"] << config->blockMACBytes;
582 cfg["blockMACRandBytes"] << config->blockMACRandBytes;
583 cfg["uniqueIV"] << config->uniqueIV;
584 cfg["chainedIV"] << config->chainedNameIV;
585 cfg["externalIV"] << config->externalIVChaining;
586
587 return cfg.save(configFile);
588 }
589
590 bool writeV4Config(const char *configFile, const EncFSConfig *config) {
591 ConfigReader cfg;
592
593 cfg["cipher"] << config->cipherIface;
594 cfg["keySize"] << config->keySize;
595 cfg["blockSize"] << config->blockSize;
596 string key;
597 key.assign((char *)config->getKeyData(), config->keyData.size());
598 cfg["keyData"] << key;
599
600 return cfg.save(configFile);
601 }
602
603 static Cipher::CipherAlgorithm findCipherAlgorithm(const char *name,
604 int keySize) {
605 Cipher::AlgorithmList algorithms = Cipher::GetAlgorithmList();
606 Cipher::AlgorithmList::const_iterator it;
607 for (it = algorithms.begin(); it != algorithms.end(); ++it) {
608 if ((strcmp(name, it->name.c_str()) == 0) &&
609 it->keyLength.allowed(keySize)) {
610 return *it;
611 }
612 }
613
614 Cipher::CipherAlgorithm result;
615 return result;
616 }
617
618 /**
619 * Ask the user which cipher to use
620 */
621 static Cipher::CipherAlgorithm selectCipherAlgorithm() {
622 for (;;) {
623 // figure out what cipher they want to use..
624 // xgroup(setup)
625 cout << _("The following cipher algorithms are available:") << "\n";
626 Cipher::AlgorithmList algorithms = Cipher::GetAlgorithmList();
627 Cipher::AlgorithmList::const_iterator it;
628 int optNum = 1;
629 for (it = algorithms.begin(); it != algorithms.end(); ++it, ++optNum) {
630 cout << optNum << ". " << it->name << " : "
631 << gettext(it->description.c_str()) << "\n";
632 if (it->keyLength.min() == it->keyLength.max()) {
633 // shown after algorithm name and description.
634 // xgroup(setup)
635 cout << autosprintf(_(" -- key length %i bits"), it->keyLength.min())
636 << "\n";
637 } else {
638 cout << autosprintf(
639 // shown after algorithm name and description.
640 // xgroup(setup)
641 _(" -- Supports key lengths of %i to %i bits"),
642 it->keyLength.min(), it->keyLength.max())
643 << "\n";
644 }
645
646 if (it->blockSize.min() == it->blockSize.max()) {
647 cout << autosprintf(
648 // shown after algorithm name and description.
649 // xgroup(setup)
650 _(" -- block size %i bytes"), it->blockSize.min())
651 << "\n";
652 } else {
653 cout << autosprintf(
654 // shown after algorithm name and description.
655 // xgroup(setup)
656 _(" -- Supports block sizes of %i to %i bytes"),
657 it->blockSize.min(), it->blockSize.max())
658 << "\n";
659 }
660 }
661
662 // xgroup(setup)
663 cout << "\n" << _("Enter the number corresponding to your choice: ");
664 char answer[10];
665 char *res = fgets(answer, sizeof(answer), stdin);
666 int cipherNum = (res == nullptr ? 0 : (int)strtol(answer, nullptr, 10));
667 cout << "\n";
668
669 if (cipherNum < 1 || cipherNum > (int)algorithms.size()) {
670 cerr << _("Invalid selection.") << "\n";
671 continue;
672 }
673
674 it = algorithms.begin();
675 while (--cipherNum != 0) { // numbering starts at 1
676 ++it;
677 }
678
679 Cipher::CipherAlgorithm alg = *it;
680
681 // xgroup(setup)
682 cout << autosprintf(_("Selected algorithm \"%s\""), alg.name.c_str())
683 << "\n\n";
684
685 return alg;
686 }
687 }
688
689 /**
690 * Ask the user which encoding to use for file names
691 */
692 static Interface selectNameCoding() {
693 for (;;) {
694 // figure out what cipher they want to use..
695 // xgroup(setup)
696 cout << _("The following filename encoding algorithms are available:")
697 << "\n";
698 NameIO::AlgorithmList algorithms = NameIO::GetAlgorithmList();
699 NameIO::AlgorithmList::const_iterator it;
700 int optNum = 1;
701 for (it = algorithms.begin(); it != algorithms.end(); ++it, ++optNum) {
702 cout << optNum << ". " << it->name << " : "
703 << gettext(it->description.c_str()) << "\n";
704 }
705
706 // xgroup(setup)
707 cout << "\n" << _("Enter the number corresponding to your choice: ");
708 char answer[10];
709 char *res = fgets(answer, sizeof(answer), stdin);
710 int algNum = (res == nullptr ? 0 : (int)strtol(answer, nullptr, 10));
711 cout << "\n";
712
713 if (algNum < 1 || algNum > (int)algorithms.size()) {
714 cerr << _("Invalid selection.") << "\n";
715 continue;
716 }
717
718 it = algorithms.begin();
719 while (--algNum != 0) { // numbering starts at 1
720 ++it;
721 }
722
723 // xgroup(setup)
724 cout << autosprintf(_("Selected algorithm \"%s\""), it->name.c_str())
725 << "\"\n\n";
726
727 return it->iface;
728 }
729 }
730
731 /**
732 * Ask the user which key size to use
733 */
734 static int selectKeySize(const Cipher::CipherAlgorithm &alg) {
735 if (alg.keyLength.min() == alg.keyLength.max()) {
736 cout << autosprintf(_("Using key size of %i bits"), alg.keyLength.min())
737 << "\n";
738 return alg.keyLength.min();
739 }
740
741 cout
742 << autosprintf(
743 // xgroup(setup)
744 _("Please select a key size in bits. The cipher you have chosen\n"
745 "supports sizes from %i to %i bits in increments of %i bits.\n"
746 "For example: "),
747 alg.keyLength.min(), alg.keyLength.max(), alg.keyLength.inc())
748 << "\n";
749
750 int numAvail =
751 (alg.keyLength.max() - alg.keyLength.min()) / alg.keyLength.inc();
752
753 if (numAvail < 5) {
754 // show them all
755 for (int i = 0; i <= numAvail; ++i) {
756 if (i != 0) {
757 cout << ", ";
758 }
759 cout << alg.keyLength.min() + i * alg.keyLength.inc();
760 }
761 } else {
762 // partial
763 for (int i = 0; i < 3; ++i) {
764 if (i != 0) {
765 cout << ", ";
766 }
767 cout << alg.keyLength.min() + i * alg.keyLength.inc();
768 }
769 cout << " ... " << alg.keyLength.max() - alg.keyLength.inc();
770 cout << ", " << alg.keyLength.max();
771 }
772 // xgroup(setup)
773 cout << "\n" << _("Selected key size: ");
774
775 char answer[10];
776 char *res = fgets(answer, sizeof(answer), stdin);
777 int keySize = (res == nullptr ? 0 : (int)strtol(answer, nullptr, 10));
778 cout << "\n";
779
780 keySize = alg.keyLength.closest(keySize);
781
782 // xgroup(setup)
783 cout << autosprintf(_("Using key size of %i bits"), keySize) << "\n\n";
784
785 return keySize;
786 }
787
788 /**
789 * Ask the user which block size to use
790 */
791 static int selectBlockSize(const Cipher::CipherAlgorithm &alg) {
792 if (alg.blockSize.min() == alg.blockSize.max()) {
793 cout << autosprintf(
794 // xgroup(setup)
795 _("Using filesystem block size of %i bytes"),
796 alg.blockSize.min())
797 << "\n";
798 return alg.blockSize.min();
799 }
800
801 cout << autosprintf(
802 // xgroup(setup)
803 _("Select a block size in bytes. The cipher you have chosen\n"
804 "supports sizes from %i to %i bytes in increments of %i.\n"
805 "Or just hit enter for the default (%i bytes)\n"),
806 alg.blockSize.min(), alg.blockSize.max(), alg.blockSize.inc(),
807 DefaultBlockSize);
808
809 // xgroup(setup)
810 cout << "\n" << _("filesystem block size: ");
811
812 int blockSize = DefaultBlockSize;
813 char answer[10];
814 char *res = fgets(answer, sizeof(answer), stdin);
815 cout << "\n";
816
817 if (res != nullptr && (int)strtol(answer, nullptr, 10) >= alg.blockSize.min()) {
818 blockSize = (int)strtol(answer, nullptr, 10);
819 }
820
821 blockSize = alg.blockSize.closest(blockSize);
822
823 // xgroup(setup)
824 cout << autosprintf(_("Using filesystem block size of %i bytes"), blockSize)
825 << "\n\n";
826
827 return blockSize;
828 }
829
830 /**
831 * Prompt the user for a "y" or "n" answer.
832 * An empty answer returns defaultValue.
833 */
834 static bool boolDefault(const char *prompt, bool defaultValue) {
835
836 cout << prompt;
837 cout << "\n";
838
839 string yesno;
840
841 if (defaultValue) {
842 yesno = "[y]/n: ";
843 } else {
844 yesno = "y/[n]: ";
845 }
846
847 string response;
848 bool value;
849
850 while (true) {
851 cout << yesno;
852 getline(cin, response);
853
854 if (cin.fail() || response == "") {
855 value = defaultValue;
856 break;
857 }
858 if (response == "y") {
859 value = true;
860 break;
861 }
862 if (response == "n") {
863 value = false;
864 break;
865 }
866 }
867
868 cout << "\n";
869 return value;
870 }
871
872 static bool boolDefaultNo(const char *prompt) {
873 return boolDefault(prompt, false);
874 }
875
876 static bool boolDefaultYes(const char *prompt) {
877 return boolDefault(prompt, true);
878 }
879
880 /**
881 * Ask the user to select plain data
882 */
883 static bool selectPlainData(bool insecure) {
884 bool plainData = false;
885 if (insecure) {
886 plainData = boolDefaultNo(
887 _("You used --insecure, you can then disable file data encryption\n"
888 "which is of course abolutely discouraged.\n"
889 "Disable file data encryption?"));
890 }
891 return plainData;
892 }
893
894 /**
895 * Ask the user whether to enable block MAC and random header bytes
896 */
897 static void selectBlockMAC(int *macBytes, int *macRandBytes, bool forceMac) {
898 bool addMAC = false;
899 if (!forceMac) {
900 // xgroup(setup)
901 addMAC = boolDefaultNo(
902 _("Enable block authentication code headers\n"
903 "on every block in a file? This adds about 8 bytes per block\n"
904 "to the storage requirements for a file, and significantly affects\n"
905 "performance but it also means [almost] any modifications or errors\n"
906 "within a block will be caught and will cause a read error."));
907 } else {
908 cout << "\n\n"
909 << _("You specified --require-macs. "
910 "Enabling block authentication code headers...")
911 << "\n\n";
912 addMAC = true;
913 }
914
915 if (addMAC) {
916 *macBytes = 8;
917 } else {
918 *macBytes = 0;
919 }
920
921 // xgroup(setup)
922 cout << _(
923 "Add random bytes to each block header?\n"
924 "This adds a performance penalty, but ensures that blocks\n"
925 "have different authentication codes. Note that you can\n"
926 "have the same benefits by enabling per-file initialization\n"
927 "vectors, which does not come with as great of performance\n"
928 "penalty. \n"
929 "Select a number of bytes, from 0 (no random bytes) to 8: ");
930
931 char answer[10];
932 int randSize = 0;
933 char *res = fgets(answer, sizeof(answer), stdin);
934 cout << "\n";
935
936 randSize = (res == nullptr ? 0 : (int)strtol(answer, nullptr, 10));
937 if (randSize < 0) {
938 randSize = 0;
939 }
940 if (randSize > 8) {
941 randSize = 8;
942 }
943
944 *macRandBytes = randSize;
945 }
946
947 /**
948 * Ask the user if per-file unique IVs should be used
949 */
950 static bool selectUniqueIV(bool default_answer) {
951 // xgroup(setup)
952 return boolDefault(
953 _("Enable per-file initialization vectors?\n"
954 "This adds about 8 bytes per file to the storage requirements.\n"
955 "It should not affect performance except possibly with applications\n"
956 "which rely on block-aligned file io for performance."),
957 default_answer);
958 }
959
960 /**
961 * Ask the user if the filename IV should depend on the complete path
962 */
963 static bool selectChainedIV() {
964 // xgroup(setup)
965 return boolDefaultYes(
966 _("Enable filename initialization vector chaining?\n"
967 "This makes filename encoding dependent on the complete path, \n"
968 "rather then encoding each path element individually."));
969 }
970
971 /**
972 * Ask the user if the file IV should depend on the file path
973 */
974 static bool selectExternalChainedIV() {
975 // xgroup(setup)
976 return boolDefaultNo(
977 _("Enable filename to IV header chaining?\n"
978 "This makes file data encoding dependent on the complete file path.\n"
979 "If a file is renamed, it will not decode sucessfully unless it\n"
980 "was renamed by encfs with the proper key.\n"
981 "If this option is enabled, then hard links will not be supported\n"
982 "in the filesystem."));
983 }
984
985 /**
986 * Ask the user if file holes should be passed through
987 */
988 static bool selectZeroBlockPassThrough() {
989 // xgroup(setup)
990 return boolDefaultYes(
991 _("Enable file-hole pass-through?\n"
992 "This avoids writing encrypted blocks when file holes are created."));
993 }
994
995 RootPtr createV6Config(EncFS_Context *ctx,
996 const std::shared_ptr<EncFS_Opts> &opts) {
997 const std::string rootDir = opts->rootDir;
998 bool enableIdleTracking = opts->idleTracking;
999 bool forceDecode = opts->forceDecode;
1000 const std::string passwordProgram = opts->passwordProgram;
1001 bool useStdin = opts->useStdin;
1002 bool reverseEncryption = opts->reverseEncryption;
1003 ConfigMode configMode = opts->configMode;
1004 bool annotate = opts->annotate;
1005
1006 RootPtr rootInfo;
1007
1008 // creating new volume key.. should check that is what the user is
1009 // expecting...
1010 // xgroup(setup)
1011 cout << _("Creating new encrypted volume.") << endl;
1012
1013 char answer[10] = {0};
1014 if (configMode == Config_Prompt) {
1015 // xgroup(setup)
1016 cout << _(
1017 "Please choose from one of the following options:\n"
1018 " enter \"x\" for expert configuration mode,\n"
1019 " enter \"p\" for pre-configured paranoia mode,\n"
1020 " anything else, or an empty line will select standard mode.\n"
1021 "?> ");
1022
1023 if (annotate) {
1024 cerr << "$PROMPT$ config_option" << endl;
1025 }
1026
1027 char *res = fgets(answer, sizeof(answer), stdin);
1028 (void)res;
1029 cout << "\n";
1030 }
1031
1032 // documented in ...
1033 int keySize = 0; // selectKeySize()
1034 int blockSize = 0; // selectBlockSize()
1035 Cipher::CipherAlgorithm alg; // selectCipherAlgorithm()
1036 Interface nameIOIface; // selectNameCoding()
1037 int blockMACBytes = 0; // selectBlockMAC()
1038 int blockMACRandBytes = 0; // selectBlockMAC()
1039 bool plainData = false; // selectPlainData()
1040 bool uniqueIV = true; // selectUniqueIV()
1041 bool chainedIV = true; // selectChainedIV()
1042 bool externalIV = false; // selectExternalChainedIV()
1043 bool allowHoles = true; // selectZeroBlockPassThrough()
1044 long desiredKDFDuration = NormalKDFDuration;
1045
1046 if (reverseEncryption) {
1047 chainedIV = false;
1048 externalIV = false;
1049 uniqueIV = false;
1050 blockMACBytes = 0;
1051 blockMACRandBytes = 0;
1052 }
1053
1054 if (configMode == Config_Paranoia || answer[0] == 'p') {
1055 if (reverseEncryption) {
1056 cerr << _("Paranoia configuration not supported for reverse encryption");
1057 return rootInfo;
1058 }
1059
1060 // xgroup(setup)
1061 cout << _("Paranoia configuration selected.") << "\n";
1062 // look for AES with 256 bit key..
1063 // Use block filename encryption mode.
1064 // Enable per-block HMAC headers at substantial performance penalty..
1065 // Enable per-file initialization vector headers.
1066 // Enable filename initialization vector chaning
1067 keySize = 256;
1068 blockSize = DefaultBlockSize;
1069 alg = findCipherAlgorithm("AES", keySize);
1070
1071 // If case-insensitive system, opt for Block32 filename encoding
1072 #if defined(DEFAULT_CASE_INSENSITIVE)
1073 nameIOIface = BlockNameIO::CurrentInterface(true);
1074 #else
1075 nameIOIface = BlockNameIO::CurrentInterface();
1076 #endif
1077
1078 blockMACBytes = 8;
1079 blockMACRandBytes = 0; // using uniqueIV, so this isn't necessary
1080 externalIV = true;
1081 desiredKDFDuration = ParanoiaKDFDuration;
1082 } else if (configMode == Config_Standard || answer[0] != 'x') {
1083 // xgroup(setup)
1084 cout << _("Standard configuration selected.") << "\n";
1085 // AES w/ 192 bit key, block name encoding, per-file initialization
1086 // vectors are all standard.
1087 keySize = 192;
1088 blockSize = DefaultBlockSize;
1089 alg = findCipherAlgorithm("AES", keySize);
1090
1091 // If case-insensitive system, opt for Block32 filename encoding
1092 #if defined(DEFAULT_CASE_INSENSITIVE)
1093 nameIOIface = BlockNameIO::CurrentInterface(true);
1094 #else
1095 nameIOIface = BlockNameIO::CurrentInterface();
1096 #endif
1097
1098 if (opts->requireMac) {
1099 blockMACBytes = 8;
1100 }
1101 }
1102
1103 if (answer[0] == 'x' || alg.name.empty()) {
1104 if (answer[0] != 'x') {
1105 // xgroup(setup)
1106 cout << _(
1107 "Sorry, unable to locate cipher for predefined "
1108 "configuration...\n"
1109 "Falling through to Manual configuration mode.");
1110 } else {
1111 // xgroup(setup)
1112 cout << _("Manual configuration mode selected.");
1113 }
1114 cout << endl;
1115
1116 // query user for settings..
1117 alg = selectCipherAlgorithm();
1118 keySize = selectKeySize(alg);
1119 blockSize = selectBlockSize(alg);
1120 plainData = selectPlainData(opts->insecure);
1121 nameIOIface = selectNameCoding();
1122 if (plainData) {
1123 cout << _("plain data - IV, MAC and file-hole disabled") << "\n";
1124 allowHoles = false;
1125 chainedIV = false;
1126 externalIV = false;
1127 uniqueIV = false;
1128 blockMACBytes = 0;
1129 blockMACRandBytes = 0;
1130 }
1131 else {
1132 if (reverseEncryption) {
1133 cout << _("reverse encryption - chained IV and MAC disabled") << "\n";
1134 uniqueIV = selectUniqueIV(false);
1135 /* If uniqueIV is off, writing can be allowed, because there
1136 * is no header that could be overwritten.
1137 * So if it is on, enforce readOnly. */
1138 if (uniqueIV) {
1139 opts->readOnly = true;
1140 }
1141 } else {
1142 chainedIV = selectChainedIV();
1143 uniqueIV = selectUniqueIV(true);
1144 if (chainedIV && uniqueIV) {
1145 externalIV = selectExternalChainedIV();
1146 } else {
1147 // xgroup(setup)
1148 cout << _("External chained IV disabled, as both 'IV chaining'\n"
1149 "and 'unique IV' features are required for this option.")
1150 << "\n";
1151 externalIV = false;
1152 }
1153 selectBlockMAC(&blockMACBytes, &blockMACRandBytes, opts->requireMac);
1154 allowHoles = selectZeroBlockPassThrough();
1155 }
1156 }
1157 }
1158
1159 std::shared_ptr<Cipher> cipher = Cipher::New(alg.name, keySize);
1160 if (!cipher) {
1161 cerr << autosprintf(
1162 _("Unable to instanciate cipher %s, key size %i, block size %i"),
1163 alg.name.c_str(), keySize, blockSize);
1164 return rootInfo;
1165 }
1166 VLOG(1) << "Using cipher " << alg.name << ", key size " << keySize
1167 << ", block size " << blockSize;
1168
1169 std::shared_ptr<EncFSConfig> config(new EncFSConfig);
1170
1171 config->cfgType = Config_V6;
1172 config->cipherIface = cipher->interface();
1173 config->keySize = keySize;
1174 config->blockSize = blockSize;
1175 config->plainData = plainData;
1176 config->nameIface = nameIOIface;
1177 config->creator = "EncFS " VERSION;
1178 config->subVersion = V6SubVersion;
1179 config->blockMACBytes = blockMACBytes;
1180 config->blockMACRandBytes = blockMACRandBytes;
1181 config->uniqueIV = uniqueIV;
1182 config->chainedNameIV = chainedIV;
1183 config->externalIVChaining = externalIV;
1184 config->allowHoles = allowHoles;
1185
1186 config->salt.clear();
1187 config->kdfIterations = 0; // filled in by keying function
1188 config->desiredKDFDuration = desiredKDFDuration;
1189
1190 cout << "\n";
1191 // xgroup(setup)
1192 cout << _("Configuration finished. The filesystem to be created has\n"
1193 "the following properties:")
1194 << endl;
1195 showFSInfo(config.get());
1196
1197 if (config->externalIVChaining) {
1198 cout << _("-------------------------- WARNING --------------------------\n")
1199 << _("The external initialization-vector chaining option has been\n"
1200 "enabled. This option disables the use of hard links on the\n"
1201 "filesystem. Without hard links, some programs may not work.\n"
1202 "The programs 'mutt' and 'procmail' are known to fail. For\n"
1203 "more information, please see the encfs mailing list.\n"
1204 "If you would like to choose another configuration setting,\n"
1205 "please press CTRL-C now to abort and start over.")
1206 << endl;
1207 cout << endl;
1208 }
1209
1210 // xgroup(setup)
1211 cout << _(
1212 "Now you will need to enter a password for your filesystem.\n"
1213 "You will need to remember this password, as there is absolutely\n"
1214 "no recovery mechanism. However, the password can be changed\n"
1215 "later using encfsctl.\n\n");
1216
1217 int encodedKeySize = cipher->encodedKeySize();
1218 auto *encodedKey = new unsigned char[encodedKeySize];
1219
1220 CipherKey volumeKey = cipher->newRandomKey();
1221
1222 // get user key and use it to encode volume key
1223 CipherKey userKey;
1224 VLOG(1) << "useStdin: " << useStdin;
1225 if (useStdin) {
1226 if (annotate) {
1227 cerr << "$PROMPT$ new_passwd" << endl;
1228 }
1229 userKey = config->getUserKey(useStdin);
1230 } else if (!passwordProgram.empty()) {
1231 userKey = config->getUserKey(passwordProgram, rootDir);
1232 } else {
1233 userKey = config->getNewUserKey();
1234 }
1235
1236 cipher->writeKey(volumeKey, encodedKey, userKey);
1237 userKey.reset();
1238
1239 config->assignKeyData(encodedKey, encodedKeySize);
1240 delete[] encodedKey;
1241
1242 if (!volumeKey) {
1243 cerr << _(
1244 "Failure generating new volume key! "
1245 "Please report this error.");
1246 return rootInfo;
1247 }
1248
1249 if (!saveConfig(Config_V6, rootDir, config.get(), opts->config)) {
1250 return rootInfo;
1251 }
1252
1253 // fill in config struct
1254 std::shared_ptr<NameIO> nameCoder =
1255 NameIO::New(config->nameIface, cipher, volumeKey);
1256 if (!nameCoder) {
1257 cerr << _("Name coding interface not supported");
1258 cout << _("The filename encoding interface requested is not available")
1259 << endl;
1260 return rootInfo;
1261 }
1262
1263 nameCoder->setChainedNameIV(config->chainedNameIV);
1264 nameCoder->setReverseEncryption(reverseEncryption);
1265
1266 FSConfigPtr fsConfig(new FSConfig);
1267 if (plainData) {
1268 static Interface NullInterface("nullCipher", 1, 0, 0);
1269 fsConfig->cipher = Cipher::New(NullInterface, 0);
1270 }
1271 else {
1272 fsConfig->cipher = cipher;
1273 }
1274 fsConfig->key = volumeKey;
1275 fsConfig->nameCoding = nameCoder;
1276 fsConfig->config = config;
1277 fsConfig->forceDecode = forceDecode;
1278 fsConfig->reverseEncryption = reverseEncryption;
1279 fsConfig->idleTracking = enableIdleTracking;
1280 fsConfig->opts = opts;
1281
1282 rootInfo = std::make_shared<encfs::EncFS_Root>();
1283 rootInfo->cipher = cipher;
1284 rootInfo->volumeKey = volumeKey;
1285 rootInfo->root = std::make_shared<DirNode>(ctx, rootDir, fsConfig);
1286
1287 return rootInfo;
1288 }
1289
1290 void showFSInfo(const EncFSConfig *config) {
1291 std::shared_ptr<Cipher> cipher = Cipher::New(config->cipherIface, -1);
1292 {
1293 cout << autosprintf(
1294 // xgroup(diag)
1295 _("Filesystem cipher: \"%s\", version %i:%i:%i"),
1296 config->cipherIface.name().c_str(), config->cipherIface.current(),
1297 config->cipherIface.revision(), config->cipherIface.age());
1298 // check if we support this interface..
1299 if (!cipher) {
1300 cout << _(" (NOT supported)\n");
1301 } else {
1302 // if we're using a newer interface, show the version number
1303 if (config->cipherIface != cipher->interface()) {
1304 Interface iface = cipher->interface();
1305 // xgroup(diag)
1306 cout << autosprintf(_(" (using %i:%i:%i)\n"), iface.current(),
1307 iface.revision(), iface.age());
1308 } else {
1309 cout << "\n";
1310 }
1311 }
1312 }
1313 {
1314 // xgroup(diag)
1315 cout << autosprintf(_("Filename encoding: \"%s\", version %i:%i:%i"),
1316 config->nameIface.name().c_str(),
1317 config->nameIface.current(),
1318 config->nameIface.revision(), config->nameIface.age());
1319
1320 // check if we support the filename encoding interface..
1321 std::shared_ptr<NameIO> nameCoder =
1322 NameIO::New(config->nameIface, cipher, CipherKey());
1323 if (!nameCoder) {
1324 // xgroup(diag)
1325 cout << _(" (NOT supported)\n");
1326 } else {
1327 // if we're using a newer interface, show the version number
1328 if (config->nameIface != nameCoder->interface()) {
1329 Interface iface = nameCoder->interface();
1330 cout << autosprintf(_(" (using %i:%i:%i)\n"), iface.current(),
1331 iface.revision(), iface.age());
1332 } else {
1333 cout << "\n";
1334 }
1335 }
1336 }
1337 {
1338 cout << autosprintf(_("Key Size: %i bits"), config->keySize);
1339 cipher = config->getCipher();
1340 if (!cipher) {
1341 // xgroup(diag)
1342 cout << _(" (NOT supported)\n");
1343 } else {
1344 cout << "\n";
1345 }
1346 }
1347 if (config->kdfIterations > 0 && !config->salt.empty()) {
1348 cout << autosprintf(_("Using PBKDF2, with %i iterations"),
1349 config->kdfIterations)
1350 << "\n";
1351 cout << autosprintf(_("Salt Size: %i bits"), (int)(8 * config->salt.size()))
1352 << "\n";
1353 }
1354 if ((config->blockMACBytes != 0) || (config->blockMACRandBytes != 0)) {
1355 if (config->subVersion < 20040813) {
1356 cout << autosprintf(
1357 // xgroup(diag)
1358 _("Block Size: %i bytes + %i byte MAC header"),
1359 config->blockSize,
1360 config->blockMACBytes + config->blockMACRandBytes)
1361 << endl;
1362 } else {
1363 // new version stores the header as part of that block size..
1364 cout << autosprintf(
1365 // xgroup(diag)
1366 _("Block Size: %i bytes, including %i byte MAC header"),
1367 config->blockSize,
1368 config->blockMACBytes + config->blockMACRandBytes)
1369 << endl;
1370 }
1371 } else {
1372 // xgroup(diag)
1373 cout << autosprintf(_("Block Size: %i bytes"), config->blockSize);
1374 cout << "\n";
1375 }
1376
1377 if (config->uniqueIV) {
1378 // xgroup(diag)
1379 cout << _("Each file contains 8 byte header with unique IV data.\n");
1380 }
1381 if (config->chainedNameIV) {
1382 // xgroup(diag)
1383 cout << _("Filenames encoded using IV chaining mode.\n");
1384 }
1385 if (config->externalIVChaining) {
1386 // xgroup(diag)
1387 cout << _("File data IV is chained to filename IV.\n");
1388 }
1389 if (config->allowHoles) {
1390 // xgroup(diag)
1391 cout << _("File holes passed through to ciphertext.\n");
1392 }
1393 cout << "\n";
1394 }
1395 std::shared_ptr<Cipher> EncFSConfig::getCipher() const {
1396 return Cipher::New(cipherIface, keySize);
1397 }
1398
1399 void EncFSConfig::assignKeyData(const std::string &in) {
1400 keyData.assign(in.data(), in.data() + in.length());
1401 }
1402
1403 void EncFSConfig::assignKeyData(unsigned char *data, int len) {
1404 keyData.assign(data, data + len);
1405 }
1406
1407 void EncFSConfig::assignSaltData(unsigned char *data, int len) {
1408 salt.assign(data, data + len);
1409 }
1410
1411 unsigned char *EncFSConfig::getKeyData() const {
1412 return const_cast<unsigned char *>(&keyData.front());
1413 }
1414
1415 unsigned char *EncFSConfig::getSaltData() const {
1416 return const_cast<unsigned char *>(&salt.front());
1417 }
1418
1419 CipherKey EncFSConfig::makeKey(const char *password, int passwdLen) {
1420 CipherKey userKey;
1421 std::shared_ptr<Cipher> cipher = getCipher();
1422
1423 if (passwdLen == 0) {
1424 cerr << _("fatal: zero-length passwords are not allowed\n");
1425 exit(1);
1426 }
1427
1428 // if no salt is set and we're creating a new password for a new
1429 // FS type, then initialize salt..
1430 if (salt.empty() && kdfIterations == 0 && cfgType >= Config_V6) {
1431 // upgrade to using salt
1432 salt.resize(20);
1433 }
1434
1435 if (!salt.empty()) {
1436 // if iterations isn't known, then we're creating a new key, so
1437 // randomize the salt..
1438 if (kdfIterations == 0 &&
1439 !cipher->randomize(getSaltData(), salt.size(), true)) {
1440 cout << _("Error creating salt\n");
1441 return userKey;
1442 }
1443
1444 userKey = cipher->newKey(password, passwdLen, kdfIterations,
1445 desiredKDFDuration, getSaltData(), salt.size());
1446 } else {
1447 userKey = cipher->newKey(password, passwdLen);
1448 }
1449
1450 return userKey;
1451 }
1452
1453 CipherKey EncFSConfig::getUserKey(bool useStdin) {
1454 char passBuf[MaxPassBuf];
1455 char *res;
1456
1457 if (useStdin) {
1458 res = fgets(passBuf, sizeof(passBuf), stdin);
1459 // Kill the trailing newline.
1460 if (passBuf[strlen(passBuf) - 1] == '\n') {
1461 passBuf[strlen(passBuf) - 1] = '\0';
1462 }
1463 } else {
1464 // xgroup(common)
1465 res = readpassphrase(_("EncFS Password: "), passBuf, sizeof(passBuf),
1466 RPP_ECHO_OFF);
1467 }
1468
1469 CipherKey userKey;
1470 if (res == nullptr) {
1471 cerr << _("fatal: error reading password\n");
1472 exit(1);
1473 } else {
1474 userKey = makeKey(passBuf, strlen(passBuf));
1475 }
1476
1477 memset(passBuf, 0, sizeof(passBuf));
1478
1479 return userKey;
1480 }
1481
1482 std::string readPassword(int FD) {
1483 char buffer[1024];
1484 string result;
1485
1486 while (true) {
1487 ssize_t rdSize = recv(FD, buffer, sizeof(buffer), 0);
1488
1489 if (rdSize > 0) {
1490 result.append(buffer, rdSize);
1491 memset(buffer, 0, sizeof(buffer));
1492 } else {
1493 break;
1494 }
1495 }
1496
1497 // chop off trailing "\n" if present..
1498 // This is done so that we can use standard programs like ssh-askpass
1499 // without modification, as it returns trailing newline..
1500 if (!result.empty() && result[result.length() - 1] == '\n') {
1501 result.resize(result.length() - 1);
1502 }
1503
1504 return result;
1505 }
1506
1507 CipherKey EncFSConfig::getUserKey(const std::string &passProg,
1508 const std::string &rootDir) {
1509 // have a child process run the command and get the result back to us.
1510 int fds[2], pid;
1511 int res;
1512 CipherKey result;
1513
1514 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
1515 if (res == -1) {
1516 perror(_("Internal error: socketpair() failed"));
1517 return result;
1518 }
1519 VLOG(1) << "getUserKey: fds = " << fds[0] << ", " << fds[1];
1520
1521 pid = fork();
1522 if (pid == -1) {
1523 perror(_("Internal error: fork() failed"));
1524 close(fds[0]);
1525 close(fds[1]);
1526 return result;
1527 }
1528
1529 if (pid == 0) {
1530 const char *argv[4];
1531 argv[0] = "/bin/sh";
1532 argv[1] = "-c";
1533 argv[2] = passProg.c_str();
1534 argv[3] = nullptr;
1535
1536 // child process.. run the command and send output to fds[0]
1537 close(fds[1]); // we don't use the other half..
1538
1539 // make a copy of stdout and stderr descriptors, and set an environment
1540 // variable telling where to find them, in case a child wants it..
1541 int stdOutCopy = dup(STDOUT_FILENO);
1542 int stdErrCopy = dup(STDERR_FILENO);
1543 // replace STDOUT with our socket, which we'll used to receive the
1544 // password..
1545 dup2(fds[0], STDOUT_FILENO);
1546
1547 // ensure that STDOUT_FILENO and stdout/stderr are not closed on exec..
1548 fcntl(STDOUT_FILENO, F_SETFD, 0); // don't close on exec..
1549 fcntl(stdOutCopy, F_SETFD, 0);
1550 fcntl(stdErrCopy, F_SETFD, 0);
1551
1552 char tmpBuf[8];
1553
1554 setenv(ENCFS_ENV_ROOTDIR, rootDir.c_str(), 1);
1555
1556 snprintf(tmpBuf, sizeof(tmpBuf) - 1, "%i", stdOutCopy);
1557 setenv(ENCFS_ENV_STDOUT, tmpBuf, 1);
1558
1559 snprintf(tmpBuf, sizeof(tmpBuf) - 1, "%i", stdErrCopy);
1560 setenv(ENCFS_ENV_STDERR, tmpBuf, 1);
1561
1562 execvp(argv[0], (char *const *)argv); // returns only on error..
1563
1564 perror(_("Internal error: failed to exec program"));
1565 exit(1);
1566 }
1567
1568 close(fds[0]);
1569 string password = readPassword(fds[1]);
1570 close(fds[1]);
1571
1572 waitpid(pid, nullptr, 0);
1573
1574 // convert to key..
1575 result = makeKey(password.c_str(), password.length());
1576
1577 // clear buffer..
1578 password.assign(password.length(), '\0');
1579
1580 return result;
1581 }
1582
1583 CipherKey EncFSConfig::getNewUserKey() {
1584 CipherKey userKey;
1585 char passBuf[MaxPassBuf];
1586 char passBuf2[MaxPassBuf];
1587
1588 do {
1589 // xgroup(common)
1590 char *res1 = readpassphrase(_("New Encfs Password: "), passBuf,
1591 sizeof(passBuf) - 1, RPP_ECHO_OFF);
1592 // xgroup(common)
1593 char *res2 = readpassphrase(_("Verify Encfs Password: "), passBuf2,
1594 sizeof(passBuf2) - 1, RPP_ECHO_OFF);
1595
1596 if ((res1 != nullptr) && (res2 != nullptr) &&
1597 (strcmp(passBuf, passBuf2) == 0)) {
1598 userKey = makeKey(passBuf, strlen(passBuf));
1599 } else {
1600 // xgroup(common) -- probably not common, but group with the others
1601 cerr << _("Passwords did not match, please try again\n");
1602 }
1603
1604 memset(passBuf, 0, sizeof(passBuf));
1605 memset(passBuf2, 0, sizeof(passBuf2));
1606 } while (!userKey);
1607
1608 return userKey;
1609 }
1610
1611 RootPtr initFS(EncFS_Context *ctx, const std::shared_ptr<EncFS_Opts> &opts) {
1612 RootPtr rootInfo;
1613 std::shared_ptr<EncFSConfig> config(new EncFSConfig);
1614
1615 if (readConfig(opts->rootDir, config.get(), opts->config) != Config_None) {
1616 if (config->blockMACBytes == 0 && opts->requireMac) {
1617 cout << _(
1618 "The configuration disabled MAC, but you passed --require-macs\n");
1619 return rootInfo;
1620 }
1621
1622 if (opts->reverseEncryption) {
1623 if (config->blockMACBytes != 0 || config->blockMACRandBytes != 0 ||
1624 config->externalIVChaining || config->chainedNameIV) {
1625 cout << _(
1626 "The configuration loaded is not compatible with --reverse\n");
1627 return rootInfo;
1628 }
1629 /* If uniqueIV is off, writing can be allowed, because there
1630 * is no header that could be overwritten.
1631 * So if it is on, enforce readOnly. */
1632 if (config->uniqueIV) {
1633 opts->readOnly = true;
1634 }
1635 }
1636
1637 // first, instanciate the cipher.
1638 std::shared_ptr<Cipher> cipher = config->getCipher();
1639 if (!cipher) {
1640 cerr << autosprintf(
1641 _("Unable to find cipher %s, version %i:%i:%i"),
1642 config->cipherIface.name().c_str(), config->cipherIface.current(),
1643 config->cipherIface.revision(), config->cipherIface.age());
1644 // xgroup(diag)
1645 cout << _("The requested cipher interface is not available\n");
1646 return rootInfo;
1647 }
1648
1649 if (opts->delayMount) {
1650 rootInfo = std::make_shared<encfs::EncFS_Root>();
1651 rootInfo->cipher = cipher;
1652 rootInfo->root = std::shared_ptr<DirNode>();
1653 return rootInfo;
1654 }
1655
1656 // get user key
1657 CipherKey userKey;
1658
1659 if (opts->passwordProgram.empty()) {
1660 VLOG(1) << "useStdin: " << opts->useStdin;
1661 if (opts->annotate) {
1662 cerr << "$PROMPT$ passwd" << endl;
1663 }
1664 userKey = config->getUserKey(opts->useStdin);
1665 } else {
1666 userKey = config->getUserKey(opts->passwordProgram, opts->rootDir);
1667 }
1668
1669 if (!userKey) {
1670 return rootInfo;
1671 }
1672
1673 VLOG(1) << "cipher key size = " << cipher->encodedKeySize();
1674 // decode volume key..
1675 CipherKey volumeKey =
1676 cipher->readKey(config->getKeyData(), userKey, opts->checkKey);
1677 userKey.reset();
1678
1679 if (!volumeKey) {
1680 // xgroup(diag)
1681 cout << _("Error decoding volume key, password incorrect\n");
1682 return rootInfo;
1683 }
1684
1685 std::shared_ptr<NameIO> nameCoder =
1686 NameIO::New(config->nameIface, cipher, volumeKey);
1687 if (!nameCoder) {
1688 cerr << autosprintf(
1689 _("Unable to find nameio interface '%s', version %i:%i:%i"),
1690 config->nameIface.name().c_str(), config->nameIface.current(),
1691 config->nameIface.revision(), config->nameIface.age());
1692 // xgroup(diag)
1693 cout << _(
1694 "The requested filename coding interface is "
1695 "not available\n");
1696 return rootInfo;
1697 }
1698
1699 nameCoder->setChainedNameIV(config->chainedNameIV);
1700 nameCoder->setReverseEncryption(opts->reverseEncryption);
1701
1702 FSConfigPtr fsConfig(new FSConfig);
1703 if (config->plainData) {
1704 if (! opts->insecure) {
1705 cout << _("Configuration use plainData but you did not use --insecure\n");
1706 return rootInfo;
1707 }
1708 static Interface NullInterface("nullCipher", 1, 0, 0);
1709 fsConfig->cipher = Cipher::New(NullInterface, 0);
1710 }
1711 else {
1712 fsConfig->cipher = cipher;
1713 }
1714 fsConfig->key = volumeKey;
1715 fsConfig->nameCoding = nameCoder;
1716 fsConfig->config = config;
1717 fsConfig->forceDecode = opts->forceDecode;
1718 fsConfig->reverseEncryption = opts->reverseEncryption;
1719 fsConfig->opts = opts;
1720
1721 rootInfo = std::make_shared<encfs::EncFS_Root>();
1722 rootInfo->cipher = cipher;
1723 rootInfo->volumeKey = volumeKey;
1724 rootInfo->root = std::make_shared<DirNode>(ctx, opts->rootDir, fsConfig);
1725 } else {
1726 if (opts->createIfNotFound) {
1727 // creating a new encrypted filesystem
1728 rootInfo = createV6Config(ctx, opts);
1729 }
1730 }
1731
1732 return rootInfo;
1733 }
1734
1735 void unmountFS(const char *mountPoint) {
1736 // fuse_unmount returns void, is assumed to succeed
1737 fuse_unmount(mountPoint, nullptr);
1738 #ifdef __APPLE__
1739 // fuse_unmount does not work on Mac OS, see #428
1740 // However it makes encfs to hang, so we must unmount
1741 if (unmount(mountPoint, MNT_FORCE) != 0) {
1742 int eno = errno;
1743 if (eno != EINVAL) { //[EINVAL] The requested directory is not in the mount table.
1744 RLOG(ERROR) << "Filesystem unmount failed: " << strerror(eno);
1745 }
1746 }
1747 #endif
1748 #ifdef __CYGWIN__
1749 pid_t pid;
1750 int status;
1751 if ((pid = fork()) == 0) {
1752 execl("/bin/pkill", "/bin/pkill", "-INT", "-if", string("(^|/)encfs .*(/|.:).* ").append(mountPoint).append("( |$)").c_str(), (char *)0);
1753 int eno = errno;
1754 RLOG(ERROR) << "Filesystem unmount failed: " << strerror(eno);
1755 _Exit(127);
1756 }
1757 if (pid > 0) {
1758 waitpid(pid, &status, 0);
1759 }
1760 #endif
1761 }
1762
1763 int remountFS(EncFS_Context *ctx) {
1764 VLOG(1) << "Attempting to reinitialize filesystem";
1765
1766 RootPtr rootInfo = initFS(ctx, ctx->opts);
1767 if (rootInfo) {
1768 ctx->setRoot(rootInfo->root);
1769 return 0;
1770 }
1771 RLOG(WARNING) << "Remount failed";
1772 return -EACCES;
1773 }
1774
1775 bool unmountFS(EncFS_Context *ctx) {
1776 if (ctx->opts->mountOnDemand) {
1777 VLOG(1) << "Detaching filesystem due to inactivity: "
1778 << ctx->opts->unmountPoint;
1779
1780 ctx->setRoot(std::shared_ptr<DirNode>());
1781 return false;
1782 }
1783 // Time to unmount!
1784 RLOG(INFO) << "Filesystem inactive, unmounting: " << ctx->opts->unmountPoint;
1785 unmountFS(ctx->opts->unmountPoint.c_str());
1786 return true;
1787 }
1788
1789 } // namespace encfs