From fb9a8ff8791af4c4955e84ee35749eaf39782eb3 Mon Sep 17 00:00:00 2001 From: Valient Gough Date: Tue, 29 Jan 2013 03:07:54 +0000 Subject: [PATCH] Reformat files, replace autoconf with CMake, and RLog with GLog. git-svn-id: http://encfs.googlecode.com/svn/trunk@92 db9cf616-1c43-0410-9cb8-a902689de0d6 --- ABOUT-NLS | 1068 ----------------------- AUTHORS | 14 +- CMakeLists.txt | 67 ++ CMakeModules/FindFUSE.cmake | 35 + CMakeModules/FindGLog.cmake | 59 ++ CMakeModules/FindTinyXML.cmake | 26 + CMakeModules/GettextTranslate.cmake | 287 +++++++ CMakeModules/OpenSSLTests.cmake | 24 + Makefile.am | 17 - Makefile.common | 2 - Makefile.dist | 5 - README | 11 +- README-NLS | 63 +- TRANSLATORS | 7 - base/CMakeLists.txt | 29 + base/ConfigReader.cpp | 157 ++++ {encfs => base}/ConfigReader.h | 2 +- base/ConfigVar.cpp | 253 ++++++ {encfs => base}/ConfigVar.h | 2 +- base/Error.cpp | 7 + base/Error.h | 25 + {encfs => base}/Interface.cpp | 26 +- {encfs => base}/Interface.h | 4 +- {encfs => base}/Mutex.h | 59 +- {encfs => base}/Range.h | 2 + {encfs => base}/XmlReader.cpp | 23 +- {encfs => base}/XmlReader.h | 2 +- {intl => base}/autosprintf.cpp | 2 +- {intl => base}/autosprintf.h | 0 {encfs => base}/base64.cpp | 2 +- {encfs => base}/base64.h | 0 base/config.h.cmake | 19 + {encfs => base}/config.proto | 5 + {intl => base}/gettext.h | 0 {encfs => base}/i18n.h | 4 +- {encfs => base}/shared_ptr.h | 2 +- cipher/CMakeLists.txt | 41 + {encfs => cipher}/Cipher.cpp | 43 +- {encfs => cipher}/Cipher.h | 28 +- {encfs => cipher}/CipherKey.cpp | 2 +- {encfs => cipher}/CipherKey.h | 2 +- cipher/MemoryPool.cpp | 164 ++++ {encfs => cipher}/MemoryPool.h | 24 +- {encfs => cipher}/NullCipher.cpp | 107 ++- {encfs => cipher}/NullCipher.h | 4 +- cipher/SSL_Cipher.cpp | 973 +++++++++++++++++++++ cipher/SSL_Cipher.h | 159 ++++ cipher/openssl.cpp | 109 +++ {encfs => cipher}/openssl.h | 0 {encfs => cipher}/readpassphrase.cpp | 2 +- {encfs => cipher}/readpassphrase.h | 0 configure | 1 + configure.ac | 235 ------ devmode | 2 + encfs.spec.in | 207 ----- encfs/BlockFileIO.cpp | 433 ---------- encfs/BlockNameIO.cpp | 267 ------ encfs/CMakeLists.txt | 39 + encfs/CipherFileIO.cpp | 438 ---------- encfs/ConfigReader.cpp | 162 ---- encfs/ConfigVar.cpp | 252 ------ encfs/Context.cpp | 177 ---- encfs/DirNode.cpp | 831 ------------------ encfs/FileNode.cpp | 306 ------- encfs/MACFileIO.cpp | 304 ------- encfs/Makefile.am | 163 ---- encfs/MemoryPool.cpp | 178 ---- encfs/NameIO.cpp | 351 -------- encfs/RawFileIO.cpp | 325 ------- encfs/SSL_Cipher.cpp | 927 -------------------- encfs/SSL_Cipher.h | 150 ---- encfs/StreamNameIO.cpp | 208 ----- encfs/docs/Makefile.am | 4 - encfs/docs/en/Makefile.am | 4 - encfs/encfs.cpp | 807 ------------------ encfs/encfs.pod | 154 ++-- encfs/encfsctl.cpp | 853 ------------------- encfs/main.cpp | 1173 +++++++++++++------------- encfs/openssl.cpp | 109 --- encfs/test.cpp | 563 ------------ encfs/tests.t | 213 ----- fs/BlockFileIO.cpp | 426 ++++++++++ {encfs => fs}/BlockFileIO.h | 2 +- fs/BlockNameIO.cpp | 250 ++++++ {encfs => fs}/BlockNameIO.h | 6 +- fs/CMakeLists.txt | 56 ++ fs/CipherFileIO.cpp | 513 +++++++++++ {encfs => fs}/CipherFileIO.h | 18 +- fs/Context.cpp | 175 ++++ {encfs => fs}/Context.h | 6 +- fs/DirNode.cpp | 816 ++++++++++++++++++ {encfs => fs}/DirNode.h | 10 +- {encfs => fs}/FSConfig.h | 10 +- {encfs => fs}/FileIO.cpp | 0 {encfs => fs}/FileIO.h | 5 +- fs/FileNode.cpp | 303 +++++++ {encfs => fs}/FileNode.h | 6 +- {encfs => fs}/FileUtils.cpp | 193 +++-- {encfs => fs}/FileUtils.h | 8 +- fs/MACFileIO.cpp | 295 +++++++ {encfs => fs}/MACFileIO.h | 4 +- fs/MemBlockFileIO.cpp | 76 ++ fs/MemBlockFileIO.h | 59 ++ fs/MemFileIO.cpp | 106 +++ fs/MemFileIO.h | 58 ++ fs/NameIO.cpp | 338 ++++++++ {encfs => fs}/NameIO.h | 24 +- {encfs => fs}/NullNameIO.cpp | 38 +- {encfs => fs}/NullNameIO.h | 0 fs/RawFileIO.cpp | 330 ++++++++ {encfs => fs}/RawFileIO.h | 4 +- fs/StreamNameIO.cpp | 206 +++++ {encfs => fs}/StreamNameIO.h | 4 +- fs/encfs.cpp | 793 +++++++++++++++++ {encfs => fs}/encfs.h | 2 +- fs/test.cpp | 556 ++++++++++++ fs/test_BlockIO.cpp | 67 ++ fs/test_IO.cpp | 95 +++ {encfs => fs}/testextpass | 0 fs/testing.cpp | 171 ++++ fs/testing.h | 23 + m4/Makefile.am | 10 - m4/ax_ext_check_header.m4 | 58 -- m4/ax_ext_have_lib.m4 | 72 -- m4/ax_pthread.m4 | 309 ------- makedist.sh | 12 - makedist2.sh.in | 27 - po/CMakeLists.txt | 37 + po/ChangeLog | 5 - po/Makefile.in.in | 429 ---------- po/POTFILES.in | 12 +- po/Rules-quot | 47 -- po/boldquot.sed | 10 - po/en@boldquot.header | 25 - po/en@quot.header | 22 - po/insert-header.sin | 23 - po/quot.sed | 6 - po/remove-potcdate.sin | 19 - reconfig.sh | 3 - subdirs | 3 - util/CMakeLists.txt | 40 + util/encfsctl.cpp | 844 ++++++++++++++++++ {encfs => util}/encfsctl.pod | 0 {encfs => util}/encfssh | 0 {encfs => util}/makeKey.cpp | 0 145 files changed, 10139 insertions(+), 11730 deletions(-) delete mode 100644 ABOUT-NLS create mode 100644 CMakeLists.txt create mode 100644 CMakeModules/FindFUSE.cmake create mode 100644 CMakeModules/FindGLog.cmake create mode 100644 CMakeModules/FindTinyXML.cmake create mode 100644 CMakeModules/GettextTranslate.cmake create mode 100644 CMakeModules/OpenSSLTests.cmake delete mode 100644 Makefile.am delete mode 100644 Makefile.common delete mode 100644 Makefile.dist delete mode 100644 TRANSLATORS create mode 100644 base/CMakeLists.txt create mode 100644 base/ConfigReader.cpp rename {encfs => base}/ConfigReader.h (98%) create mode 100644 base/ConfigVar.cpp rename {encfs => base}/ConfigVar.h (98%) create mode 100644 base/Error.cpp create mode 100644 base/Error.h rename {encfs => base}/Interface.cpp (81%) rename {encfs => base}/Interface.h (97%) rename {encfs => base}/Mutex.h (63%) rename {encfs => base}/Range.h (97%) rename {encfs => base}/XmlReader.cpp (90%) rename {encfs => base}/XmlReader.h (98%) rename {intl => base}/autosprintf.cpp (98%) rename {intl => base}/autosprintf.h (100%) rename {encfs => base}/base64.cpp (99%) rename {encfs => base}/base64.h (100%) create mode 100644 base/config.h.cmake rename {encfs => base}/config.proto (87%) rename {intl => base}/gettext.h (100%) rename {encfs => base}/i18n.h (95%) rename {encfs => base}/shared_ptr.h (97%) create mode 100644 cipher/CMakeLists.txt rename {encfs => cipher}/Cipher.cpp (89%) rename {encfs => cipher}/Cipher.h (88%) rename {encfs => cipher}/CipherKey.cpp (97%) rename {encfs => cipher}/CipherKey.h (97%) create mode 100644 cipher/MemoryPool.cpp rename {encfs => cipher}/MemoryPool.h (90%) rename {encfs => cipher}/NullCipher.cpp (63%) rename {encfs => cipher}/NullCipher.h (98%) create mode 100644 cipher/SSL_Cipher.cpp create mode 100644 cipher/SSL_Cipher.h create mode 100644 cipher/openssl.cpp rename {encfs => cipher}/openssl.h (100%) rename {encfs => cipher}/readpassphrase.cpp (99%) rename {encfs => cipher}/readpassphrase.h (100%) create mode 100644 configure delete mode 100644 configure.ac create mode 100644 devmode delete mode 100644 encfs.spec.in delete mode 100644 encfs/BlockFileIO.cpp delete mode 100644 encfs/BlockNameIO.cpp create mode 100644 encfs/CMakeLists.txt delete mode 100644 encfs/CipherFileIO.cpp delete mode 100644 encfs/ConfigReader.cpp delete mode 100644 encfs/ConfigVar.cpp delete mode 100644 encfs/Context.cpp delete mode 100644 encfs/DirNode.cpp delete mode 100644 encfs/FileNode.cpp delete mode 100644 encfs/MACFileIO.cpp delete mode 100644 encfs/Makefile.am delete mode 100644 encfs/MemoryPool.cpp delete mode 100644 encfs/NameIO.cpp delete mode 100644 encfs/RawFileIO.cpp delete mode 100644 encfs/SSL_Cipher.cpp delete mode 100644 encfs/SSL_Cipher.h delete mode 100644 encfs/StreamNameIO.cpp delete mode 100644 encfs/docs/Makefile.am delete mode 100644 encfs/docs/en/Makefile.am delete mode 100644 encfs/encfs.cpp delete mode 100644 encfs/encfsctl.cpp delete mode 100644 encfs/openssl.cpp delete mode 100644 encfs/test.cpp delete mode 100644 encfs/tests.t create mode 100644 fs/BlockFileIO.cpp rename {encfs => fs}/BlockFileIO.h (97%) create mode 100644 fs/BlockNameIO.cpp rename {encfs => fs}/BlockNameIO.h (95%) create mode 100644 fs/CMakeLists.txt create mode 100644 fs/CipherFileIO.cpp rename {encfs => fs}/CipherFileIO.h (85%) create mode 100644 fs/Context.cpp rename {encfs => fs}/Context.h (97%) create mode 100644 fs/DirNode.cpp rename {encfs => fs}/DirNode.h (97%) rename {encfs => fs}/FSConfig.h (94%) rename {encfs => fs}/FileIO.cpp (100%) rename {encfs => fs}/FileIO.h (97%) create mode 100644 fs/FileNode.cpp rename {encfs => fs}/FileNode.h (97%) rename {encfs => fs}/FileUtils.cpp (90%) rename {encfs => fs}/FileUtils.h (97%) create mode 100644 fs/MACFileIO.cpp rename {encfs => fs}/MACFileIO.h (97%) create mode 100644 fs/MemBlockFileIO.cpp create mode 100644 fs/MemBlockFileIO.h create mode 100644 fs/MemFileIO.cpp create mode 100644 fs/MemFileIO.h create mode 100644 fs/NameIO.cpp rename {encfs => fs}/NameIO.h (93%) rename {encfs => fs}/NullNameIO.cpp (73%) rename {encfs => fs}/NullNameIO.h (100%) create mode 100644 fs/RawFileIO.cpp rename {encfs => fs}/RawFileIO.h (96%) create mode 100644 fs/StreamNameIO.cpp rename {encfs => fs}/StreamNameIO.h (97%) create mode 100644 fs/encfs.cpp rename {encfs => fs}/encfs.h (99%) create mode 100644 fs/test.cpp create mode 100644 fs/test_BlockIO.cpp create mode 100644 fs/test_IO.cpp rename {encfs => fs}/testextpass (100%) create mode 100644 fs/testing.cpp create mode 100644 fs/testing.h delete mode 100644 m4/Makefile.am delete mode 100644 m4/ax_ext_check_header.m4 delete mode 100644 m4/ax_ext_have_lib.m4 delete mode 100644 m4/ax_pthread.m4 delete mode 100644 makedist.sh delete mode 100644 makedist2.sh.in create mode 100644 po/CMakeLists.txt delete mode 100644 po/ChangeLog delete mode 100644 po/Makefile.in.in delete mode 100644 po/Rules-quot delete mode 100644 po/boldquot.sed delete mode 100644 po/en@boldquot.header delete mode 100644 po/en@quot.header delete mode 100644 po/insert-header.sin delete mode 100644 po/quot.sed delete mode 100644 po/remove-potcdate.sin delete mode 100644 reconfig.sh delete mode 100644 subdirs create mode 100644 util/CMakeLists.txt create mode 100644 util/encfsctl.cpp rename {encfs => util}/encfsctl.pod (100%) rename {encfs => util}/encfssh (100%) rename {encfs => util}/makeKey.cpp (100%) diff --git a/ABOUT-NLS b/ABOUT-NLS deleted file mode 100644 index 83bc72e..0000000 --- a/ABOUT-NLS +++ /dev/null @@ -1,1068 +0,0 @@ -1 Notes on the Free Translation Project -*************************************** - -Free software is going international! The Free Translation Project is -a way to get maintainers of free software, translators, and users all -together, so that free software will gradually become able to speak many -languages. A few packages already provide translations for their -messages. - - If you found this `ABOUT-NLS' file inside a distribution, you may -assume that the distributed package does use GNU `gettext' internally, -itself available at your nearest GNU archive site. But you do _not_ -need to install GNU `gettext' prior to configuring, installing or using -this package with messages translated. - - Installers will find here some useful hints. These notes also -explain how users should proceed for getting the programs to use the -available translations. They tell how people wanting to contribute and -work on translations can contact the appropriate team. - - When reporting bugs in the `intl/' directory or bugs which may be -related to internationalization, you should tell about the version of -`gettext' which is used. The information can be found in the -`intl/VERSION' file, in internationalized packages. - -1.1 Quick configuration advice -============================== - -If you want to exploit the full power of internationalization, you -should configure it using - - ./configure --with-included-gettext - -to force usage of internationalizing routines provided within this -package, despite the existence of internationalizing capabilities in the -operating system where this package is being installed. So far, only -the `gettext' implementation in the GNU C library version 2 provides as -many features (such as locale alias, message inheritance, automatic -charset conversion or plural form handling) as the implementation here. -It is also not possible to offer this additional functionality on top -of a `catgets' implementation. Future versions of GNU `gettext' will -very likely convey even more functionality. So it might be a good idea -to change to GNU `gettext' as soon as possible. - - So you need _not_ provide this option if you are using GNU libc 2 or -you have installed a recent copy of the GNU gettext package with the -included `libintl'. - -1.2 INSTALL Matters -=================== - -Some packages are "localizable" when properly installed; the programs -they contain can be made to speak your own native language. Most such -packages use GNU `gettext'. Other packages have their own ways to -internationalization, predating GNU `gettext'. - - By default, this package will be installed to allow translation of -messages. It will automatically detect whether the system already -provides the GNU `gettext' functions. If not, the included GNU -`gettext' library will be used. This library is wholly contained -within this package, usually in the `intl/' subdirectory, so prior -installation of the GNU `gettext' package is _not_ required. -Installers may use special options at configuration time for changing -the default behaviour. The commands: - - ./configure --with-included-gettext - ./configure --disable-nls - -will, respectively, bypass any pre-existing `gettext' to use the -internationalizing routines provided within this package, or else, -_totally_ disable translation of messages. - - When you already have GNU `gettext' installed on your system and run -configure without an option for your new package, `configure' will -probably detect the previously built and installed `libintl.a' file and -will decide to use this. This might not be desirable. You should use -the more recent version of the GNU `gettext' library. I.e. if the file -`intl/VERSION' shows that the library which comes with this package is -more recent, you should use - - ./configure --with-included-gettext - -to prevent auto-detection. - - The configuration process will not test for the `catgets' function -and therefore it will not be used. The reason is that even an -emulation of `gettext' on top of `catgets' could not provide all the -extensions of the GNU `gettext' library. - - Internationalized packages usually have many `po/LL.po' files, where -LL gives an ISO 639 two-letter code identifying the language. Unless -translations have been forbidden at `configure' time by using the -`--disable-nls' switch, all available translations are installed -together with the package. However, the environment variable `LINGUAS' -may be set, prior to configuration, to limit the installed set. -`LINGUAS' should then contain a space separated list of two-letter -codes, stating which languages are allowed. - -1.3 Using This Package -====================== - -As a user, if your language has been installed for this package, you -only have to set the `LANG' environment variable to the appropriate -`LL_CC' combination. If you happen to have the `LC_ALL' or some other -`LC_xxx' environment variables set, you should unset them before -setting `LANG', otherwise the setting of `LANG' will not have the -desired effect. Here `LL' is an ISO 639 two-letter language code, and -`CC' is an ISO 3166 two-letter country code. For example, let's -suppose that you speak German and live in Germany. At the shell -prompt, merely execute `setenv LANG de_DE' (in `csh'), -`export LANG; LANG=de_DE' (in `sh') or `export LANG=de_DE' (in `bash'). -This can be done from your `.login' or `.profile' file, once and for -all. - - You might think that the country code specification is redundant. -But in fact, some languages have dialects in different countries. For -example, `de_AT' is used for Austria, and `pt_BR' for Brazil. The -country code serves to distinguish the dialects. - - The locale naming convention of `LL_CC', with `LL' denoting the -language and `CC' denoting the country, is the one use on systems based -on GNU libc. On other systems, some variations of this scheme are -used, such as `LL' or `LL_CC.ENCODING'. You can get the list of -locales supported by your system for your language by running the -command `locale -a | grep '^LL''. - - Not all programs have translations for all languages. By default, an -English message is shown in place of a nonexistent translation. If you -understand other languages, you can set up a priority list of languages. -This is done through a different environment variable, called -`LANGUAGE'. GNU `gettext' gives preference to `LANGUAGE' over `LANG' -for the purpose of message handling, but you still need to have `LANG' -set to the primary language; this is required by other parts of the -system libraries. For example, some Swedish users who would rather -read translations in German than English for when Swedish is not -available, set `LANGUAGE' to `sv:de' while leaving `LANG' to `sv_SE'. - - Special advice for Norwegian users: The language code for Norwegian -bokma*l changed from `no' to `nb' recently (in 2003). During the -transition period, while some message catalogs for this language are -installed under `nb' and some older ones under `no', it's recommended -for Norwegian users to set `LANGUAGE' to `nb:no' so that both newer and -older translations are used. - - In the `LANGUAGE' environment variable, but not in the `LANG' -environment variable, `LL_CC' combinations can be abbreviated as `LL' -to denote the language's main dialect. For example, `de' is equivalent -to `de_DE' (German as spoken in Germany), and `pt' to `pt_PT' -(Portuguese as spoken in Portugal) in this context. - -1.4 Translating Teams -===================== - -For the Free Translation Project to be a success, we need interested -people who like their own language and write it well, and who are also -able to synergize with other translators speaking the same language. -Each translation team has its own mailing list. The up-to-date list of -teams can be found at the Free Translation Project's homepage, -`http://translationproject.org/', in the "Teams" area. - - If you'd like to volunteer to _work_ at translating messages, you -should become a member of the translating team for your own language. -The subscribing address is _not_ the same as the list itself, it has -`-request' appended. For example, speakers of Swedish can send a -message to `sv-request@li.org', having this message body: - - subscribe - - Keep in mind that team members are expected to participate -_actively_ in translations, or at solving translational difficulties, -rather than merely lurking around. If your team does not exist yet and -you want to start one, or if you are unsure about what to do or how to -get started, please write to `coordinator@translationproject.org' to -reach the coordinator for all translator teams. - - The English team is special. It works at improving and uniformizing -the terminology in use. Proven linguistic skills are praised more than -programming skills, here. - -1.5 Available Packages -====================== - -Languages are not equally supported in all packages. The following -matrix shows the current state of internationalization, as of November -2007. The matrix shows, in regard of each package, for which languages -PO files have been submitted to translation coordination, with a -translation percentage of at least 50%. - - Ready PO files af am ar az be bg bs ca cs cy da de el en en_GB eo - +----------------------------------------------------+ - Compendium | [] [] [] [] | - a2ps | [] [] [] [] [] | - aegis | () | - ant-phone | () | - anubis | [] | - ap-utils | | - aspell | [] [] [] [] [] | - bash | [] | - bfd | | - bibshelf | [] | - binutils | | - bison | [] [] | - bison-runtime | [] | - bluez-pin | [] [] [] [] [] | - cflow | [] | - clisp | [] [] [] | - console-tools | [] [] | - coreutils | [] [] [] [] | - cpio | | - cpplib | [] [] [] | - cryptonit | [] | - dialog | | - diffutils | [] [] [] [] [] [] | - doodle | [] | - e2fsprogs | [] [] | - enscript | [] [] [] [] | - fetchmail | [] [] () [] [] | - findutils | [] | - findutils_stable | [] [] [] | - flex | [] [] [] | - fslint | | - gas | | - gawk | [] [] [] | - gcal | [] | - gcc | [] | - gettext-examples | [] [] [] [] [] | - gettext-runtime | [] [] [] [] [] | - gettext-tools | [] [] | - gip | [] | - gliv | [] [] | - glunarclock | [] | - gmult | [] [] | - gnubiff | () | - gnucash | [] [] () () [] | - gnuedu | | - gnulib | [] | - gnunet | | - gnunet-gtk | | - gnutls | [] | - gpe-aerial | [] [] | - gpe-beam | [] [] | - gpe-calendar | | - gpe-clock | [] [] | - gpe-conf | [] [] | - gpe-contacts | | - gpe-edit | [] | - gpe-filemanager | | - gpe-go | [] | - gpe-login | [] [] | - gpe-ownerinfo | [] [] | - gpe-package | | - gpe-sketchbook | [] [] | - gpe-su | [] [] | - gpe-taskmanager | [] [] | - gpe-timesheet | [] | - gpe-today | [] [] | - gpe-todo | | - gphoto2 | [] [] [] [] | - gprof | [] [] | - gpsdrive | | - gramadoir | [] [] | - grep | [] [] | - gretl | () | - gsasl | | - gss | | - gst-plugins-bad | [] [] | - gst-plugins-base | [] [] | - gst-plugins-good | [] [] [] | - gst-plugins-ugly | [] [] | - gstreamer | [] [] [] [] [] [] [] | - gtick | () | - gtkam | [] [] [] [] | - gtkorphan | [] [] | - gtkspell | [] [] [] [] | - gutenprint | [] | - hello | [] [] [] [] [] | - herrie | [] | - hylafax | | - idutils | [] [] | - indent | [] [] [] [] | - iso_15924 | | - iso_3166 | [] [] [] [] [] [] [] [] [] [] [] | - iso_3166_2 | | - iso_4217 | [] [] [] | - iso_639 | [] [] [] [] | - jpilot | [] | - jtag | | - jwhois | | - kbd | [] [] [] [] | - keytouch | [] [] | - keytouch-editor | [] | - keytouch-keyboa... | [] | - latrine | () | - ld | [] | - leafpad | [] [] [] [] [] | - libc | [] [] [] [] | - libexif | [] | - libextractor | [] | - libgpewidget | [] [] [] | - libgpg-error | [] | - libgphoto2 | [] [] | - libgphoto2_port | [] [] | - libgsasl | | - libiconv | [] [] | - libidn | [] [] [] | - lifelines | [] () | - lilypond | [] | - lingoteach | | - lprng | | - lynx | [] [] [] [] | - m4 | [] [] [] [] | - mailfromd | | - mailutils | [] | - make | [] [] | - man-db | [] [] [] | - minicom | [] [] [] | - nano | [] [] [] | - opcodes | [] | - parted | [] [] | - pilot-qof | | - popt | [] [] [] | - psmisc | [] | - pwdutils | | - qof | | - radius | [] | - recode | [] [] [] [] [] [] | - rpm | [] | - screem | | - scrollkeeper | [] [] [] [] [] [] [] [] | - sed | [] [] [] | - shared-mime-info | [] [] [] [] () [] [] [] | - sharutils | [] [] [] [] [] [] | - shishi | | - skencil | [] () | - solfege | | - soundtracker | [] [] | - sp | [] | - system-tools-ba... | [] [] [] [] [] [] [] [] [] | - tar | [] [] | - texinfo | [] [] [] | - tin | () () | - tuxpaint | [] [] [] [] [] [] | - unicode-han-tra... | | - unicode-transla... | | - util-linux | [] [] [] [] | - util-linux-ng | [] [] [] [] | - vorbis-tools | [] | - wastesedge | () | - wdiff | [] [] [] [] | - wget | [] [] [] | - xchat | [] [] [] [] [] [] [] | - xkeyboard-config | [] | - xpad | [] [] [] | - +----------------------------------------------------+ - af am ar az be bg bs ca cs cy da de el en en_GB eo - 6 0 2 1 8 26 2 40 48 2 56 88 15 1 15 18 - - es et eu fa fi fr ga gl gu he hi hr hu id is it - +--------------------------------------------------+ - Compendium | [] [] [] [] [] | - a2ps | [] [] [] () | - aegis | | - ant-phone | [] | - anubis | [] | - ap-utils | [] [] | - aspell | [] [] [] | - bash | [] | - bfd | [] [] | - bibshelf | [] [] [] | - binutils | [] [] [] | - bison | [] [] [] [] [] [] | - bison-runtime | [] [] [] [] [] | - bluez-pin | [] [] [] [] [] | - cflow | [] | - clisp | [] [] | - console-tools | | - coreutils | [] [] [] [] [] [] | - cpio | [] [] [] | - cpplib | [] [] | - cryptonit | [] | - dialog | [] [] [] | - diffutils | [] [] [] [] [] [] [] [] [] | - doodle | [] [] | - e2fsprogs | [] [] [] | - enscript | [] [] [] | - fetchmail | [] | - findutils | [] [] [] | - findutils_stable | [] [] [] [] | - flex | [] [] [] | - fslint | | - gas | [] [] | - gawk | [] [] [] [] () | - gcal | [] [] | - gcc | [] | - gettext-examples | [] [] [] [] [] [] [] | - gettext-runtime | [] [] [] [] [] [] | - gettext-tools | [] [] [] [] | - gip | [] [] [] [] | - gliv | () | - glunarclock | [] [] [] | - gmult | [] [] [] | - gnubiff | () () | - gnucash | () () () | - gnuedu | [] | - gnulib | [] [] [] | - gnunet | | - gnunet-gtk | | - gnutls | | - gpe-aerial | [] [] | - gpe-beam | [] [] | - gpe-calendar | | - gpe-clock | [] [] [] [] | - gpe-conf | [] | - gpe-contacts | [] [] | - gpe-edit | [] [] [] [] | - gpe-filemanager | [] | - gpe-go | [] [] [] | - gpe-login | [] [] [] | - gpe-ownerinfo | [] [] [] [] [] | - gpe-package | [] | - gpe-sketchbook | [] [] | - gpe-su | [] [] [] [] | - gpe-taskmanager | [] [] [] | - gpe-timesheet | [] [] [] [] | - gpe-today | [] [] [] [] | - gpe-todo | [] | - gphoto2 | [] [] [] [] [] | - gprof | [] [] [] [] [] | - gpsdrive | [] | - gramadoir | [] [] | - grep | [] [] [] | - gretl | [] [] [] () | - gsasl | [] [] | - gss | [] [] | - gst-plugins-bad | [] [] [] [] | - gst-plugins-base | [] [] [] [] | - gst-plugins-good | [] [] [] [] [] | - gst-plugins-ugly | [] [] [] [] | - gstreamer | [] [] [] | - gtick | [] [] [] | - gtkam | [] [] [] [] | - gtkorphan | [] [] | - gtkspell | [] [] [] [] [] [] [] | - gutenprint | [] | - hello | [] [] [] [] [] [] [] [] [] [] [] [] [] | - herrie | [] | - hylafax | | - idutils | [] [] [] [] [] | - indent | [] [] [] [] [] [] [] [] [] [] | - iso_15924 | [] | - iso_3166 | [] [] [] [] [] [] [] [] [] [] [] [] [] | - iso_3166_2 | [] | - iso_4217 | [] [] [] [] [] [] | - iso_639 | [] [] [] [] [] [] | - jpilot | [] [] | - jtag | [] | - jwhois | [] [] [] [] [] | - kbd | [] [] | - keytouch | [] [] [] | - keytouch-editor | [] | - keytouch-keyboa... | [] [] | - latrine | [] [] | - ld | [] [] [] [] | - leafpad | [] [] [] [] [] [] | - libc | [] [] [] [] [] | - libexif | [] | - libextractor | [] | - libgpewidget | [] [] [] [] [] | - libgpg-error | [] | - libgphoto2 | [] [] [] | - libgphoto2_port | [] [] | - libgsasl | [] [] | - libiconv | [] [] [] | - libidn | [] [] | - lifelines | () | - lilypond | [] [] [] | - lingoteach | [] [] [] | - lprng | | - lynx | [] [] [] | - m4 | [] [] [] [] | - mailfromd | | - mailutils | [] [] | - make | [] [] [] [] [] [] [] [] | - man-db | [] | - minicom | [] [] [] [] | - nano | [] [] [] [] [] [] [] | - opcodes | [] [] [] [] | - parted | [] [] [] | - pilot-qof | | - popt | [] [] [] [] | - psmisc | [] [] | - pwdutils | | - qof | [] | - radius | [] [] | - recode | [] [] [] [] [] [] [] [] | - rpm | [] [] | - screem | | - scrollkeeper | [] [] [] | - sed | [] [] [] [] [] | - shared-mime-info | [] [] [] [] [] [] | - sharutils | [] [] [] [] [] [] [] [] | - shishi | [] | - skencil | [] [] | - solfege | [] | - soundtracker | [] [] [] | - sp | [] | - system-tools-ba... | [] [] [] [] [] [] [] [] [] | - tar | [] [] [] [] [] | - texinfo | [] [] [] | - tin | [] () | - tuxpaint | [] [] | - unicode-han-tra... | | - unicode-transla... | [] [] | - util-linux | [] [] [] [] [] [] [] | - util-linux-ng | [] [] [] [] [] [] [] | - vorbis-tools | | - wastesedge | () | - wdiff | [] [] [] [] [] [] [] [] | - wget | [] [] [] [] [] [] [] [] | - xchat | [] [] [] [] [] [] [] | - xkeyboard-config | [] [] [] [] | - xpad | [] [] [] | - +--------------------------------------------------+ - es et eu fa fi fr ga gl gu he hi hr hu id is it - 85 22 14 2 48 101 61 12 2 8 2 6 53 29 1 52 - - ja ka ko ku ky lg lt lv mk mn ms mt nb ne nl nn - +--------------------------------------------------+ - Compendium | [] | - a2ps | () [] [] | - aegis | () | - ant-phone | [] | - anubis | [] [] [] | - ap-utils | [] | - aspell | [] [] | - bash | [] | - bfd | | - bibshelf | [] | - binutils | | - bison | [] [] [] | - bison-runtime | [] [] [] | - bluez-pin | [] [] [] | - cflow | | - clisp | [] | - console-tools | | - coreutils | [] | - cpio | [] | - cpplib | [] | - cryptonit | [] | - dialog | [] [] | - diffutils | [] [] [] | - doodle | | - e2fsprogs | [] | - enscript | [] | - fetchmail | [] [] | - findutils | [] | - findutils_stable | [] | - flex | [] [] | - fslint | | - gas | | - gawk | [] [] | - gcal | | - gcc | | - gettext-examples | [] [] [] | - gettext-runtime | [] [] [] | - gettext-tools | [] [] | - gip | [] [] | - gliv | [] | - glunarclock | [] [] | - gmult | [] [] [] | - gnubiff | | - gnucash | () () () | - gnuedu | | - gnulib | [] [] | - gnunet | | - gnunet-gtk | | - gnutls | [] | - gpe-aerial | [] | - gpe-beam | [] | - gpe-calendar | [] | - gpe-clock | [] [] [] | - gpe-conf | [] [] [] | - gpe-contacts | [] | - gpe-edit | [] [] [] | - gpe-filemanager | [] [] | - gpe-go | [] [] [] | - gpe-login | [] [] [] | - gpe-ownerinfo | [] [] | - gpe-package | [] [] | - gpe-sketchbook | [] [] | - gpe-su | [] [] [] | - gpe-taskmanager | [] [] [] [] | - gpe-timesheet | [] | - gpe-today | [] [] | - gpe-todo | [] | - gphoto2 | [] [] | - gprof | [] | - gpsdrive | [] | - gramadoir | () | - grep | [] [] | - gretl | | - gsasl | [] | - gss | | - gst-plugins-bad | [] | - gst-plugins-base | [] | - gst-plugins-good | [] | - gst-plugins-ugly | [] | - gstreamer | [] | - gtick | [] | - gtkam | [] [] | - gtkorphan | [] | - gtkspell | [] [] | - gutenprint | [] | - hello | [] [] [] [] [] [] [] | - herrie | [] | - hylafax | | - idutils | [] | - indent | [] [] | - iso_15924 | [] | - iso_3166 | [] [] [] [] [] [] [] [] | - iso_3166_2 | [] | - iso_4217 | [] [] [] | - iso_639 | [] [] [] [] | - jpilot | () () | - jtag | | - jwhois | [] | - kbd | [] | - keytouch | [] | - keytouch-editor | [] | - keytouch-keyboa... | | - latrine | [] | - ld | | - leafpad | [] [] | - libc | [] [] [] | - libexif | | - libextractor | | - libgpewidget | [] | - libgpg-error | | - libgphoto2 | [] | - libgphoto2_port | [] | - libgsasl | [] | - libiconv | [] | - libidn | [] [] | - lifelines | [] | - lilypond | [] | - lingoteach | [] | - lprng | | - lynx | [] [] | - m4 | [] [] | - mailfromd | | - mailutils | | - make | [] [] [] | - man-db | | - minicom | [] | - nano | [] [] [] | - opcodes | [] | - parted | [] [] | - pilot-qof | | - popt | [] [] [] | - psmisc | [] [] [] | - pwdutils | | - qof | | - radius | | - recode | [] | - rpm | [] [] | - screem | [] | - scrollkeeper | [] [] [] [] | - sed | [] [] | - shared-mime-info | [] [] [] [] [] [] [] | - sharutils | [] [] | - shishi | | - skencil | | - solfege | () () | - soundtracker | | - sp | () | - system-tools-ba... | [] [] [] [] | - tar | [] [] [] | - texinfo | [] [] | - tin | | - tuxpaint | () [] [] | - unicode-han-tra... | | - unicode-transla... | | - util-linux | [] [] | - util-linux-ng | [] [] | - vorbis-tools | | - wastesedge | [] | - wdiff | [] [] | - wget | [] [] | - xchat | [] [] [] [] | - xkeyboard-config | [] [] [] | - xpad | [] [] [] | - +--------------------------------------------------+ - ja ka ko ku ky lg lt lv mk mn ms mt nb ne nl nn - 51 2 25 3 2 0 6 0 2 2 20 0 11 1 103 6 - - or pa pl pt pt_BR rm ro ru rw sk sl sq sr sv ta - +--------------------------------------------------+ - Compendium | [] [] [] [] [] | - a2ps | () [] [] [] [] [] [] | - aegis | () () | - ant-phone | [] [] | - anubis | [] [] [] | - ap-utils | () | - aspell | [] [] [] | - bash | [] [] | - bfd | | - bibshelf | [] | - binutils | [] [] | - bison | [] [] [] [] [] | - bison-runtime | [] [] [] [] [] | - bluez-pin | [] [] [] [] [] [] [] [] [] | - cflow | [] | - clisp | [] | - console-tools | [] | - coreutils | [] [] [] [] | - cpio | [] [] [] | - cpplib | [] | - cryptonit | [] [] | - dialog | [] | - diffutils | [] [] [] [] [] [] | - doodle | [] [] | - e2fsprogs | [] [] | - enscript | [] [] [] [] [] | - fetchmail | [] [] [] | - findutils | [] [] [] | - findutils_stable | [] [] [] [] [] [] | - flex | [] [] [] [] [] | - fslint | [] | - gas | | - gawk | [] [] [] [] | - gcal | [] | - gcc | [] [] | - gettext-examples | [] [] [] [] [] [] [] [] | - gettext-runtime | [] [] [] [] [] [] [] [] | - gettext-tools | [] [] [] [] [] [] [] | - gip | [] [] [] [] | - gliv | [] [] [] [] [] [] | - glunarclock | [] [] [] [] [] [] | - gmult | [] [] [] [] | - gnubiff | () [] | - gnucash | () [] | - gnuedu | | - gnulib | [] [] [] | - gnunet | | - gnunet-gtk | [] | - gnutls | [] [] | - gpe-aerial | [] [] [] [] [] [] [] | - gpe-beam | [] [] [] [] [] [] [] | - gpe-calendar | [] [] [] [] | - gpe-clock | [] [] [] [] [] [] [] [] | - gpe-conf | [] [] [] [] [] [] [] | - gpe-contacts | [] [] [] [] [] | - gpe-edit | [] [] [] [] [] [] [] [] [] | - gpe-filemanager | [] [] | - gpe-go | [] [] [] [] [] [] [] [] | - gpe-login | [] [] [] [] [] [] [] [] | - gpe-ownerinfo | [] [] [] [] [] [] [] [] | - gpe-package | [] [] | - gpe-sketchbook | [] [] [] [] [] [] [] [] | - gpe-su | [] [] [] [] [] [] [] [] | - gpe-taskmanager | [] [] [] [] [] [] [] [] | - gpe-timesheet | [] [] [] [] [] [] [] [] | - gpe-today | [] [] [] [] [] [] [] [] | - gpe-todo | [] [] [] [] | - gphoto2 | [] [] [] [] [] [] | - gprof | [] [] [] | - gpsdrive | [] [] | - gramadoir | [] [] | - grep | [] [] [] [] | - gretl | [] [] [] | - gsasl | [] [] [] | - gss | [] [] [] [] | - gst-plugins-bad | [] [] [] | - gst-plugins-base | [] [] | - gst-plugins-good | [] [] | - gst-plugins-ugly | [] [] [] | - gstreamer | [] [] [] [] | - gtick | [] | - gtkam | [] [] [] [] [] | - gtkorphan | [] | - gtkspell | [] [] [] [] [] [] [] [] | - gutenprint | [] | - hello | [] [] [] [] [] [] [] [] | - herrie | [] [] [] | - hylafax | | - idutils | [] [] [] [] [] | - indent | [] [] [] [] [] [] [] | - iso_15924 | | - iso_3166 | [] [] [] [] [] [] [] [] [] [] [] [] [] | - iso_3166_2 | | - iso_4217 | [] [] [] [] [] [] [] | - iso_639 | [] [] [] [] [] [] [] | - jpilot | | - jtag | [] | - jwhois | [] [] [] [] | - kbd | [] [] [] | - keytouch | [] | - keytouch-editor | [] | - keytouch-keyboa... | [] | - latrine | | - ld | [] | - leafpad | [] [] [] [] [] [] | - libc | [] [] [] [] | - libexif | [] [] | - libextractor | [] [] | - libgpewidget | [] [] [] [] [] [] [] [] | - libgpg-error | [] [] [] | - libgphoto2 | [] | - libgphoto2_port | [] [] [] | - libgsasl | [] [] [] [] | - libiconv | [] [] [] | - libidn | [] [] () | - lifelines | [] [] | - lilypond | | - lingoteach | [] | - lprng | [] | - lynx | [] [] [] | - m4 | [] [] [] [] [] | - mailfromd | [] | - mailutils | [] [] [] | - make | [] [] [] [] | - man-db | [] [] [] [] | - minicom | [] [] [] [] [] | - nano | [] [] [] [] | - opcodes | [] [] | - parted | [] | - pilot-qof | | - popt | [] [] [] [] | - psmisc | [] [] | - pwdutils | [] [] | - qof | [] [] | - radius | [] [] | - recode | [] [] [] [] [] [] [] | - rpm | [] [] [] [] | - screem | | - scrollkeeper | [] [] [] [] [] [] [] | - sed | [] [] [] [] [] [] [] [] [] | - shared-mime-info | [] [] [] [] [] [] | - sharutils | [] [] [] [] | - shishi | [] | - skencil | [] [] [] | - solfege | [] | - soundtracker | [] [] | - sp | | - system-tools-ba... | [] [] [] [] [] [] [] [] [] | - tar | [] [] [] [] | - texinfo | [] [] [] [] | - tin | () | - tuxpaint | [] [] [] [] [] [] | - unicode-han-tra... | | - unicode-transla... | | - util-linux | [] [] [] [] | - util-linux-ng | [] [] [] [] | - vorbis-tools | [] | - wastesedge | | - wdiff | [] [] [] [] [] [] [] | - wget | [] [] [] [] | - xchat | [] [] [] [] [] [] [] | - xkeyboard-config | [] [] [] | - xpad | [] [] [] | - +--------------------------------------------------+ - or pa pl pt pt_BR rm ro ru rw sk sl sq sr sv ta - 0 5 77 31 53 4 58 72 3 45 46 9 45 122 3 - - tg th tk tr uk ven vi wa xh zh_CN zh_HK zh_TW zu - +---------------------------------------------------+ - Compendium | [] [] [] [] | 19 - a2ps | [] [] [] | 19 - aegis | [] | 1 - ant-phone | [] [] | 6 - anubis | [] [] [] | 11 - ap-utils | () [] | 4 - aspell | [] [] [] | 16 - bash | [] | 6 - bfd | | 2 - bibshelf | [] | 7 - binutils | [] [] [] [] | 9 - bison | [] [] [] [] | 20 - bison-runtime | [] [] [] [] | 18 - bluez-pin | [] [] [] [] [] [] | 28 - cflow | [] [] | 5 - clisp | | 9 - console-tools | [] [] | 5 - coreutils | [] [] [] | 18 - cpio | [] [] [] [] | 11 - cpplib | [] [] [] [] [] | 12 - cryptonit | [] | 6 - dialog | [] [] [] | 9 - diffutils | [] [] [] [] [] | 29 - doodle | [] | 6 - e2fsprogs | [] [] | 10 - enscript | [] [] [] | 16 - fetchmail | [] [] | 12 - findutils | [] [] [] | 11 - findutils_stable | [] [] [] [] | 18 - flex | [] [] | 15 - fslint | [] | 2 - gas | [] | 3 - gawk | [] [] [] | 16 - gcal | [] | 5 - gcc | [] [] [] | 7 - gettext-examples | [] [] [] [] [] [] | 29 - gettext-runtime | [] [] [] [] [] [] | 28 - gettext-tools | [] [] [] [] [] | 20 - gip | [] [] | 13 - gliv | [] [] | 11 - glunarclock | [] [] [] | 15 - gmult | [] [] [] [] | 16 - gnubiff | [] | 2 - gnucash | () [] | 5 - gnuedu | [] | 2 - gnulib | [] | 10 - gnunet | | 0 - gnunet-gtk | [] [] | 3 - gnutls | | 4 - gpe-aerial | [] [] | 14 - gpe-beam | [] [] | 14 - gpe-calendar | [] [] | 7 - gpe-clock | [] [] [] [] | 21 - gpe-conf | [] [] [] | 16 - gpe-contacts | [] [] | 10 - gpe-edit | [] [] [] [] [] | 22 - gpe-filemanager | [] [] | 7 - gpe-go | [] [] [] [] | 19 - gpe-login | [] [] [] [] [] | 21 - gpe-ownerinfo | [] [] [] [] | 21 - gpe-package | [] | 6 - gpe-sketchbook | [] [] | 16 - gpe-su | [] [] [] [] | 21 - gpe-taskmanager | [] [] [] [] | 21 - gpe-timesheet | [] [] [] [] | 18 - gpe-today | [] [] [] [] [] | 21 - gpe-todo | [] [] | 8 - gphoto2 | [] [] [] [] | 21 - gprof | [] [] | 13 - gpsdrive | [] | 5 - gramadoir | [] | 7 - grep | [] | 12 - gretl | | 6 - gsasl | [] [] [] | 9 - gss | [] | 7 - gst-plugins-bad | [] [] [] | 13 - gst-plugins-base | [] [] | 11 - gst-plugins-good | [] [] [] [] [] | 16 - gst-plugins-ugly | [] [] [] | 13 - gstreamer | [] [] [] | 18 - gtick | [] [] | 7 - gtkam | [] | 16 - gtkorphan | [] | 7 - gtkspell | [] [] [] [] [] [] | 27 - gutenprint | | 4 - hello | [] [] [] [] [] | 38 - herrie | [] [] | 8 - hylafax | | 0 - idutils | [] [] | 15 - indent | [] [] [] [] [] | 28 - iso_15924 | [] [] | 4 - iso_3166 | [] [] [] [] [] [] [] [] [] | 54 - iso_3166_2 | [] [] | 4 - iso_4217 | [] [] [] [] [] | 24 - iso_639 | [] [] [] [] [] | 26 - jpilot | [] [] [] [] | 7 - jtag | [] | 3 - jwhois | [] [] [] | 13 - kbd | [] [] [] | 13 - keytouch | [] | 8 - keytouch-editor | [] | 5 - keytouch-keyboa... | [] | 5 - latrine | [] [] | 5 - ld | [] [] [] [] | 10 - leafpad | [] [] [] [] [] | 24 - libc | [] [] [] | 19 - libexif | [] | 5 - libextractor | [] | 5 - libgpewidget | [] [] [] | 20 - libgpg-error | [] | 6 - libgphoto2 | [] [] | 9 - libgphoto2_port | [] [] [] | 11 - libgsasl | [] | 8 - libiconv | [] [] | 11 - libidn | [] [] | 11 - lifelines | | 4 - lilypond | [] | 6 - lingoteach | [] | 6 - lprng | [] | 2 - lynx | [] [] [] | 15 - m4 | [] [] [] | 18 - mailfromd | [] [] | 3 - mailutils | [] [] | 8 - make | [] [] [] | 20 - man-db | [] | 9 - minicom | [] | 14 - nano | [] [] [] | 20 - opcodes | [] [] | 10 - parted | [] [] [] | 11 - pilot-qof | [] | 1 - popt | [] [] [] [] | 18 - psmisc | [] [] | 10 - pwdutils | [] | 3 - qof | [] | 4 - radius | [] [] | 7 - recode | [] [] [] | 25 - rpm | [] [] [] [] | 13 - screem | [] | 2 - scrollkeeper | [] [] [] [] | 26 - sed | [] [] [] [] | 23 - shared-mime-info | [] [] [] | 29 - sharutils | [] [] [] | 23 - shishi | [] | 3 - skencil | [] | 7 - solfege | [] | 3 - soundtracker | [] [] | 9 - sp | [] | 3 - system-tools-ba... | [] [] [] [] [] [] [] | 38 - tar | [] [] [] | 17 - texinfo | [] [] [] | 15 - tin | | 1 - tuxpaint | [] [] [] | 19 - unicode-han-tra... | | 0 - unicode-transla... | | 2 - util-linux | [] [] [] | 20 - util-linux-ng | [] [] [] | 20 - vorbis-tools | [] [] | 4 - wastesedge | | 1 - wdiff | [] [] | 23 - wget | [] [] [] | 20 - xchat | [] [] [] [] | 29 - xkeyboard-config | [] [] [] | 14 - xpad | [] [] [] | 15 - +---------------------------------------------------+ - 76 teams tg th tk tr uk ven vi wa xh zh_CN zh_HK zh_TW zu - 163 domains 0 3 1 74 51 0 143 21 1 57 7 45 0 2036 - - Some counters in the preceding matrix are higher than the number of -visible blocks let us expect. This is because a few extra PO files are -used for implementing regional variants of languages, or language -dialects. - - For a PO file in the matrix above to be effective, the package to -which it applies should also have been internationalized and -distributed as such by its maintainer. There might be an observable -lag between the mere existence a PO file and its wide availability in a -distribution. - - If November 2007 seems to be old, you may fetch a more recent copy -of this `ABOUT-NLS' file on most GNU archive sites. The most -up-to-date matrix with full percentage details can be found at -`http://translationproject.org/extra/matrix.html'. - -1.6 Using `gettext' in new packages -=================================== - -If you are writing a freely available program and want to -internationalize it you are welcome to use GNU `gettext' in your -package. Of course you have to respect the GNU Library General Public -License which covers the use of the GNU `gettext' library. This means -in particular that even non-free programs can use `libintl' as a shared -library, whereas only free software can use `libintl' as a static -library or use modified versions of `libintl'. - - Once the sources are changed appropriately and the setup can handle -the use of `gettext' the only thing missing are the translations. The -Free Translation Project is also available for packages which are not -developed inside the GNU project. Therefore the information given above -applies also for every other Free Software Project. Contact -`coordinator@translationproject.org' to make the `.pot' files available -to the translation teams. - diff --git a/AUTHORS b/AUTHORS index 54a20ce..a36c116 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2,7 +2,15 @@ Valient Gough -Also, thanks to the work of many contributors, encfs as of 1.1.11 now has -full or partial translations for many languages. -See README-NLS and TRANSLATORS for more details. +With significant contributions from: + +Csaba Henk +David Rosenstrauch +Gerald Klix +Janne Hellsten +p.kosseff + + +Also, thanks to the work of many contributors, encfs has full or partial +translations for many languages. See README-NLS for more details. diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a7fe73c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_minimum_required(VERSION 2.8) +project(Encfs) + +set (ENCFS_MAJOR 2) +set (ENCFS_MINOR 0) +set (ENCFS_PATCH 0) +set (ENCFS_VERSION "${ENCFS_MAJOR}.${ENCFS_MINOR}.${ENCFS_PATCH}") + +option (BUILD_SHARED_LIBS "Build dynamic link libraries" OFF) + +set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + "${CMAKE_SOURCE_DIR}/CMakeModules/") + +set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall") + +set (CPACK_PACKAGE_NAME "Encfs") +set (CPACK_PACKAGE_VERSION_MAJOR ${ENCFS_MAJOR}) +set (CPACK_PACKAGE_VERSION_MINOR ${ENCFS_MINOR}) +set (CPACK_PACKAGE_VERSION_PATCH ${ENCFS_PATCH}) +set (CPACK_SOURCE_GENERATOR TGZ) +set (CPACK_SOURCE_IGNORE_FILES + "/_darcs/" + "/build/") +include (CPack) + +include (CheckIncludeFileCXX) +check_include_file_cxx (attr/xattr.h HAVE_ATTR_XATTR_H) +check_include_file_cxx (sys/xattr.h HAVE_SYS_XATTR_H) + +check_include_file_cxx (tr1/memory HAVE_TR1_MEMORY) +check_include_file_cxx (tr1/unordered_map HAVE_TR1_UNORDERED_MAP) +check_include_file_cxx (tr1/unordered_set HAVE_TR1_UNORDERED_SET) +check_include_file_cxx (tr1/tuple HAVE_TR1_TUPLE) + +check_include_file_cxx (valgrind/valgrind.h HAVE_VALGRIND_VALGRIND_H) +check_include_file_cxx (valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H) + +# TODO: move this to cipher directory. +find_package (OpenSSL REQUIRED) +include (OpenSSLTests) + +# Check if xattr functions take extra argument. +include (CheckCXXSourceCompiles) +CHECK_CXX_SOURCE_COMPILES ("#include + #include + int main() { getxattr(0,0,0,0,0,0); return 1; } " XATTR_ADD_OPT) + +add_definitions (-D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=26) +if (APPLE) + add_definitions (-D__FreeBSD__=10) +endif (APPLE) + +find_package (GLog REQUIRED) +include_directories (${GLOG_INCLUDE_DIRS}) + +find_program (POD2MAN pod2man) + +include_directories (${Encfs_BINARY_DIR}) +include_directories (${Encfs_SOURCE_DIR}) + +add_subdirectory(base) +add_subdirectory(cipher) +add_subdirectory(fs) +add_subdirectory(encfs) +add_subdirectory(util) +add_subdirectory(po) + diff --git a/CMakeModules/FindFUSE.cmake b/CMakeModules/FindFUSE.cmake new file mode 100644 index 0000000..2458c00 --- /dev/null +++ b/CMakeModules/FindFUSE.cmake @@ -0,0 +1,35 @@ +# Find the FUSE includes and library +# +# FUSE_INCLUDE_DIR - where to find fuse.h, etc. +# FUSE_LIBRARIES - List of libraries when using FUSE. +# FUSE_FOUND - True if FUSE lib is found. + +# check if already in cache, be silent +IF (FUSE_INCLUDE_DIR) + SET (FUSE_FIND_QUIETLY TRUE) +ENDIF (FUSE_INCLUDE_DIR) + +# find includes +FIND_PATH (FUSE_INCLUDE_DIR fuse.h + /usr/local/include/osxfuse + /usr/local/include + /usr/include +) + +# find lib +if (APPLE) + SET(FUSE_NAMES libosxfuse.dylib fuse) +else (APPLE) + SET(FUSE_NAMES fuse) +endif (APPLE) +FIND_LIBRARY(FUSE_LIBRARIES + NAMES ${FUSE_NAMES} + PATHS /lib64 /lib /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib +) + +include ("FindPackageHandleStandardArgs") +find_package_handle_standard_args ("FUSE" DEFAULT_MSG + FUSE_INCLUDE_DIR FUSE_LIBRARIES) + +mark_as_advanced (FUSE_INCLUDE_DIR FUSE_LIBRARIES) + diff --git a/CMakeModules/FindGLog.cmake b/CMakeModules/FindGLog.cmake new file mode 100644 index 0000000..a4960ee --- /dev/null +++ b/CMakeModules/FindGLog.cmake @@ -0,0 +1,59 @@ +# Try to find the libglog libraries +# Once done this will define : +# +# Glog_FOUND - system has libglog +# Glog_INCLUDE_DIRS - the libglog include directory +# Glog_LIBRARIES - libglog library + +# Inputs to this module: +# GLOG_ROOT The preferred installation prefix for searching for glog. Set +# this if the module has problems finding the proper glog installation. + +# If GLOG_ROOT was defined in the environment, use it. +IF (NOT GLOG_ROOT AND NOT $ENV{GLOG_ROOT} STREQUAL "") + SET(GLOG_ROOT $ENV{GLOG_ROOT}) +ENDIF(NOT GLOG_ROOT AND NOT $ENV{GLOG_ROOT} STREQUAL "") +IF( GLOG_ROOT ) + file(TO_CMAKE_PATH ${GLOG_ROOT} GLOG_ROOT) +ENDIF( GLOG_ROOT ) + +SET (GLOG_INCLUDE_DIRS) +SET (GLOG_LIBRARIES) +IF(WIN32) + IF(MSVC) + FIND_PATH(GLOG_INCLUDE_DIRS NAMES src/windows/glog/logging.h HINTS ${GLOG_ROOT}) + IF(GLOG_INCLUDE_DIRS) + SET(GLOG_INCLUDE_DIRS ${GLOG_INCLUDE_DIRS}/src/windows) + ENDIF(GLOG_INCLUDE_DIRS) + + IF (CMAKE_BUILD_TYPE STREQUAL "Release") + message (STATUS " searching ${GLOG_ROOT}/Release/libglog.lib ...") + FIND_LIBRARY(GLOG_LIBRARIES NAMES libglog.lib HINTS ${GLOG_ROOT}/Release $ENV{LIB} PATH_SUFFIXES ".lib") + ELSE (CMAKE_BUILD_TYPE STREQUAL "Release") + message (STATUS " searching ${GLOG_ROOT}/Debug/libglog.lib ...") + FIND_LIBRARY(GLOG_LIBRARIES NAMES libglog.lib HINTS ${GLOG_ROOT}/Debug $ENV{LIB} PATH_SUFFIXES ".lib") + ENDIF (CMAKE_BUILD_TYPE STREQUAL "Release") + ELSE(MSVC) + SET(Glog_FOUND FALSE) + message (STATUS " Crap. this module supports only MSVC in Windows.") + ENDIF(MSVC) +ELSE(WIN32) + FIND_PATH(GLOG_INCLUDE_DIRS NAMES glog/logging.h HINTS ${GLOG_ROOT}/include ${GLOG_ROOT} /include/ /usr/include/ /usr/local/include/ /opt/local/include/) + FIND_LIBRARY(GLOG_LIBRARIES NAMES glog HINTS ${GLOG_ROOT}/lib ${GLOG_ROOT} /lib /usr/lib /usr/local/lib /opt/local/lib) +ENDIF(WIN32) + +IF(GLOG_INCLUDE_DIRS AND GLOG_LIBRARIES) + SET(Glog_FOUND TRUE) + message (STATUS " glog found in include=${GLOG_INCLUDE_DIRS},lib=${GLOG_LIBRARIES}") +ELSE(GLOG_INCLUDE_DIRS AND GLOG_LIBRARIES) + SET(Glog_FOUND FALSE) + message (STATUS " glog not found. Please set GLOG_ROOT to the root directory containing glog.") + IF(GLOG_INCLUDE_DIRS) + message (STATUS " include=${GLOG_INCLUDE_DIRS}, but lib not found") + ENDIF(GLOG_INCLUDE_DIRS) + IF(GLOG_LIBRARIES) + message (STATUS " lib=${GLOG_LIBRARIES}, but include not found") + ENDIF(GLOG_LIBRARIES) +ENDIF(GLOG_INCLUDE_DIRS AND GLOG_LIBRARIES) + +MARK_AS_ADVANCED(GLOG_INCLUDE_DIRS GLOG_LIBRARIES) diff --git a/CMakeModules/FindTinyXML.cmake b/CMakeModules/FindTinyXML.cmake new file mode 100644 index 0000000..2f3bc3c --- /dev/null +++ b/CMakeModules/FindTinyXML.cmake @@ -0,0 +1,26 @@ +# - Find TinyXML +# Find the native TinyXML includes and library +# +# TINYXML_FOUND - True if TinyXML found. +# TINYXML_INCLUDE_DIR - where to find tinyxml.h, etc. +# TINYXML_LIBRARIES - List of libraries when using TinyXML. +# + +IF( TINYXML_INCLUDE_DIR ) + # Already in cache, be silent + SET( TinyXML_FIND_QUIETLY TRUE ) +ENDIF( TINYXML_INCLUDE_DIR ) + +FIND_PATH( TINYXML_INCLUDE_DIR "tinyxml.h" + PATH_SUFFIXES "tinyxml" ) + +FIND_LIBRARY( TINYXML_LIBRARIES + NAMES "tinyxml" + PATH_SUFFIXES "tinyxml" ) + +# handle the QUIETLY and REQUIRED arguments and set TINYXML_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE( "FindPackageHandleStandardArgs" ) +FIND_PACKAGE_HANDLE_STANDARD_ARGS( "TinyXML" DEFAULT_MSG TINYXML_INCLUDE_DIR TINYXML_LIBRARIES ) + +MARK_AS_ADVANCED( TINYXML_INCLUDE_DIR TINYXML_LIBRARIES ) diff --git a/CMakeModules/GettextTranslate.cmake b/CMakeModules/GettextTranslate.cmake new file mode 100644 index 0000000..e6b5d20 --- /dev/null +++ b/CMakeModules/GettextTranslate.cmake @@ -0,0 +1,287 @@ +# Copyright (c) 2012, Jarryd Beck +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + + +# This module creates build rules for updating translation files made +# with gettext +# In your top level CMakeLists.txt, do +# include(GettextTranslate) +# then in any po directory where you want things to be translated, write +# GettextTranslate() +# +# This module also finds the gettext binaries. If these are in a non-standard +# location, you can define the following variables to provide paths to search +# in +# GettextTranslate_BINARIES --- a path in which to look for every program +# GettextTranslate_XGETTEXT --- the xgettext program +# GettextTranslate_MSGINIT --- the msginit program +# GettextTranslate_MSGFILTER --- the msgfilter program +# GettextTranslate_MSGCONV --- the msgconv program +# GettextTranslate_MSGMERGE --- the msgmerge program +# GettextTranslate_MSGFMT --- the msgfmt program +# these are searched first before $PATH, so set this if you have your own +# version that overrides the system version +# +# it reads variables from Makevars, one of the most important being DOMAIN +# it reads the languages to generate from LINGUAS +# +# it adds the following targets +# update-po +# update-gmo +# ${DOMAIN}-pot.update +# generate-${DOMAIN}-${lang}-po +# generate-${DOMAIN}-${lang}-gmo +# +# where ${DOMAIN} is the DOMAIN variable read from Makevars +# and ${lang} is each language mentioned in LINGUAS +# +# if you want update-gmo to be added to the "all" target, then define the +# variable GettextTranslate_ALL before including this file +# +# by default, the gmo files are built in the source directory. If you want +# them to be built in the binary directory, then define the variable +# GettextTranslate_GMO_BINARY + + + +# add the update-po and update-gmo targets, the actual files that need to +# depend on this will be added as we go + +if (DEFINED GettextTranslate_ALL) + set(_addToALL "ALL") +endif() + +add_custom_target(update-po) +add_custom_target(update-gmo ${_addToALL}) + +#look for all the programs +#xgettext, msginit, msgfilter, msgconv, msgmerge, msgfmt + +function(REQUIRE_BINARY binname varname) + if (defined ${${varname}-NOTFOUND}) + message(FATAL_ERROR "Could not find " binname) + endif() +endfunction() + +find_program(GettextTranslate_XGETTEXT_EXECUTABLE xgettext + HINTS ${GettextTranslate_XGETTEXT} ${GettextTranslate_BINARIES} +) +REQUIRE_BINARY(xgettext GettextTranslate_XGETTEXT_EXECUTABLE) + +find_program(GettextTranslate_MSGINIT_EXECUTABLE msginit + HINTS ${GettextTranslate_MSGINIT} ${GettextTranslate_BINARIES} +) +REQUIRE_BINARY(msginit GettextTranslate_MSGINIT_EXECUTABLE) + +find_program(GettextTranslate_MSGFILTER_EXECUTABLE msgfilter + HINTS ${GettextTranslate_MSGFILTER} ${GettextTranslate_BINARIES} +) +REQUIRE_BINARY(msgfilter GettextTranslate_MSGFILTER_EXECUTABLE) + +find_program(GettextTranslate_MSGCONV_EXECUTABLE msgconv + HINTS ${GettextTranslate_MSGCONV} ${GettextTranslate_BINARIES} +) +REQUIRE_BINARY(msgconv GettextTranslate_MSGCONV_EXECUTABLE) + +find_program(GettextTranslate_MSGMERGE_EXECUTABLE msgmerge + HINTS ${GettextTranslate_MSGMERGE} ${GettextTranslate_BINARIES} +) +REQUIRE_BINARY(msgmerge GettextTranslate_MSGMERGE_EXECUTABLE) + +find_program(GettextTranslate_MSGFMT_EXECUTABLE msgfmt + HINTS ${GettextTranslate_MSGFMT} ${GettextTranslate_BINARIES} +) +REQUIRE_BINARY(msgfmt GettextTranslate_MSGFMT_EXECUTABLE) + +mark_as_advanced( + GettextTranslate_MSGCONV_EXECUTABLE + GettextTranslate_MSGFILTER_EXECUTABLE + GettextTranslate_MSGFMT_EXECUTABLE + GettextTranslate_MSGINIT_EXECUTABLE + GettextTranslate_MSGMERGE_EXECUTABLE + GettextTranslate_XGETTEXT_EXECUTABLE +) + +macro(GettextTranslate) + + if(GettextTranslate_GMO_BINARY) + set (GMO_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}) + else() + set (GMO_BUILD_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + endif() + + if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/POTFILES.in) + message(FATAL_ERROR "There is no POTFILES.in in + ${CMAKE_CURRENT_SOURCE_DIR}") + endif() + + if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/Makevars) + message(FATAL_ERROR "There is no Makevars in ${CMAKE_CURRENT_SOURCE_DIR}") + endif() + + file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/Makevars makevars + REGEX "^[^=]+=(.*)$" + ) + + foreach(makevar ${makevars}) + string(REGEX REPLACE "^([^= ]+) =[ ]?(.*)$" "\\1" MAKEVAR_KEY ${makevar}) + string(REGEX REPLACE "^([^= ]+) =[ ]?(.*)$" "\\2" + MAKEVAR_${MAKEVAR_KEY} ${makevar}) + endforeach() + + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/POTFILES.in + ${CMAKE_CURRENT_BINARY_DIR}/POTFILES + COPYONLY + ) + + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/LINGUAS + ${CMAKE_CURRENT_BINARY_DIR}/LINGUAS + COPYONLY + ) + + #set the directory to not clean + set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + PROPERTY CLEAN_NO_CUSTOM true) + + file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/POTFILES.in potfiles + REGEX "^[^#].*" + ) + + foreach(potfile ${potfiles}) + list(APPEND source_translatable + ${CMAKE_CURRENT_SOURCE_DIR}/${MAKEVAR_top_builddir}/${potfile}) + endforeach() + + set(TEMPLATE_FILE ${MAKEVAR_DOMAIN}.pot) + set(TEMPLATE_FILE_ABS ${CMAKE_CURRENT_SOURCE_DIR}/${TEMPLATE_FILE}) + string(REGEX MATCHALL "[^ ]+" XGETTEXT_OPTS ${MAKEVAR_XGETTEXT_OPTIONS}) + #add_custom_target(${MAKEVAR_DOMAIN}.pot-update DEPENDS + # ${TEMPLATE_FILE_ABS} + #) + + add_custom_target(${MAKEVAR_DOMAIN}.pot-update + COMMAND ${GettextTranslate_XGETTEXT_EXECUTABLE} ${XGETTEXT_OPTS} + -o ${TEMPLATE_FILE_ABS} + --default-domain=${MAKEVAR_DOMAIN} + --add-comments=TRANSLATORS: + --copyright-holder=${MAKEVAR_COPYRIGHT_HOLDER} + --msgid-bugs-address="${MAKEVAR_MSGID_BUGS_ADDRESS}" + --directory=${MAKEVAR_top_builddir} + --files-from=${CMAKE_CURRENT_BINARY_DIR}/POTFILES + --package-version=${VERSION} + --package-name=${CMAKE_PROJECT_NAME} + DEPENDS ${source_translatable} + ${CMAKE_CURRENT_SOURCE_DIR}/POTFILES.in + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + + #add_custom_command(OUTPUT ${TEMPLATE_FILE_ABS} + # COMMAND ${GettextTranslate_XGETTEXT_EXECUTABLE} ${XGETTEXT_OPTS} + # -o ${TEMPLATE_FILE_ABS} + # --default-domain=${MAKEVAR_DOMAIN} + # --add-comments=TRANSLATORS: + # --copyright-holder=${MAKEVAR_COPYRIGHT_HOLDER} + # --msgid-bugs-address="${MAKEVAR_MSGID_BUGS_ADDRESS}" + # --directory=${MAKEVAR_top_builddir} + # --files-from=${CMAKE_CURRENT_BINARY_DIR}/POTFILES + # --package-version=${VERSION} + # --package-name=${CMAKE_PROJECT_NAME} + # DEPENDS ${source_translatable} + # ${CMAKE_CURRENT_SOURCE_DIR}/POTFILES.in + # WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + #) + + #add_dependencies(update-po ${MAKEVAR_DOMAIN}.pot-update) + + file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/LINGUAS LINGUAS + REGEX "^[^#].*") + string(REGEX MATCHALL "[^ ]+" languages ${LINGUAS}) + + foreach(lang ${languages}) + set(PO_FILE_NAME "${CMAKE_CURRENT_SOURCE_DIR}/${lang}.po") + set(GMO_FILE_NAME "${GMO_BUILD_DIR}/${lang}.gmo") + set(PO_TARGET "generate-${MAKEVAR_DOMAIN}-${lang}-po") + set(GMO_TARGET "generate-${MAKEVAR_DOMAIN}-${lang}-gmo") + list(APPEND po_files ${PO_TARGET}) + list(APPEND gmo_files ${GMO_TARGET}) + + if(${lang} MATCHES "en@(.*)quot") + + add_custom_command(OUTPUT ${lang}.insert-header + COMMAND + sed -e "'/^#/d'" -e 's/HEADER/${lang}.header/g' + ${CMAKE_CURRENT_SOURCE_DIR}/insert-header.sin > ${lang}.insert-header + ) + + #generate the en@quot files + add_custom_command(OUTPUT ${PO_FILE_NAME} + COMMAND + ${GettextTranslate_MSGINIT_EXECUTABLE} -i ${TEMPLATE_FILE_ABS} + --no-translator -l ${lang} + -o - 2>/dev/null + | sed -f ${CMAKE_CURRENT_BINARY_DIR}/${lang}.insert-header + | ${GettextTranslate_MSGCONV_EXECUTABLE} -t UTF-8 + | ${GettextTranslate_MSGFILTER_EXECUTABLE} sed -f + ${CMAKE_CURRENT_SOURCE_DIR}/`echo ${lang} + | sed -e 's/.*@//'`.sed 2>/dev/null > + ${PO_FILE_NAME} + DEPENDS ${lang}.insert-header ${TEMPLATE_FILE_ABS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + + else() + + add_custom_command(OUTPUT ${PO_FILE_NAME} + COMMAND ${GettextTranslate_MSGMERGE_EXECUTABLE} --lang=${lang} + ${PO_FILE_NAME} ${TEMPLATE_FILE_ABS} + -o ${PO_FILE_NAME}.new + COMMAND mv ${PO_FILE_NAME}.new ${PO_FILE_NAME} + DEPENDS ${TEMPLATE_FILE_ABS} + ) + + endif() + + add_custom_command(OUTPUT ${GMO_FILE_NAME} + COMMAND ${GettextTranslate_MSGFMT_EXECUTABLE} -c --statistics --verbose + -o ${GMO_FILE_NAME} ${PO_FILE_NAME} + DEPENDS ${PO_TARGET} + ) + add_custom_target(${GMO_TARGET} DEPENDS ${GMO_FILE_NAME}) + + add_custom_target(${PO_TARGET} DEPENDS ${PO_FILE_NAME}) + add_dependencies(${PO_TARGET} ${MAKEVAR_DOMAIN}.pot-update) + + install(FILES ${GMO_FILE_NAME} DESTINATION + ${LOCALEDIR}/${lang}/LC_MESSAGES + RENAME ${MAKEVAR_DOMAIN}.mo + ) + + endforeach() + + add_dependencies(update-po ${po_files}) + add_dependencies(update-gmo ${gmo_files}) + +#string(REGEX MATCH "^[^=]+=(.*)$" parsed_variables ${makevars}) + +endmacro() diff --git a/CMakeModules/OpenSSLTests.cmake b/CMakeModules/OpenSSLTests.cmake new file mode 100644 index 0000000..2e64c33 --- /dev/null +++ b/CMakeModules/OpenSSLTests.cmake @@ -0,0 +1,24 @@ + +include (CheckFunctionExists) + +set (CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) +set (CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) + +check_function_exists (EVP_bf_cbc HAVE_EVP_BF) +if (NOT HAVE_EVP_BF) + message (STATUS " Blowfish support disabled.") +endif (NOT HAVE_EVP_BF) + +check_function_exists (EVP_aes_128_cbc HAVE_EVP_AES) +if (NOT HAVE_EVP_AES) + message (STATUS " AES support disabled.") +endif (NOT HAVE_EVP_AES) + +check_function_exists (EVP_aes_128_xts HAVE_EVP_AES_XTS) +if (NOT HAVE_EVP_AES_XTS) + message (STATUS " AES/XTS support disabled.") +endif (NOT HAVE_EVP_AES_XTS) + +set (CMAKE_REQUIRED_LIBRARIES) +set (CMAKE_REQUIRED_INCLUDES) + diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index 4908fb0..0000000 --- a/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ - -if BUILD_NLS -NLS_DIR = po -endif - -SUBDIRS = encfs m4 $(NLS_DIR) - - -EXTRA_DIST = config.rpath mkinstalldirs encfs.spec makedist.sh makedist2.sh \ - intl/autosprintf.h intl/autosprintf.cpp intl/gettext.h - -AUTOMAKE_OPTIONS = foreign - -MAINTAINERCLEANFILES = aclocal.m4 - - -ACLOCAL_AMFLAGS = -I m4 diff --git a/Makefile.common b/Makefile.common deleted file mode 100644 index f6faeb4..0000000 --- a/Makefile.common +++ /dev/null @@ -1,2 +0,0 @@ -KDE_OPTIONS = qtonly - diff --git a/Makefile.dist b/Makefile.dist deleted file mode 100644 index 21faf23..0000000 --- a/Makefile.dist +++ /dev/null @@ -1,5 +0,0 @@ -default: all - -all: - autoreconf -if - diff --git a/README b/README index 7cb63cb..9be3259 100644 --- a/README +++ b/README @@ -35,12 +35,13 @@ Usage: Technology: - - Encfs uses algorithms from third-party libraries (OpenSSL is the default) to - encrypt data and filenames. + - Encfs uses algorithms from the third-party library OpenSSL to encrypt data + and filenames. - - a user supplied password is used to decrypt a volume key, and the volume key - is used for encrypting all file names and contents. This makes it possible - to change the password without needing to re-encrypt all files. + - a user supplied password is used to decrypt a randomly generated volume key, + and the volume key is used for encrypting all file names and contents. This + makes it possible to change the password without needing to re-encrypt all + files. - EncFS has two encryption modes, which are used in different places: - Stream encryption: diff --git a/README-NLS b/README-NLS index 422156d..66bfc37 100644 --- a/README-NLS +++ b/README-NLS @@ -1,67 +1,6 @@ -Quick configuration advice -========================== - -The configuration script will automatically find and make use of your installed -'gettext' package. If you do not have gettext installed, or do not want -internationalization support included in the build, then you can disable native -language support using - ./configu --disable-nls - -Using This Package -================== - -As a user, if your language has been installed for this package, you -only have to set the `LANG' environment variable to the appropriate -`LL_CC' combination. Here `LL' is an ISO 639 two-letter language code, -and `CC' is an ISO 3166 two-letter country code. For example, let's -suppose that you speak German and live in Germany. At the shell -prompt, merely execute `setenv LANG de_DE' (in `csh'), -`export LANG; LANG=de_DE' (in `sh') or `export LANG=de_DE' (in `bash'). -This can be done from your `.login' or `.profile' file, once and for -all. - - You might think that the country code specification is redundant. -But in fact, some languages have dialects in different countries. For -example, `de_AT' is used for Austria, and `pt_BR' for Brazil. The -country code serves to distinguish the dialects. - - The locale naming convention of `LL_CC', with `LL' denoting the -language and `CC' denoting the country, is the one use on systems based -on GNU libc. On other systems, some variations of this scheme are -used, such as `LL' or `LL_CC.ENCODING'. You can get the list of -locales supported by your system for your country by running the command -`locale -a | grep '^LL''. - - Not all programs have translations for all languages. By default, an -English message is shown in place of a nonexistent translation. If you -understand other languages, you can set up a priority list of languages. -This is done through a different environment variable, called -`LANGUAGE'. GNU `gettext' gives preference to `LANGUAGE' over `LANG' -for the purpose of message handling, but you still need to have `LANG' -set to the primary language; this is required by other parts of the -system libraries. For example, some Swedish users who would rather -read translations in German than English for when Swedish is not -available, set `LANGUAGE' to `sv:de' while leaving `LANG' to `sv_SE'. - - Special advice for Norwegian users: The language code for Norwegian -bokma*l changed from `no' to `nb' recently (in 2003). During the -transition period, while some message catalogs for this language are -installed under `nb' and some older ones under `no', it's recommended -for Norwegian users to set `LANGUAGE' to `nb:no' so that both newer and -older translations are used. - - In the `LANGUAGE' environment variable, but not in the `LANG' -environment variable, `LL_CC' combinations can be abbreviated as `LL' -to denote the language's main dialect. For example, `de' is equivalent -to `de_DE' (German as spoken in Germany), and `pt' to `pt_PT' -(Portuguese as spoken in Portugal) in this context. - -Translating -=========== - EncFS is registered with Rosetta - an online interface for supplying -translations. See https://launchpad.ubuntu.com/rosetta/products/encfs +translations. See https://translations.launchpad.net/encfs If your language is not included in this distribution, you may want to check if translated text is already available online in Rosetta. diff --git a/TRANSLATORS b/TRANSLATORS deleted file mode 100644 index 58e520f..0000000 --- a/TRANSLATORS +++ /dev/null @@ -1,7 +0,0 @@ -Many people have contributed translations for EncFS. Thank you for making -EncFS easier for everyone to use! - -If you would like to help with translations, please use the online -interface provided by Canonical Ltd (the makers of Ubuntu Linux): - https://translations.launchpad.net/encfs/main/ - diff --git a/base/CMakeLists.txt b/base/CMakeLists.txt new file mode 100644 index 0000000..e08d26c --- /dev/null +++ b/base/CMakeLists.txt @@ -0,0 +1,29 @@ +find_package (Protobuf REQUIRED) +include_directories (${PROTOBUF_INCLUDE_DIR}) + +find_package (TinyXML REQUIRED) +include_directories (${TINYXML_INCLUDE_DIR}) +set (LIBS ${LIBS} ${TINYXML_LIBRARIES}) + +protobuf_generate_cpp (PROTO_SRCS PROTO_HDRS config.proto) + +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/config.h) + +add_library (encfs-base + autosprintf.cpp + base64.cpp + ConfigReader.cpp + ConfigVar.cpp + Error.cpp + Interface.cpp + XmlReader.cpp + ${PROTO_SRCS} + ${PROTO_HDRS} +) + +target_link_libraries (encfs-base + ${PROTOBUF_LIBRARY} + ${TINYXML_LIBRARIES} +) + diff --git a/base/ConfigReader.cpp b/base/ConfigReader.cpp new file mode 100644 index 0000000..1ba71dd --- /dev/null +++ b/base/ConfigReader.cpp @@ -0,0 +1,157 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004-2013, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "base/ConfigReader.h" + +#include + +#include +#include +#include +#include +#include + + +using namespace std; + + +ConfigReader::ConfigReader() +{ +} + +ConfigReader::~ConfigReader() +{ +} + +// read the entire file into a ConfigVar instance and then use that to decode +// into mapped variables. +bool ConfigReader::load(const char *fileName) +{ + struct stat stbuf; + memset( &stbuf, 0, sizeof(struct stat)); + if( lstat( fileName, &stbuf ) != 0) + return false; + + int size = stbuf.st_size; + + int fd = open( fileName, O_RDONLY ); + if(fd < 0) + return false; + + char *buf = new char[size]; + + int res = ::read( fd, buf, size ); + close( fd ); + + if( res != size ) + { + LOG(WARNING) << "Partial read of config file, expecting " + << size << " bytes, got " << res; + delete[] buf; + return false; + } + + ConfigVar in; + in.write( (unsigned char *)buf, size ); + delete[] buf; + + return loadFromVar( in ); +} + +bool ConfigReader::loadFromVar(ConfigVar &in) +{ + in.resetOffset(); + + // parse. + int numEntries = in.readInt(); + + for(int i=0; i> key >> value; + + if(key.length() == 0) + { + LOG(ERROR) << "Invalid key encoding in buffer"; + return false; + } + ConfigVar newVar( value ); + vars.insert( make_pair( key, newVar ) ); + } + + return true; +} + +bool ConfigReader::save(const char *fileName) const +{ + // write everything to a ConfigVar, then output to disk + ConfigVar out = toVar(); + + int fd = ::open( fileName, O_RDWR | O_CREAT, 0640 ); + if(fd >= 0) + { + int retVal = ::write( fd, out.buffer(), out.size() ); + close( fd ); + if(retVal != out.size()) + { + LOG(ERROR) << "Error writing to config file " << fileName; + return false; + } + } else + { + LOG(ERROR) << "Unable to open or create file " << fileName; + return false; + } + + return true; +} + +ConfigVar ConfigReader::toVar() const +{ + // write everything to a ConfigVar, then output to disk + ConfigVar out; + out.writeInt( vars.size() ); + map::const_iterator it; + for(it = vars.begin(); it != vars.end(); ++it) + { + out.writeInt( it->first.size() ); + out.write( (unsigned char*)it->first.data(), it->first.size() ); + out.writeInt( it->second.size() ); + out.write( (unsigned char*)it->second.buffer(), it->second.size() ); + } + + return out; +} + +ConfigVar ConfigReader::operator[] ( const std::string &varName ) const +{ + // read only + map::const_iterator it = vars.find( varName ); + if( it == vars.end() ) + return ConfigVar(); + else + return it->second; +} + +ConfigVar &ConfigReader::operator[] ( const std::string &varName ) +{ + return vars[ varName ]; +} + diff --git a/encfs/ConfigReader.h b/base/ConfigReader.h similarity index 98% rename from encfs/ConfigReader.h rename to base/ConfigReader.h index 4d6bf71..da521e3 100644 --- a/encfs/ConfigReader.h +++ b/base/ConfigReader.h @@ -24,7 +24,7 @@ #include #include -#include "ConfigVar.h" +#include "base/ConfigVar.h" /* handles Configuration load / store for Encfs filesystems. diff --git a/base/ConfigVar.cpp b/base/ConfigVar.cpp new file mode 100644 index 0000000..eb1a1a1 --- /dev/null +++ b/base/ConfigVar.cpp @@ -0,0 +1,253 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "base/ConfigVar.h" +#include "base/Error.h" + +#include +#include + +#ifndef MIN +inline int MIN(int a, int b) +{ + return (a < b) ? a : b; +} +#endif + + +ConfigVar::ConfigVar() + : pd( new ConfigVarData ) +{ + pd->offset = 0; +} + +ConfigVar::ConfigVar(const std::string &buf) + : pd( new ConfigVarData ) +{ + pd->buffer = buf; + pd->offset = 0; +} + +ConfigVar::ConfigVar(const ConfigVar &src) +{ + pd = src.pd; +} + +ConfigVar::~ConfigVar() +{ + pd.reset(); +} + +ConfigVar & ConfigVar::operator = (const ConfigVar &src) +{ + if(src.pd == pd) + return *this; + else + pd = src.pd; + + return *this; +} + +void ConfigVar::resetOffset() +{ + pd->offset = 0; +} + +int ConfigVar::read(unsigned char *buffer_, int bytes) const +{ + int toCopy = MIN( bytes, pd->buffer.size() - pd->offset ); + + if(toCopy > 0) + memcpy( buffer_, pd->buffer.data() + pd->offset, toCopy ); + + pd->offset += toCopy; + + return toCopy; +} + +int ConfigVar::write(const unsigned char *data, int bytes) +{ + if(pd->buffer.size() == (unsigned int)pd->offset) + { + pd->buffer.append( (const char *)data, bytes ); + } else + { + pd->buffer.insert( pd->offset, (const char *)data, bytes ); + } + + pd->offset += bytes; + + return bytes; +} + +int ConfigVar::size() const +{ + return pd->buffer.size(); +} + +const char *ConfigVar::buffer() const +{ + return pd->buffer.data(); +} + +int ConfigVar::at() const +{ + return pd->offset; +} + +void ConfigVar::writeString(const char *data, int bytes) +{ + writeInt( bytes ); + write( (const unsigned char *)data, bytes ); +} + + +// convert integer to BER encoded integer +void ConfigVar::writeInt(int val) +{ + // we can represent 7 bits per char output, so a 32bit number may take up + // to 5 bytes. + // first byte: 0x0000007f 0111,1111 + // second byte: 0x00003f80 0011,1111 1000,0000 + // third byte: 0x001fb000 0000,0000 0001,1111 1100,0000 0000,0000 + // fourth byte: 0x0fe00000 0000,1111 1110,0000 + // fifth byte: 0xf0000000 1111,0000 + unsigned char digit[5]; + + digit[4] = (unsigned char)((val & 0x0000007f)); + digit[3] = 0x80 | (unsigned char)((val & 0x00003f80) >> 7); + digit[2] = 0x80 | (unsigned char)((val & 0x001fc000) >> 14); + digit[1] = 0x80 | (unsigned char)((val & 0x0fe00000) >> 21); + digit[0] = 0x80 | (unsigned char)((val & 0xf0000000) >> 28); + + // find the starting point - we only need to output starting at the most + // significant non-zero digit.. + int start = 0; + while(digit[start] == 0x80) + ++start; + + write( digit + start, 5-start ); +} + +int ConfigVar::readInt() const +{ + const unsigned char * buf = (const unsigned char *)buffer(); + int bytes = this->size(); + int offset = at(); + int value = 0; + bool highBitSet; + + rAssert( offset < bytes ); + + do + { + unsigned char tmp = buf[offset++]; + highBitSet = tmp & 0x80; + + value = (value << 7) | (int)(tmp & 0x7f); + } while(highBitSet && offset < bytes); + + pd->offset = offset; + + // should never end up with a negative number.. + rAssert( value >= 0 ); + + return value; +} + +int ConfigVar::readInt( int defaultValue ) const +{ + int bytes = this->size(); + int offset = at(); + + if(offset >= bytes) + return defaultValue; + else + return readInt(); +} + +bool ConfigVar::readBool( bool defaultValue ) const +{ + int tmp = readInt( defaultValue ? 1 : 0 ); + return (tmp != 0); +} + +ConfigVar & operator << (ConfigVar &src, bool value) +{ + src.writeInt( value ? 1 : 0 ); + return src; +} + +ConfigVar & operator << (ConfigVar &src, int var) +{ + src.writeInt( var ); + return src; +} + +ConfigVar & operator << (ConfigVar &src, const std::string &str) +{ + src.writeString( str.data(), str.length() ); + return src; +} + +const ConfigVar & operator >> (const ConfigVar &src, bool &result) +{ + int tmp = src.readInt(); + result = (tmp != 0); + return src; +} + +const ConfigVar & operator >> (const ConfigVar &src, int &result) +{ + result = src.readInt(); + return src; +} + +const ConfigVar & operator >> (const ConfigVar &src, std::string &result) +{ + int length = src.readInt(); + LOG_IF(WARNING, length <= 0) << "Invalid config length " << length; + + int readLen; + + unsigned char tmpBuf[32]; + if(length > (int)sizeof(tmpBuf)) + { + unsigned char *ptr = new unsigned char[length]; + readLen = src.read( ptr, length ); + result.assign( (char*)ptr, length ); + delete[] ptr; + } else + { + readLen = src.read( tmpBuf, length ); + result.assign( (char*)tmpBuf, length ); + } + + if(readLen != length) + { + VLOG(1) << "string encoded as size " << length + << " bytes, read " << readLen; + } + + rAssert(readLen == length); + + return src; +} + diff --git a/encfs/ConfigVar.h b/base/ConfigVar.h similarity index 98% rename from encfs/ConfigVar.h rename to base/ConfigVar.h index 4a51304..5dcbaa7 100644 --- a/encfs/ConfigVar.h +++ b/base/ConfigVar.h @@ -22,7 +22,7 @@ #define _ConfigVar_incl_ #include -#include "shared_ptr.h" +#include "base/shared_ptr.h" class ConfigVar { diff --git a/base/Error.cpp b/base/Error.cpp new file mode 100644 index 0000000..ee628e0 --- /dev/null +++ b/base/Error.cpp @@ -0,0 +1,7 @@ +#include "base/Error.h" + +Error::Error(const char *msg) + : runtime_error(msg) +{ +} + diff --git a/base/Error.h b/base/Error.h new file mode 100644 index 0000000..836af32 --- /dev/null +++ b/base/Error.h @@ -0,0 +1,25 @@ +#ifndef _Error_incl_ +#define _Error_incl_ + +#include +#include + +class Error : public std::runtime_error +{ +public: + Error(const char *msg); +}; + +#define STR(X) #X + +#define rAssert( cond ) \ + do { \ + if( (cond) == false) \ + { LOG(ERROR) << "Assert failed: " << STR(cond); \ + throw Error(STR(cond)); \ + } \ + } while(0) + + +#endif + diff --git a/encfs/Interface.cpp b/base/Interface.cpp similarity index 81% rename from encfs/Interface.cpp rename to base/Interface.cpp index 489996d..03c0d10 100644 --- a/encfs/Interface.cpp +++ b/base/Interface.cpp @@ -2,7 +2,7 @@ * Author: Valient Gough * ***************************************************************************** - * Copyright (c) 2004, Valient Gough + * Copyright (c) 2004-2013, Valient Gough * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the @@ -18,22 +18,23 @@ * along with this program. If not, see . */ -#include "Interface.h" +#include "base/Interface.h" -#include "ConfigVar.h" +#include "base/ConfigVar.h" -#include -#include +#include +#include -using namespace rlog; - -static RLogChannel * Info = DEF_CHANNEL( "info/iface", Log_Info ); +std::ostream& operator << (std::ostream& out, const Interface &iface) +{ + out << iface.name() << "(" << iface.major() + << ":" << iface.minor() << ":" << iface.age() << ")"; + return out; +} bool implements(const Interface &A, const Interface &B) { - rLog(Info, "checking if %s(%i:%i:%i) implements %s(%i:%i:%i)", - A.name().c_str(), A.major(), A.minor(), A.age(), - B.name().c_str(), B.major(), B.minor(), B.age()); + VLOG(1) << "checking if " << A << " implements " << B; if( A.name() != B.name() ) return false; @@ -54,7 +55,8 @@ Interface makeInterface(const char *name, int major, int minor, int age) ConfigVar & operator << (ConfigVar &dst, const Interface &iface) { - dst << iface.name() << (int)iface.major() << (int)iface.minor() << (int)iface.age(); + dst << iface.name() << (int)iface.major() << (int)iface.minor() + << (int)iface.age(); return dst; } diff --git a/encfs/Interface.h b/base/Interface.h similarity index 97% rename from encfs/Interface.h rename to base/Interface.h index bfced6d..38b5e62 100644 --- a/encfs/Interface.h +++ b/base/Interface.h @@ -22,7 +22,7 @@ #define _Interface_incl_ #include -#include "config.pb.h" +#include "base/config.pb.h" // check if A implements the interface described by B. // Note that implements(A, B) is not the same as implements(B, A) @@ -32,7 +32,7 @@ bool implements( const Interface &a, const Interface &b ); Interface makeInterface( const char *name, int major, int minor, int age ); -// Reae operation +// Read operation class ConfigVar; const ConfigVar & operator >> (const ConfigVar &, Interface &); diff --git a/encfs/Mutex.h b/base/Mutex.h similarity index 63% rename from encfs/Mutex.h rename to base/Mutex.h index 318c56a..0a13a60 100644 --- a/encfs/Mutex.h +++ b/base/Mutex.h @@ -26,40 +26,41 @@ namespace rel { - class Lock - { - public: - Lock( pthread_mutex_t &mutex ); - ~Lock(); +class Lock +{ +public: + Lock( pthread_mutex_t &mutex ); + ~Lock(); - // leave the lock as it is. When the Lock wrapper is destroyed, it - // will do nothing with the pthread mutex. - void leave(); + // leave the lock as it is. When the Lock wrapper is destroyed, it + // will do nothing with the pthread mutex. + void leave(); - private: - Lock(const Lock &src); // not allowed - Lock &operator = (const Lock &src); // not allowed +private: + Lock(const Lock &src); // not allowed + Lock &operator = (const Lock &src); // not allowed - pthread_mutex_t *_mutex; - }; + pthread_mutex_t *_mutex; +}; - inline Lock::Lock( pthread_mutex_t &mutex ) - : _mutex( &mutex ) - { - pthread_mutex_lock( _mutex ); - } - - inline Lock::~Lock( ) - { - if(_mutex) - pthread_mutex_unlock( _mutex ); - } - - inline void Lock::leave() - { - _mutex = 0; - } +inline Lock::Lock( pthread_mutex_t &mutex ) + : _mutex( &mutex ) +{ + pthread_mutex_lock( _mutex ); } +inline Lock::~Lock( ) +{ + if(_mutex) + pthread_mutex_unlock( _mutex ); +} + +inline void Lock::leave() +{ + _mutex = 0; +} + +} // namespace rel + #endif diff --git a/encfs/Range.h b/base/Range.h similarity index 97% rename from encfs/Range.h rename to base/Range.h index f4f3c7d..b89804c 100644 --- a/encfs/Range.h +++ b/base/Range.h @@ -66,6 +66,8 @@ inline Range::Range() inline bool Range::allowed(int value) const { + if(minVal < 0 && maxVal < 0) + return true; if(value >= minVal && value <= maxVal) { int tmp = value - minVal; diff --git a/encfs/XmlReader.cpp b/base/XmlReader.cpp similarity index 90% rename from encfs/XmlReader.cpp rename to base/XmlReader.cpp index 925271c..c774708 100644 --- a/encfs/XmlReader.cpp +++ b/base/XmlReader.cpp @@ -2,7 +2,7 @@ * Author: Valient Gough * ***************************************************************************** - * Copyright (c) 2012, Valient Gough + * Copyright (c) 2012-2013, Valient Gough * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -#include "XmlReader.h" +#include "base/XmlReader.h" #include #include @@ -35,12 +35,11 @@ #include #include -#include -#include "base64.h" -#include "Interface.h" +#include +#include "base/base64.h" +#include "base/Interface.h" using namespace std; -using namespace rlog; XmlValue::~XmlValue() { @@ -53,7 +52,7 @@ XmlValuePtr XmlValue::operator[] (const char *path) const XmlValuePtr XmlValue::find(const char *path) const { - rError("in XmlValue::find(%s)", path); + LOG_FIRST_N(ERROR, 1) << "in XmlValue::find( " << path << ")"; return XmlValuePtr(); } @@ -127,8 +126,9 @@ bool XmlValue::readB64(const char *path, unsigned char *data, int length) const if (decodedSize != length) { - rError("decoding bytes len %i, expecting output len %i, got %i", - (int)s.size(), length, decodedSize); + LOG(ERROR) << "decoding bytes len " << s.size() + << ", expecting output len " << length + << ", got " << decodedSize; return false; } @@ -231,14 +231,15 @@ XmlValuePtr XmlReader::operator[] ( const char *name ) const TiXmlNode *node = pd->doc->FirstChild(name); if (node == NULL) { - rError("Xml node %s not found", name); + LOG(ERROR) << "Xml node " << name << " not found"; return XmlValuePtr(new XmlValue()); } TiXmlElement *element = node->ToElement(); if (element == NULL) { - rError("Xml node %s not element, type = %i", name, node->Type()); + LOG(ERROR) << "Xml node " << name + << " not element, type = " << node->Type(); return XmlValuePtr(new XmlValue()); } diff --git a/encfs/XmlReader.h b/base/XmlReader.h similarity index 98% rename from encfs/XmlReader.h rename to base/XmlReader.h index e869261..dae3826 100644 --- a/encfs/XmlReader.h +++ b/base/XmlReader.h @@ -22,7 +22,7 @@ #define _XmlReader_incl_ #include -#include "shared_ptr.h" +#include "base/shared_ptr.h" class XmlValue; typedef shared_ptr XmlValuePtr; diff --git a/intl/autosprintf.cpp b/base/autosprintf.cpp similarity index 98% rename from intl/autosprintf.cpp rename to base/autosprintf.cpp index ce76011..842c80a 100644 --- a/intl/autosprintf.cpp +++ b/base/autosprintf.cpp @@ -25,7 +25,7 @@ #endif /* Specification. */ -#include "autosprintf.h" +#include "base/autosprintf.h" #include #include diff --git a/intl/autosprintf.h b/base/autosprintf.h similarity index 100% rename from intl/autosprintf.h rename to base/autosprintf.h diff --git a/encfs/base64.cpp b/base/base64.cpp similarity index 99% rename from encfs/base64.cpp rename to base/base64.cpp index 2f0da1d..cfb0e38 100644 --- a/encfs/base64.cpp +++ b/base/base64.cpp @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -#include "base64.h" +#include "base/base64.h" #include diff --git a/encfs/base64.h b/base/base64.h similarity index 100% rename from encfs/base64.h rename to base/base64.h diff --git a/base/config.h.cmake b/base/config.h.cmake new file mode 100644 index 0000000..b9834f2 --- /dev/null +++ b/base/config.h.cmake @@ -0,0 +1,19 @@ +#cmakedefine HAVE_ATTR_XATTR_H +#cmakedefine HAVE_SYS_XATTR_H +#cmakedefine XATTR_ADD_OPT +#cmakedefine HAVE_COMMON_CRYPTO + +#cmakedefine HAVE_TR1_MEMORY +#cmakedefine HAVE_TR1_UNORDERED_MAP +#cmakedefine HAVE_TR1_UNORDERED_SET +#cmakedefine HAVE_TR1_TUPLE + +#cmakedefine HAVE_EVP_BF +#cmakedefine HAVE_EVP_AES +#cmakedefine HAVE_EVP_AES_XTS + +#cmakedefine HAVE_VALGRIND_VALGRIND_H +#cmakedefine HAVE_VALGRIND_MEMCHECK_H + +#define VERSION "@ENCFS_VERSION@" + diff --git a/encfs/config.proto b/base/config.proto similarity index 87% rename from encfs/config.proto rename to base/config.proto index eeaf6df..28b1bad 100644 --- a/encfs/config.proto +++ b/base/config.proto @@ -2,9 +2,13 @@ message EncfsConfig { optional string creator = 1; + optional string writer = 11; optional int32 revision = 2 [default=0]; required Interface cipher = 3; + // added for FileIO/Cipher 3.0 (encfs 1.8) + // Use only block encryption, no stream encryption. + optional bool block_mode_only = 31; required EncryptedKey key = 4; optional Interface naming = 5; @@ -16,6 +20,7 @@ message EncfsConfig optional int32 block_mac_bytes = 61 [default=0]; optional int32 block_mac_rand_bytes = 611 [default=0]; optional bool allow_holes = 62 [default = false]; + } message EncryptedKey diff --git a/intl/gettext.h b/base/gettext.h similarity index 100% rename from intl/gettext.h rename to base/gettext.h diff --git a/encfs/i18n.h b/base/i18n.h similarity index 95% rename from encfs/i18n.h rename to base/i18n.h index 29b2582..4a73f14 100644 --- a/encfs/i18n.h +++ b/base/i18n.h @@ -23,11 +23,11 @@ #if defined(LOCALEDIR) -# include "gettext.h" +# include "base/gettext.h" // make shortcut for gettext # define _(STR) gettext (STR) -# include "autosprintf.h" +# include "base/autosprintf.h" using gnu::autosprintf; #else diff --git a/encfs/shared_ptr.h b/base/shared_ptr.h similarity index 97% rename from encfs/shared_ptr.h rename to base/shared_ptr.h index 4c2ebed..82b9068 100644 --- a/encfs/shared_ptr.h +++ b/base/shared_ptr.h @@ -22,7 +22,7 @@ #ifndef _SHARED_PTR_incl_ #define _SHARED_PTR_incl_ -#include "config.h" +#include "base/config.h" #ifdef HAVE_TR1_MEMORY #include diff --git a/cipher/CMakeLists.txt b/cipher/CMakeLists.txt new file mode 100644 index 0000000..ea56731 --- /dev/null +++ b/cipher/CMakeLists.txt @@ -0,0 +1,41 @@ +include_directories (${OPENSSL_INCLUDE_DIR}) + +link_directories (${Encfs_BINARY_DIR}/base) + +enable_testing () +find_package (GTest REQUIRED) + +add_library (encfs-cipher + readpassphrase.cpp + Cipher.cpp + CipherKey.cpp + MemoryPool.cpp + NullCipher.cpp + openssl.cpp + SSL_Cipher.cpp +) + +target_link_libraries (encfs-cipher + ${OPENSSL_LIBRARIES} +) + +#include_directories (${GTEST_INCLUDE_DIR}) +#add_executable (unittests +#MemBlockFileIO.cpp +#MemFileIO.cpp +#testing.cpp +#test_IO.cpp +#test_BlockIO.cpp +#) + +#target_link_libraries (unittests +#${GTEST_BOTH_LIBRARIES} +#encfs-fs +#encfs-base +#${GLOG_LIBRARIES} +#) + +#add_test (UnitTests unittests) +#GTEST_ADD_TESTS (unittests "${UnitTestArgs}" test_IO.cpp test_BlockIO.cpp) +#add_custom_target (test COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS unittests) + diff --git a/encfs/Cipher.cpp b/cipher/Cipher.cpp similarity index 89% rename from encfs/Cipher.cpp rename to cipher/Cipher.cpp index 6e6e47b..2d208c0 100644 --- a/encfs/Cipher.cpp +++ b/cipher/Cipher.cpp @@ -18,12 +18,12 @@ * along with this program. If not, see . */ -#include "config.h" +#include "base/config.h" +#include "cipher/Cipher.h" -#include "Cipher.h" -#include "Interface.h" -#include "Range.h" -#include "base64.h" +#include "base/Interface.h" +#include "base/Range.h" +#include "base/base64.h" #include #include @@ -57,6 +57,7 @@ struct CipherAlg Interface iface; Range keyLength; Range blockSize; + bool hasStreamMode; }; typedef multimap< string, CipherAlg> CipherMap_t; @@ -84,6 +85,7 @@ Cipher::GetAlgorithmList( bool includeHidden ) tmp.iface = it->second.iface; tmp.keyLength = it->second.keyLength; tmp.blockSize = it->second.blockSize; + tmp.hasStreamMode = it->second.hasStreamMode; result.push_back( tmp ); } @@ -93,18 +95,21 @@ Cipher::GetAlgorithmList( bool includeHidden ) } bool Cipher::Register(const char *name, const char *description, - const Interface &iface, CipherConstructor fn, bool hidden) + const Interface &iface, CipherConstructor fn, + bool hasStreamMode, bool hidden) { Range keyLength(-1,-1,1); Range blockSize(-1,-1,1); return Cipher::Register( name, description, iface, - keyLength, blockSize, fn, hidden ); + keyLength, blockSize, fn, hasStreamMode, hidden ); } bool Cipher::Register(const char *name, const char *description, const Interface &iface, const Range &keyLength, const Range &blockSize, - CipherConstructor fn, bool hidden) + CipherConstructor fn, + bool hasStreamMode, + bool hidden) { if(!gCipherMap) gCipherMap = new CipherMap_t; @@ -116,6 +121,7 @@ bool Cipher::Register(const char *name, const char *description, ca.iface = iface; ca.keyLength = keyLength; ca.blockSize = blockSize; + ca.hasStreamMode = hasStreamMode; gCipherMap->insert( make_pair(string(name), ca) ); return true; @@ -195,26 +201,13 @@ unsigned int Cipher::MAC_16( const unsigned char *src, int len, return mac16; } -bool Cipher::nameEncode( unsigned char *data, int len, - uint64_t iv64, const CipherKey &key ) const -{ - return streamEncode( data, len, iv64, key ); -} - -bool Cipher::nameDecode( unsigned char *data, int len, - uint64_t iv64, const CipherKey &key ) const -{ - return streamDecode( data, len, iv64, key ); -} - string Cipher::encodeAsString(const CipherKey &key, const CipherKey &encodingKey ) { int encodedKeySize = this->encodedKeySize(); unsigned char *keyBuf = new unsigned char[ encodedKeySize ]; - // write the key, encoding it with itself. - this->writeKey( key, keyBuf, key ); + this->writeKey( key, keyBuf, encodingKey ); int b64Len = B256ToB64Bytes( encodedKeySize ); unsigned char *b64Key = new unsigned char[ b64Len + 1 ]; @@ -226,3 +219,9 @@ string Cipher::encodeAsString(const CipherKey &key, return string( (const char *)b64Key ); } + +bool Cipher::hasStreamMode() const +{ + return true; +} + diff --git a/encfs/Cipher.h b/cipher/Cipher.h similarity index 88% rename from encfs/Cipher.h rename to cipher/Cipher.h index e0dc3b2..8b0795d 100644 --- a/encfs/Cipher.h +++ b/cipher/Cipher.h @@ -21,11 +21,9 @@ #ifndef _Cipher_incl_ #define _Cipher_incl_ -#include "encfs.h" - -#include "Range.h" -#include "Interface.h" -#include "CipherKey.h" +#include "cipher/CipherKey.h" +#include "base/Interface.h" +#include "base/Range.h" #include #include @@ -52,6 +50,7 @@ public: Interface iface; Range keyLength; Range blockSize; + bool hasStreamMode; }; @@ -60,15 +59,16 @@ public: static shared_ptr New( const Interface &iface, - int keyLen = -1); + int keyLen = -1); static shared_ptr New( const std::string &cipherName, - int keyLen = -1 ); + int keyLen = -1 ); static bool Register(const char *cipherName, const char *description, const Interface &iface, CipherConstructor constructor, + bool hasStreamMode, bool hidden = false); static bool Register(const char *cipherName, @@ -76,6 +76,7 @@ public: const Interface &iface, const Range &keyLength, const Range &blockSize, CipherConstructor constructor, + bool hasStreamMode, bool hidden = false); Cipher(); @@ -117,6 +118,8 @@ public: virtual int encodedKeySize() const=0; // size virtual int cipherBlockSize() const=0; // size of a cipher block + virtual bool hasStreamMode() const; + // fill the supplied buffer with random data // The data may be pseudo random and might not be suitable for key // generation. For generating keys, uses newRandomKey() instead. @@ -143,17 +146,6 @@ public: virtual bool streamDecode( unsigned char *data, int len, uint64_t iv64, const CipherKey &key) const=0; - /* - These are just aliases of streamEncode / streamDecode, but there are - provided here for backward compatibility for earlier ciphers that has - effectively two stream modes - one for encoding partial blocks and - another for encoding filenames. - */ - virtual bool nameEncode( unsigned char *data, int len, - uint64_t iv64, const CipherKey &key) const; - virtual bool nameDecode( unsigned char *data, int len, - uint64_t iv64, const CipherKey &key) const; - /* Block encoding of data in-place. The data size should be a multiple of the cipher block size. diff --git a/encfs/CipherKey.cpp b/cipher/CipherKey.cpp similarity index 97% rename from encfs/CipherKey.cpp rename to cipher/CipherKey.cpp index c2e815a..01b9a3f 100644 --- a/encfs/CipherKey.cpp +++ b/cipher/CipherKey.cpp @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -#include "CipherKey.h" +#include "cipher/CipherKey.h" AbstractCipherKey::AbstractCipherKey() { diff --git a/encfs/CipherKey.h b/cipher/CipherKey.h similarity index 97% rename from encfs/CipherKey.h rename to cipher/CipherKey.h index b8cf639..9c1490b 100644 --- a/encfs/CipherKey.h +++ b/cipher/CipherKey.h @@ -21,7 +21,7 @@ #ifndef _CipherKey_incl_ #define _CipherKey_incl_ -#include "shared_ptr.h" +#include "base/shared_ptr.h" class AbstractCipherKey { diff --git a/cipher/MemoryPool.cpp b/cipher/MemoryPool.cpp new file mode 100644 index 0000000..d1cd935 --- /dev/null +++ b/cipher/MemoryPool.cpp @@ -0,0 +1,164 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2003-2013, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "cipher/MemoryPool.h" + +#include +#include + +#include "base/config.h" +#include "base/Error.h" + +#include + +#include + +#include + +#ifdef HAVE_VALGRIND_MEMCHECK_H +#include +#else +#define VALGRIND_MAKE_MEM_NOACCESS( a, b ) +#define VALGRIND_MAKE_MEM_UNDEFINED( a, b ) +#endif + +#include +#include + +using namespace std; + +# include +# include + +static BUF_MEM *allocBlock( int size ) +{ + BUF_MEM *block = BUF_MEM_new( ); + BUF_MEM_grow( block, size ); + VALGRIND_MAKE_MEM_NOACCESS( block->data, block->max ); + + return block; +} + +static void freeBlock( BUF_MEM *block ) +{ + VALGRIND_MAKE_MEM_UNDEFINED( block->data, block->max ); + BUF_MEM_free( block ); +} + +static pthread_mutex_t gMPoolMutex = PTHREAD_MUTEX_INITIALIZER; + +typedef std::map > FreeBlockMap; +static FreeBlockMap gFreeBlocks; + +void MemBlock::allocate(int size) +{ + rAssert(size > 0); + pthread_mutex_lock( &gMPoolMutex ); + + list &freeList = gFreeBlocks[size]; + BUF_MEM *mem; + + if (!freeList.empty()) + { + mem = freeList.front(); + freeList.pop_front(); + pthread_mutex_unlock( &gMPoolMutex ); + } else + { + pthread_mutex_unlock( &gMPoolMutex ); + mem = allocBlock( size ); + } + + internalData = mem; + data = reinterpret_cast(mem->data); + VALGRIND_MAKE_MEM_UNDEFINED( data, size ); +} + +MemBlock::~MemBlock() +{ + BUF_MEM *block = (BUF_MEM*)internalData; + data = NULL; + internalData = NULL; + + if (block) + { + // wipe the buffer.. + VALGRIND_MAKE_MEM_UNDEFINED( block->data, block->max ); + memset( block->data , 0, block->max); + VALGRIND_MAKE_MEM_NOACCESS( block->data, block->max ); + + pthread_mutex_lock( &gMPoolMutex ); + gFreeBlocks[ block->max ].push_front(block); + pthread_mutex_unlock( &gMPoolMutex ); + } +} + +void MemoryPool::destroyAll() +{ + pthread_mutex_lock( &gMPoolMutex ); + + for (FreeBlockMap::const_iterator it = gFreeBlocks.begin(); + it != gFreeBlocks.end(); it++) + { + for (list::const_iterator bIt = it->second.begin(); + bIt != it->second.end(); bIt++) + { + freeBlock( *bIt ); + } + } + + gFreeBlocks.clear(); + + pthread_mutex_unlock( &gMPoolMutex ); +} + +SecureMem::SecureMem(int len) +{ + rAssert(len > 0); + data = (char *)OPENSSL_malloc(len); + if (data) + { + size = len; + mlock(data, size); + memset(data, '\0', size); + VALGRIND_MAKE_MEM_UNDEFINED( data, size ); + } else + { + size = 0; + } +} + +SecureMem::~SecureMem() +{ + if (size) + { + memset(data, '\0', size); + OPENSSL_cleanse(data, size); + + munlock(data, size); + OPENSSL_free(data); + VALGRIND_MAKE_MEM_NOACCESS( data, size ); + + data = NULL; + size = 0; + } +} + + diff --git a/encfs/MemoryPool.h b/cipher/MemoryPool.h similarity index 90% rename from encfs/MemoryPool.h rename to cipher/MemoryPool.h index ba46bff..330e5ce 100644 --- a/encfs/MemoryPool.h +++ b/cipher/MemoryPool.h @@ -21,14 +21,25 @@ #ifndef _MemoryPool_incl_ #define _MemoryPool_incl_ +/* + Memory Pool for fixed sized objects. + Usage: + MemBlock mb( size ); + // do things with storage in mb.data + unsigned char *buffer = mb.data; + + // memblock freed when destructed +*/ struct MemBlock { unsigned char *data; - void *internalData; MemBlock(); + ~MemBlock(); + + void allocate(int size); }; inline MemBlock::MemBlock() @@ -36,19 +47,8 @@ inline MemBlock::MemBlock() { } -/* - Memory Pool for fixed sized objects. - - Usage: - MemBlock mb = MemoryPool::allocate( size ); - // do things with storage in mb.data - unsigned char *buffer = mb.data; - MemoryPool::release( mb ); -*/ namespace MemoryPool { - MemBlock allocate( int size ); - void release( const MemBlock &el ); void destroyAll(); } diff --git a/encfs/NullCipher.cpp b/cipher/NullCipher.cpp similarity index 63% rename from encfs/NullCipher.cpp rename to cipher/NullCipher.cpp index 4971fd8..867328b 100644 --- a/encfs/NullCipher.cpp +++ b/cipher/NullCipher.cpp @@ -18,18 +18,15 @@ * along with this program. If not, see . */ -#include "NullCipher.h" +#include "cipher/NullCipher.h" -#include "Range.h" -#include "Interface.h" -#include "shared_ptr.h" - -#include +#include "base/Range.h" +#include "base/Interface.h" +#include "base/shared_ptr.h" #include using namespace std; -using namespace rlog; static Interface NullInterface = makeInterface( "nullCipher", 1, 0, 0 ); @@ -38,40 +35,40 @@ static Range NullBlockRange(1,4096,1); static shared_ptr NewNullCipher(const Interface &iface, int keyLen) { - (void)keyLen; - return shared_ptr( new NullCipher( iface ) ); + (void)keyLen; + return shared_ptr( new NullCipher( iface ) ); } const bool HiddenCipher = true; static bool NullCipher_registered = Cipher::Register("Null", - "Non encrypting cipher. For testing only!", - NullInterface, NullKeyRange, NullBlockRange, NewNullCipher, - HiddenCipher); + "Non encrypting cipher. For testing only!", + NullInterface, NullKeyRange, NullBlockRange, NewNullCipher, + HiddenCipher); class NullKey : public AbstractCipherKey { public: - NullKey() {} - virtual ~NullKey() {} + NullKey() {} + virtual ~NullKey() {} }; class NullDestructor { public: - NullDestructor() {} - NullDestructor(const NullDestructor &) {} - ~NullDestructor() {} + NullDestructor() {} + NullDestructor(const NullDestructor &) {} + ~NullDestructor() {} - NullDestructor &operator = (const NullDestructor &){ return *this; } - void operator ()(NullKey *&) {} + NullDestructor &operator = (const NullDestructor &){ return *this; } + void operator ()(NullKey *&) {} }; shared_ptr gNullKey( new NullKey(), NullDestructor() ); NullCipher::NullCipher(const Interface &iface_) { - this->iface = iface_; + this->iface = iface_; } NullCipher::~NullCipher() @@ -80,105 +77,105 @@ NullCipher::~NullCipher() Interface NullCipher::interface() const { - return iface; + return iface; } CipherKey NullCipher::newKey(const char *, int, int &, long, const unsigned char *, int ) { - return gNullKey; + return gNullKey; } CipherKey NullCipher::newKey(const char *, int) { - return gNullKey; + return gNullKey; } CipherKey NullCipher::newRandomKey() { - return gNullKey; + return gNullKey; } bool NullCipher::randomize( unsigned char *buf, int len, bool ) const { - memset( buf, 0, len ); - return true; + memset( buf, 0, len ); + return true; } uint64_t NullCipher::MAC_64(const unsigned char *, int , - const CipherKey &, uint64_t *) const + const CipherKey &, uint64_t *) const { - return 0; + return 0; } CipherKey NullCipher::readKey( const unsigned char *, - const CipherKey &, bool) + const CipherKey &, bool) { - return gNullKey; + return gNullKey; } void NullCipher::writeKey(const CipherKey &, unsigned char *, - const CipherKey &) + const CipherKey &) { } bool NullCipher::compareKey(const CipherKey &A_, - const CipherKey &B_) const + const CipherKey &B_) const { - shared_ptr A = dynamic_pointer_cast(A_); - shared_ptr B = dynamic_pointer_cast(B_); - return A.get() == B.get(); + shared_ptr A = dynamic_pointer_cast(A_); + shared_ptr B = dynamic_pointer_cast(B_); + return A.get() == B.get(); } int NullCipher::encodedKeySize() const { - return 0; + return 0; } int NullCipher::keySize() const { - return 0; + return 0; } int NullCipher::cipherBlockSize() const { - return 1; + return 1; } bool NullCipher::streamEncode( unsigned char *src, int len, - uint64_t iv64, const CipherKey &key) const + uint64_t iv64, const CipherKey &key) const { - (void)src; - (void)len; - (void)iv64; - (void)key; - return true; + (void)src; + (void)len; + (void)iv64; + (void)key; + return true; } bool NullCipher::streamDecode( unsigned char *src, int len, - uint64_t iv64, const CipherKey &key) const + uint64_t iv64, const CipherKey &key) const { - (void)src; - (void)len; - (void)iv64; - (void)key; - return true; + (void)src; + (void)len; + (void)iv64; + (void)key; + return true; } bool NullCipher::blockEncode( unsigned char *, int , uint64_t, - const CipherKey & ) const + const CipherKey & ) const { - return true; + return true; } bool NullCipher::blockDecode( unsigned char *, int, uint64_t, - const CipherKey & ) const + const CipherKey & ) const { - return true; + return true; } bool NullCipher::Enabled() { - return true; + return true; } diff --git a/encfs/NullCipher.h b/cipher/NullCipher.h similarity index 98% rename from encfs/NullCipher.h rename to cipher/NullCipher.h index 103a141..afe6cb4 100644 --- a/encfs/NullCipher.h +++ b/cipher/NullCipher.h @@ -21,8 +21,8 @@ #ifndef _NullCipher_incl_ #define _NullCipher_incl_ -#include "Cipher.h" -#include "Interface.h" +#include "cipher/Cipher.h" +#include "base/Interface.h" /* Implements Cipher interface for a pass-through mode. May be useful for diff --git a/cipher/SSL_Cipher.cpp b/cipher/SSL_Cipher.cpp new file mode 100644 index 0000000..8390ad3 --- /dev/null +++ b/cipher/SSL_Cipher.cpp @@ -0,0 +1,973 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "base/config.h" + +#include +#include +#include +#include +#include + +#include "cipher/SSL_Cipher.h" +#include "cipher/MemoryPool.h" +#include "base/Error.h" +#include "base/Mutex.h" +#include "base/Range.h" + +#include +#include + +#include +#include + +#include + +#include "base/i18n.h" + +using namespace std; +using namespace rel; + +const int MAX_KEYLENGTH = 64; // in bytes (256 bit) +const int MAX_IVLENGTH = 16; +const int KEY_CHECKSUM_BYTES = 4; + +#ifndef MIN +inline int MIN(int a, int b) +{ + return (a < b) ? a : b; +} +#endif + +/* + This produces the same result as OpenSSL's EVP_BytesToKey. The difference + is that here we can explicitly specify the key size, instead of relying on + the state of EVP_CIPHER struct. EVP_BytesToKey will only produce 128 bit + keys for the EVP Blowfish interface, which is not what we want. + + DEPRECATED: this is here for backward compatibilty only. Use PBKDF +*/ +int BytesToKey( int keyLen, int ivLen, const EVP_MD *md, + const unsigned char *data, int dataLen, + unsigned int rounds, unsigned char *key, unsigned char *iv) +{ + if( data == NULL || dataLen == 0 ) + return 0; // OpenSSL returns nkey here, but why? It is a failure.. + + unsigned char mdBuf[ EVP_MAX_MD_SIZE ]; + unsigned int mds=0; + int addmd =0; + int nkey = key ? keyLen : 0; + int niv = iv ? ivLen : 0; + + EVP_MD_CTX cx; + EVP_MD_CTX_init( &cx ); + + for(;;) + { + EVP_DigestInit_ex( &cx, md, NULL ); + if( addmd++ ) + EVP_DigestUpdate( &cx, mdBuf, mds ); + EVP_DigestUpdate( &cx, data, dataLen ); + EVP_DigestFinal_ex( &cx, mdBuf, &mds ); + + for(unsigned int i=1; i < rounds; ++i) + { + EVP_DigestInit_ex( &cx, md, NULL ); + EVP_DigestUpdate( &cx, mdBuf, mds ); + EVP_DigestFinal_ex( &cx, mdBuf, &mds ); + } + + int offset = 0; + int toCopy = MIN( nkey, (int)mds - offset ); + if( toCopy ) + { + memcpy( key, mdBuf+offset, toCopy ); + key += toCopy; + nkey -= toCopy; + offset += toCopy; + } + toCopy = MIN( niv, (int)mds - offset ); + if( toCopy ) + { + memcpy( iv, mdBuf+offset, toCopy ); + iv += toCopy; + niv -= toCopy; + offset += toCopy; + } + if((nkey == 0) && (niv == 0)) break; + } + EVP_MD_CTX_cleanup( &cx ); + OPENSSL_cleanse( mdBuf, sizeof(mdBuf) ); + + return keyLen; +} + +long time_diff(const timeval &end, const timeval &start) +{ + return (end.tv_sec - start.tv_sec) * 1000 * 1000 + + (end.tv_usec - start.tv_usec); +} + +int SSL_Cipher::TimedPBKDF2(const char *pass, int passlen, + const unsigned char *salt, int saltlen, + int keylen, unsigned char *out, + long desiredPDFTime) +{ + int iter = 1000; + timeval start, end; + + for(;;) + { + gettimeofday( &start, 0 ); + int res = PKCS5_PBKDF2_HMAC_SHA1( + pass, passlen, const_cast(salt), saltlen, + iter, keylen, out); + if(res != 1) + return -1; + + gettimeofday( &end, 0 ); + + long delta = time_diff(end, start); + if(delta < desiredPDFTime / 8) + { + iter *= 4; + } else if(delta < (5 * desiredPDFTime / 6)) + { + // estimate number of iterations to get close to desired time + iter = (int)((double)iter * (double)desiredPDFTime + / (double)delta); + } else + return iter; + } +} + + +// - Version 1:0 used EVP_BytesToKey, which didn't do the right thing for +// Blowfish key lengths > 128 bit. +// - Version 2:0 uses BytesToKey. +// We support both 2:0 and 1:0, hence current:revision:age = 2:0:1 +// - Version 2:1 adds support for Message Digest function interface +// - Version 2:2 adds PBKDF2 for password derivation +// - Version 3:0 adds a new IV mechanism +// - Version 3:1 adds ssl/aes_xts +static Interface BlowfishInterface = makeInterface( "ssl/blowfish", 3, 0, 2 ); +static Interface AESInterface = makeInterface( "ssl/aes", 3, 0, 2 ); +static Interface AesXtsInterface = makeInterface( "ssl/aes_xts", 3, 1, 2 ); + +#if defined(HAVE_EVP_BF) + +static Range BFKeyRange(128,256,32); +static Range BFBlockRange(64,4096,8); + +static shared_ptr NewBFCipher( const Interface &iface, int keyLen ) +{ + if( keyLen <= 0 ) + keyLen = 160; + + keyLen = BFKeyRange.closest( keyLen ); + + const EVP_CIPHER *blockCipher = EVP_bf_cbc(); + const EVP_CIPHER *streamCipher = EVP_bf_cfb(); + + return shared_ptr( new SSL_Cipher(iface, BlowfishInterface, + blockCipher, streamCipher, keyLen / 8) ); +} + +static bool BF_Cipher_registered = Cipher::Register( + "Blowfish", + // xgroup(setup) + gettext_noop("8 byte block cipher"), + BlowfishInterface, BFKeyRange, BFBlockRange, NewBFCipher, true); +#endif + + +#if defined(HAVE_EVP_AES) + +static Range AESKeyRange(128,256,64); +static Range AESBlockRange(64,4096,16); + +static shared_ptr NewAESCipher( const Interface &iface, int keyLen ) +{ + if( keyLen <= 0 ) + keyLen = 192; + + keyLen = AESKeyRange.closest( keyLen ); + + const EVP_CIPHER *blockCipher = 0; + const EVP_CIPHER *streamCipher = 0; + + switch(keyLen) + { + case 128: + blockCipher = EVP_aes_128_cbc(); + streamCipher = EVP_aes_128_cfb(); + break; + + case 192: + blockCipher = EVP_aes_192_cbc(); + streamCipher = EVP_aes_192_cfb(); + break; + + case 256: + default: + blockCipher = EVP_aes_256_cbc(); + streamCipher = EVP_aes_256_cfb(); + break; + } + + return shared_ptr( new SSL_Cipher(iface, AESInterface, + blockCipher, streamCipher, keyLen / 8) ); +} + +static bool AES_Cipher_registered = Cipher::Register( + "AES", "16 byte block cipher", + AESInterface, AESKeyRange, AESBlockRange, NewAESCipher, true); +#endif + +#if defined(HAVE_EVP_AES_XTS) + +static Range AesXtsKeyRange(128,256,128); +static Range AesXtsBlockRange(1024,8192,256); + +static shared_ptr NewAesXtsCipher( const Interface &iface, int keyLen ) +{ + if( keyLen <= 0 ) + keyLen = 256; + + keyLen = AesXtsKeyRange.closest( keyLen ); + + const EVP_CIPHER *blockCipher = 0; + + switch(keyLen) + { + case 128: + blockCipher = EVP_aes_128_xts(); + break; + + case 256: + default: + blockCipher = EVP_aes_256_xts(); + break; + } + + // XTS uses 2 keys, so the key size is doubled here. + // Eg XTS-AES-256 uses two 256 bit keys. + return shared_ptr( new SSL_Cipher(iface, AesXtsInterface, + blockCipher, NULL, 2 * keyLen / 8) ); +} + +static bool AES_XTS_Cipher_registered = Cipher::Register( + "AES_XTS", "Tweakable wide-block cipher", + AesXtsInterface, AesXtsKeyRange, AesXtsBlockRange, NewAesXtsCipher, false); +#endif + +class SSLKey : public AbstractCipherKey +{ + public: + pthread_mutex_t mutex; + + unsigned int keySize; // in bytes + unsigned int ivLength; + + // key data is first _keySize bytes, + // followed by iv of _ivLength bytes, + SecureMem buf; + + EVP_CIPHER_CTX block_enc; + EVP_CIPHER_CTX block_dec; + EVP_CIPHER_CTX stream_enc; + EVP_CIPHER_CTX stream_dec; + + HMAC_CTX mac_ctx; + + SSLKey(int keySize, int ivLength); + ~SSLKey(); +}; + +SSLKey::SSLKey(int keySize_, int ivLength_) + : buf(keySize_ + ivLength_) +{ + rAssert(keySize_ >= 8); + rAssert(ivLength_ >= 8); + + this->keySize = keySize_; + this->ivLength = ivLength_; + pthread_mutex_init( &mutex, 0 ); +} + +SSLKey::~SSLKey() +{ + keySize = 0; + ivLength = 0; + + EVP_CIPHER_CTX_cleanup( &block_enc ); + EVP_CIPHER_CTX_cleanup( &block_dec ); + + EVP_CIPHER_CTX_cleanup( &stream_enc ); + EVP_CIPHER_CTX_cleanup( &stream_dec ); + + HMAC_CTX_cleanup( &mac_ctx ); + + pthread_mutex_destroy( &mutex ); +} + +inline unsigned char* KeyData( const shared_ptr &key ) +{ + return (unsigned char *)key->buf.data; +} + +inline unsigned char* IVData( const shared_ptr &key ) +{ + return (unsigned char *)key->buf.data + key->keySize; +} + +void initKey(const shared_ptr &key, const EVP_CIPHER *_blockCipher, + const EVP_CIPHER *_streamCipher, int _keySize) +{ + Lock lock( key->mutex ); + // initialize the cipher context once so that we don't have to do it for + // every block.. + EVP_CIPHER_CTX_init( &key->block_enc ); + EVP_CIPHER_CTX_init( &key->block_dec ); + EVP_EncryptInit_ex( &key->block_enc, _blockCipher, NULL, NULL, NULL); + EVP_DecryptInit_ex( &key->block_dec, _blockCipher, NULL, NULL, NULL); + EVP_CIPHER_CTX_set_key_length( &key->block_enc, _keySize ); + EVP_CIPHER_CTX_set_key_length( &key->block_dec, _keySize ); + EVP_CIPHER_CTX_set_padding( &key->block_enc, 0 ); + EVP_CIPHER_CTX_set_padding( &key->block_dec, 0 ); + EVP_EncryptInit_ex( &key->block_enc, NULL, NULL, KeyData(key), NULL); + EVP_DecryptInit_ex( &key->block_dec, NULL, NULL, KeyData(key), NULL); + + EVP_CIPHER_CTX_init( &key->stream_enc ); + EVP_CIPHER_CTX_init( &key->stream_dec ); + if (_streamCipher != NULL) + { + EVP_EncryptInit_ex( &key->stream_enc, _streamCipher, NULL, NULL, NULL); + EVP_DecryptInit_ex( &key->stream_dec, _streamCipher, NULL, NULL, NULL); + EVP_CIPHER_CTX_set_key_length( &key->stream_enc, _keySize ); + EVP_CIPHER_CTX_set_key_length( &key->stream_dec, _keySize ); + EVP_CIPHER_CTX_set_padding( &key->stream_enc, 0 ); + EVP_CIPHER_CTX_set_padding( &key->stream_dec, 0 ); + EVP_EncryptInit_ex( &key->stream_enc, NULL, NULL, KeyData(key), NULL); + EVP_DecryptInit_ex( &key->stream_dec, NULL, NULL, KeyData(key), NULL); + } + + HMAC_CTX_init( &key->mac_ctx ); + HMAC_Init_ex( &key->mac_ctx, KeyData(key), _keySize, EVP_sha1(), 0 ); +} + + +SSL_Cipher::SSL_Cipher(const Interface &iface_, + const Interface &realIface_, + const EVP_CIPHER *blockCipher, + const EVP_CIPHER *streamCipher, + int keySize_) +{ + this->iface = iface_; + this->realIface = realIface_; + this->_blockCipher = blockCipher; + this->_streamCipher = streamCipher; + this->_keySize = keySize_; + this->_ivLength = EVP_CIPHER_iv_length( _blockCipher ); + + rAssert(_ivLength == 8 || _ivLength == 16); + rAssert(_ivLength <= _keySize); + + VLOG(1) << "allocated cipher " << iface.name() + << ", keySize " << _keySize + << ", ivlength " << _ivLength; + + // EVP_CIPHER_key_length isn't useful for variable-length ciphers like + // Blowfish. Version 1 relied upon it incorrectly. + if( (EVP_CIPHER_key_length( _blockCipher ) != (int )_keySize) + && iface.major() == 1) + { + LOG(WARNING) << "Running in backward compatibilty mode for 1.0 - \n" + << "key is really " << EVP_CIPHER_key_length( _blockCipher ) * 8 + << " bits, not " << _keySize * 8; + } +} + +SSL_Cipher::~SSL_Cipher() +{ +} + +Interface SSL_Cipher::interface() const +{ + return realIface; +} + +/* + Create a key from the password. + Use SHA to distribute entropy from the password into the key. + + This algorithm must remain constant for backward compatibility, as this key + is used to encipher/decipher the master key. +*/ +CipherKey SSL_Cipher::newKey(const char *password, int passwdLength, + int &iterationCount, long desiredDuration, + const unsigned char *salt, int saltLen) +{ + shared_ptr key( new SSLKey( _keySize, _ivLength) ); + + if(iterationCount == 0) + { + // timed run, fills in iteration count + int res = TimedPBKDF2(password, passwdLength, + salt, saltLen, + _keySize+_ivLength, KeyData(key), + 1000 * desiredDuration); + if(res <= 0) + { + LOG(ERROR) << "openssl error, PBKDF2 failed"; + return CipherKey(); + } else + iterationCount = res; + } else + { + // known iteration length + if(PKCS5_PBKDF2_HMAC_SHA1( + password, passwdLength, + const_cast(salt), saltLen, + iterationCount, _keySize + _ivLength, KeyData(key)) != 1) + { + LOG(ERROR) << "openssl error, PBKDF2 failed"; + return CipherKey(); + } + } + + initKey( key, _blockCipher, _streamCipher, _keySize ); + + return key; +} + +CipherKey SSL_Cipher::newKey(const char *password, int passwdLength) +{ + shared_ptr key( new SSLKey( _keySize, _ivLength) ); + + int bytes = 0; + if( iface.major() > 1 ) + { + // now we use BytesToKey, which can deal with Blowfish keys larger then + // 128 bits. + bytes = BytesToKey( _keySize, _ivLength, EVP_sha1(), + (unsigned char *)password, passwdLength, 16, + KeyData(key), IVData(key) ); + + // the reason for moving from EVP_BytesToKey to BytesToKey function.. + if(bytes != (int)_keySize) + { + LOG(WARNING) << "newKey: BytesToKey returned " << bytes + << ", expecting " << _keySize << " key bytes"; + } + } else + { + // for backward compatibility with filesystems created with 1:0 + bytes = EVP_BytesToKey( _blockCipher, EVP_sha1(), NULL, + (unsigned char *)password, passwdLength, 16, + KeyData(key), IVData(key) ); + } + + initKey( key, _blockCipher, _streamCipher, _keySize ); + + return key; +} + +/* + Create a random key. + We use the OpenSSL library to generate random bytes, then take the hash of + those bytes to use as the key. + + This algorithm can change at any time without affecting backward + compatibility. +*/ +CipherKey SSL_Cipher::newRandomKey() +{ + const int bufLen = MAX_KEYLENGTH; + unsigned char tmpBuf[ bufLen ]; + int saltLen = 20; + unsigned char saltBuf[ saltLen ]; + + if(!randomize(tmpBuf, bufLen, true) || + !randomize(saltBuf, saltLen, true)) + return CipherKey(); + + shared_ptr key( new SSLKey( _keySize, _ivLength) ); + + // doesn't need to be versioned, because a random key is a random key.. + // Doesn't need to be reproducable.. + if(PKCS5_PBKDF2_HMAC_SHA1((char*)tmpBuf, bufLen, saltBuf, saltLen, + 1000, _keySize + _ivLength, KeyData(key)) != 1) + { + LOG(ERROR) << "openssl error, PBKDF2 failed"; + return CipherKey(); + } + + OPENSSL_cleanse(tmpBuf, bufLen); + + initKey( key, _blockCipher, _streamCipher, _keySize ); + + return key; +} + +/* + Compute a 64-bit check value for the data using HMAC. +*/ +static uint64_t _checksum_64(SSLKey *key, + const unsigned char *data, + int dataLen, + uint64_t *chainedIV) +{ + rAssert( dataLen > 0 ); + Lock lock( key->mutex ); + + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned int mdLen = EVP_MAX_MD_SIZE; + + HMAC_Init_ex( &key->mac_ctx, 0, 0, 0, 0 ); + HMAC_Update( &key->mac_ctx, data, dataLen ); + if(chainedIV) + { + // toss in the chained IV as well + uint64_t tmp = *chainedIV; + unsigned char h[8]; + for(unsigned int i=0; i<8; ++i) + { + h[i] = tmp & 0xff; + tmp >>= 8; + } + + HMAC_Update( &key->mac_ctx, h, 8 ); + } + + HMAC_Final( &key->mac_ctx, md, &mdLen ); + + rAssert(mdLen >= 8); + + // chop this down to a 64bit value.. + unsigned char h[8] = {0,0,0,0,0,0,0,0}; + for(unsigned int i=0; i<(mdLen-1); ++i) + h[i%8] ^= (unsigned char)(md[i]); + + uint64_t value = (uint64_t)h[0]; + for(int i=1; i<8; ++i) + value = (value << 8) | (uint64_t)h[i]; + + return value; +} + +bool SSL_Cipher::randomize( unsigned char *buf, int len, + bool strongRandom ) const +{ + // to avoid warnings of uninitialized data from valgrind + memset(buf, 0, len); + int result; + if(strongRandom) + result = RAND_bytes( buf, len ); + else + result = RAND_pseudo_bytes( buf, len ); + + if(result != 1) + { + char errStr[120]; // specs require string at least 120 bytes long.. + unsigned long errVal = 0; + if((errVal = ERR_get_error()) != 0) + LOG(ERROR) << "openssl error: " << ERR_error_string( errVal, errStr ); + + return false; + } else + return true; +} + +uint64_t SSL_Cipher::MAC_64( const unsigned char *data, int len, + const CipherKey &key, uint64_t *chainedIV ) const +{ + shared_ptr mk = dynamic_pointer_cast(key); + uint64_t tmp = _checksum_64( mk.get(), data, len, chainedIV ); + + if(chainedIV) + *chainedIV = tmp; + + return tmp; +} + +CipherKey SSL_Cipher::readKey(const unsigned char *data, + const CipherKey &masterKey, bool checkKey) +{ + shared_ptr mk = dynamic_pointer_cast(masterKey); + rAssert(mk->keySize == _keySize); + + unsigned char tmpBuf[ 2 * MAX_KEYLENGTH ]; + + // First N bytes are checksum bytes. + unsigned int checksum = 0; + for(int i=0; i key( new SSLKey( _keySize, _ivLength) ); + + rAssert(_keySize + _ivLength == (unsigned int)key->buf.size ); + memcpy( key->buf.data, tmpBuf, key->buf.size ); + OPENSSL_cleanse(tmpBuf, sizeof(tmpBuf)); + + initKey( key, _blockCipher, _streamCipher, _keySize ); + + return key; +} + +void SSL_Cipher::writeKey(const CipherKey &ckey, unsigned char *data, + const CipherKey &masterKey) +{ + shared_ptr key = dynamic_pointer_cast(ckey); + rAssert(key->keySize == _keySize); + rAssert(key->ivLength == _ivLength); + + shared_ptr mk = dynamic_pointer_cast(masterKey); + rAssert(mk->keySize == _keySize); + rAssert(mk->ivLength == _ivLength); + + unsigned char tmpBuf[ 2 * MAX_KEYLENGTH ]; + + unsigned int bufLen = key->buf.size; + rAssert(_keySize + _ivLength == bufLen ); + memcpy( tmpBuf, key->buf.data, bufLen ); + + unsigned int checksum = MAC_32( tmpBuf, bufLen, masterKey ); + + if (_streamCipher != NULL) + streamEncode(tmpBuf, bufLen, checksum, masterKey); + else + { + bufLen = 2 * _keySize; + blockEncode(tmpBuf, bufLen, checksum, masterKey); + } + + memcpy( data+KEY_CHECKSUM_BYTES, tmpBuf, bufLen ); + + // first N bytes contain HMAC derived checksum.. + for(int i=1; i<=KEY_CHECKSUM_BYTES; ++i) + { + data[KEY_CHECKSUM_BYTES-i] = checksum & 0xff; + checksum >>= 8; + } + + OPENSSL_cleanse(tmpBuf, sizeof(tmpBuf)); +} + +bool SSL_Cipher::compareKey( const CipherKey &A, const CipherKey &B) const +{ + shared_ptr key1 = dynamic_pointer_cast(A); + shared_ptr key2 = dynamic_pointer_cast(B); + + rAssert(key1->buf.size == key2->buf.size); + + if(memcmp(key1->buf.data, key2->buf.data, key1->buf.size) != 0) + return false; + else + return true; +} + +int SSL_Cipher::encodedKeySize() const +{ + if (_streamCipher != NULL) + return _keySize + _ivLength + KEY_CHECKSUM_BYTES; + else + return 2 * _keySize + KEY_CHECKSUM_BYTES; +} + +int SSL_Cipher::keySize() const +{ + return _keySize; +} + +int SSL_Cipher::cipherBlockSize() const +{ + int size = EVP_CIPHER_block_size( _blockCipher ); + // OpenSSL (1.0.1-4ubuntu5.5) reports a block size of 1 for aes_xts. + // If this happens, use a single key width (ie 32 bytes for aes-xts-256). + if (size == 1) + size = _keySize / 2; + return size; +} + +void SSL_Cipher::setIVec(unsigned char *ivec, uint64_t seed, + const shared_ptr &key) const +{ + if (iface.major() >= 3) + { + memcpy( ivec, IVData(key), _ivLength ); + + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned int mdLen = EVP_MAX_MD_SIZE; + + for(int i=0; i<8; ++i) + { + md[i] = (unsigned char)(seed & 0xff); + seed >>= 8; + } + + // combine ivec and seed with HMAC + HMAC_Init_ex( &key->mac_ctx, 0, 0, 0, 0 ); + HMAC_Update( &key->mac_ctx, ivec, _ivLength ); + HMAC_Update( &key->mac_ctx, md, 8 ); + HMAC_Final( &key->mac_ctx, md, &mdLen ); + rAssert(mdLen >= _ivLength); + + memcpy( ivec, md, _ivLength ); + } else + { + setIVec_old(ivec, seed, key); + } +} + +// Deprecated: For backward compatibility only. +// A watermark attack was discovered against this IV setup. If an attacker +// could get a victim to store a carefully crafted file, they could later +// determine if the victim had the file in encrypted storage (without decrypting +// the file). +void SSL_Cipher::setIVec_old(unsigned char *ivec, + unsigned int seed, + const shared_ptr &key) const +{ + unsigned int var1 = 0x060a4011 * seed; + unsigned int var2 = 0x0221040d * (seed ^ 0xD3FEA11C); + + memcpy( ivec, IVData(key), _ivLength ); + + ivec[0] ^= (var1 >> 24) & 0xff; + ivec[1] ^= (var2 >> 16) & 0xff; + ivec[2] ^= (var1 >> 8 ) & 0xff; + ivec[3] ^= (var2 ) & 0xff; + ivec[4] ^= (var2 >> 24) & 0xff; + ivec[5] ^= (var1 >> 16) & 0xff; + ivec[6] ^= (var2 >> 8 ) & 0xff; + ivec[7] ^= (var1 ) & 0xff; + + if(_ivLength > 8) + { + ivec[8+0] ^= (var1 ) & 0xff; + ivec[8+1] ^= (var2 >> 8 ) & 0xff; + ivec[8+2] ^= (var1 >> 16) & 0xff; + ivec[8+3] ^= (var2 >> 24) & 0xff; + ivec[8+4] ^= (var1 >> 24) & 0xff; + ivec[8+5] ^= (var2 >> 16) & 0xff; + ivec[8+6] ^= (var1 >> 8 ) & 0xff; + ivec[8+7] ^= (var2 ) & 0xff; + } +} + +static void flipBytes(unsigned char *buf, int size) +{ + unsigned char revBuf[64]; + + int bytesLeft = size; + while(bytesLeft) + { + int toFlip = MIN( (int)sizeof(revBuf), bytesLeft ); + + for(int i=0; i 0 ); + shared_ptr key = dynamic_pointer_cast(ckey); + rAssert(key->keySize == _keySize); + rAssert(key->ivLength == _ivLength); + rAssert( key->stream_enc.key_len > 0 ); + + Lock lock( key->mutex ); + + unsigned char ivec[ MAX_IVLENGTH ]; + int dstLen=0, tmpLen=0; + + shuffleBytes( buf, size ); + + setIVec( ivec, iv64, key ); + EVP_EncryptInit_ex( &key->stream_enc, NULL, NULL, NULL, ivec); + EVP_EncryptUpdate( &key->stream_enc, buf, &dstLen, buf, size ); + EVP_EncryptFinal_ex( &key->stream_enc, buf+dstLen, &tmpLen ); + + flipBytes( buf, size ); + shuffleBytes( buf, size ); + + setIVec( ivec, iv64 + 1, key ); + EVP_EncryptInit_ex( &key->stream_enc, NULL, NULL, NULL, ivec); + EVP_EncryptUpdate( &key->stream_enc, buf, &dstLen, buf, size ); + EVP_EncryptFinal_ex( &key->stream_enc, buf+dstLen, &tmpLen ); + + dstLen += tmpLen; + LOG_IF(ERROR, dstLen != size) << "encoding " << size + << " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)"; + + return true; +} + +bool SSL_Cipher::streamDecode(unsigned char *buf, int size, + uint64_t iv64, const CipherKey &ckey) const +{ + rAssert( size > 0 ); + shared_ptr key = dynamic_pointer_cast(ckey); + rAssert(key->keySize == _keySize); + rAssert(key->ivLength == _ivLength); + rAssert( key->stream_dec.key_len > 0 ); + + Lock lock( key->mutex ); + + unsigned char ivec[ MAX_IVLENGTH ]; + int dstLen=0, tmpLen=0; + + setIVec( ivec, iv64 + 1, key ); + EVP_DecryptInit_ex( &key->stream_dec, NULL, NULL, NULL, ivec); + EVP_DecryptUpdate( &key->stream_dec, buf, &dstLen, buf, size ); + EVP_DecryptFinal_ex( &key->stream_dec, buf+dstLen, &tmpLen ); + + unshuffleBytes( buf, size ); + flipBytes( buf, size ); + + setIVec( ivec, iv64, key ); + EVP_DecryptInit_ex( &key->stream_dec, NULL, NULL, NULL, ivec); + EVP_DecryptUpdate( &key->stream_dec, buf, &dstLen, buf, size ); + EVP_DecryptFinal_ex( &key->stream_dec, buf+dstLen, &tmpLen ); + + unshuffleBytes( buf, size ); + + dstLen += tmpLen; + LOG_IF(ERROR, dstLen != size) << "encoding " << size + << " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)"; + + return true; +} + + +bool SSL_Cipher::blockEncode(unsigned char *buf, int size, + uint64_t iv64, const CipherKey &ckey ) const +{ + rAssert( size > 0 ); + shared_ptr key = dynamic_pointer_cast(ckey); + rAssert(key->keySize == _keySize); + rAssert(key->ivLength == _ivLength); + + // data must be integer number of blocks + const int blockMod = size % EVP_CIPHER_CTX_block_size( &key->block_enc ); + rAssert(blockMod == 0); + + Lock lock( key->mutex ); + + unsigned char ivec[ MAX_IVLENGTH ]; + + int dstLen = 0, tmpLen = 0; + setIVec( ivec, iv64, key ); + + EVP_EncryptInit_ex( &key->block_enc, NULL, NULL, NULL, ivec); + EVP_EncryptUpdate( &key->block_enc, buf, &dstLen, buf, size ); + EVP_EncryptFinal_ex( &key->block_enc, buf+dstLen, &tmpLen ); + dstLen += tmpLen; + + LOG_IF(ERROR, dstLen != size) << "encoding " << size + << " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)"; + + return true; +} + +bool SSL_Cipher::blockDecode(unsigned char *buf, int size, + uint64_t iv64, const CipherKey &ckey ) const +{ + rAssert( size > 0 ); + shared_ptr key = dynamic_pointer_cast(ckey); + rAssert(key->keySize == _keySize); + rAssert(key->ivLength == _ivLength); + + // data must be integer number of blocks + const int blockMod = size % EVP_CIPHER_CTX_block_size( &key->block_dec ); + rAssert(blockMod == 0); + + Lock lock( key->mutex ); + + unsigned char ivec[ MAX_IVLENGTH ]; + + int dstLen = 0, tmpLen = 0; + setIVec( ivec, iv64, key ); + + EVP_DecryptInit_ex( &key->block_dec, NULL, NULL, NULL, ivec); + EVP_DecryptUpdate( &key->block_dec, buf, &dstLen, buf, size ); + EVP_DecryptFinal_ex( &key->block_dec, buf+dstLen, &tmpLen ); + dstLen += tmpLen; + + LOG_IF(ERROR, dstLen != size) << "decoding " << size + << " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)"; + + return true; +} + +bool SSL_Cipher::Enabled() +{ + return true; +} + +bool SSL_Cipher::hasStreamMode() const +{ + return false; +} diff --git a/cipher/SSL_Cipher.h b/cipher/SSL_Cipher.h new file mode 100644 index 0000000..9eb4d1b --- /dev/null +++ b/cipher/SSL_Cipher.h @@ -0,0 +1,159 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef _SSL_Cipher_incl_ +#define _SSL_Cipher_incl_ + +#include "cipher/Cipher.h" +#include "base/Interface.h" + +class SSLKey; +#ifndef EVP_CIPHER +struct evp_cipher_st; +typedef struct evp_cipher_st EVP_CIPHER; +#endif + +/* + Implements Cipher interface for OpenSSL's ciphers. + + Design: + Variable algorithm, key size, and block size. + + Partial blocks, keys, and names are encrypted using the cipher in a pseudo + stream mode (CFB). + + Keys are encrypted with 2-4 (KEY_CHECKSUM_BYTES define) checksum bytes + derived from an HMAC over both they key data and the initial value vector + associated with the key. This allows a good chance at detecting an + incorrect password when we try and decrypt the master key. + + File names are encrypted in the same way, with 2 checksum bytes derived + from an HMAC over the filename. This is done not to allow checking the + results, but to make the output much more random. Changing one letter in a + filename should result in a completely different encrypted filename, to + help frustrate any attempt to guess information about files from their + encrypted names. + + Stream encryption involves two encryption passes over the data, implemented + as: + 1. shuffle + 2. encrypt + 3. reverse + 4. shuffle + 5. encrypt + The reason for the shuffle and reverse steps (and the second encrypt pass) + is to try and propogate any changed bits to a larger set. If only a single + pass was made with the stream cipher in CFB mode, then a change to one byte + may only affect one byte of output, allowing some XOR attacks. + + The shuffle/encrypt is used as above in filename encryption as well, + although it is not necessary as they have checksum bytes which augment the + initial value vector to randomize the output. But it makes the code + simpler to reuse the encryption algorithm as is. +*/ +class SSL_Cipher : public Cipher +{ + Interface iface; + Interface realIface; + const EVP_CIPHER *_blockCipher; + const EVP_CIPHER *_streamCipher; + unsigned int _keySize; // in bytes + unsigned int _ivLength; + + public: + SSL_Cipher(const Interface &iface, const Interface &realIface, + const EVP_CIPHER *blockCipher, const EVP_CIPHER *streamCipher, + int keyLength); + virtual ~SSL_Cipher(); + + // returns the real interface, not the one we're emulating (if any).. + virtual Interface interface() const; + + // create a new key based on a password + virtual CipherKey newKey(const char *password, int passwdLength, + int &iterationCount, long desiredDuration, + const unsigned char *salt, int saltLen); + // deprecated - for backward compatibility + virtual CipherKey newKey(const char *password, int passwdLength); + // create a new random key + virtual CipherKey newRandomKey(); + + // data must be len keySize() + virtual CipherKey readKey(const unsigned char *data, + const CipherKey &encodingKey, + bool checkKey); + virtual void writeKey(const CipherKey &key, unsigned char *data, + const CipherKey &encodingKey); + virtual bool compareKey( const CipherKey &A, + const CipherKey &B ) const; + + // meta-data about the cypher + virtual int keySize() const; + virtual int encodedKeySize() const; + virtual int cipherBlockSize() const; + + virtual bool hasStreamMode() const; + + virtual bool randomize( unsigned char *buf, int len, + bool strongRandom ) const; + + virtual uint64_t MAC_64( const unsigned char *src, int len, + const CipherKey &key, uint64_t *augment ) const; + + // functional interfaces + /* + Stream encoding in-place. + */ + virtual bool streamEncode(unsigned char *in, int len, + uint64_t iv64, const CipherKey &key) const; + virtual bool streamDecode(unsigned char *in, int len, + uint64_t iv64, const CipherKey &key) const; + + /* + Block encoding is done in-place. Partial blocks are supported, but + blocks are always expected to begin on a block boundary. See + blockSize(). + */ + virtual bool blockEncode(unsigned char *buf, int size, + uint64_t iv64, const CipherKey &key) const; + virtual bool blockDecode(unsigned char *buf, int size, + uint64_t iv64, const CipherKey &key) const; + + // hack to help with static builds + static bool Enabled(); + + // Password-based key derivation function which determines the + // number of iterations based on a desired execution time (in microseconds). + // Returns the number of iterations applied. + static int TimedPBKDF2(const char *pass, int passLen, + const unsigned char *salt, int saltLen, + int keyLen, unsigned char *out, + long desiredPDFTimeMicroseconds); + private: + void setIVec( unsigned char *ivec, uint64_t seed, + const shared_ptr &key ) const; + + // deprecated - for backward compatibility + void setIVec_old( unsigned char *ivec, unsigned int seed, + const shared_ptr &key ) const; +}; + +#endif + diff --git a/cipher/openssl.cpp b/cipher/openssl.cpp new file mode 100644 index 0000000..658ea99 --- /dev/null +++ b/cipher/openssl.cpp @@ -0,0 +1,109 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2007, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "cipher/openssl.h" + +#include + +#include + +#define NO_DES +#include +#include +#ifndef OPENSSL_NO_ENGINE +#include +#endif + +unsigned long pthreads_thread_id() +{ + return (unsigned long)pthread_self(); +} + +static pthread_mutex_t *crypto_locks = NULL; +void pthreads_locking_callback( int mode, int n, + const char *caller_file, int caller_line ) +{ + (void)caller_file; + (void)caller_line; + + if(!crypto_locks) + { + VLOG(1) << "Allocating " << CRYPTO_num_locks() << " locks for OpenSSL"; + crypto_locks = new pthread_mutex_t[ CRYPTO_num_locks() ]; + for(int i=0; i= 5) - { - return 0; - } else - return -1; -]])], - [AC_MSG_RESULT([yes])], - [AC_MSG_RESULT([no]) - AC_MSG_FAILURE([ -Encfs 1.3 requires FUSE 2.5 or newer. Please check config.log for errors. If -you cannot determine the problem, mail encfs-users@lists.sourceforge.net -and include the config.log file]) - ] -) - -dnl fuse_operations.setxattr was added 2004-03-31 -dnl only enable it if setxattr function is found.. -AC_CHECK_HEADERS([attr/xattr.h sys/xattr.h]) - -dnl xattr functions take additional arguments on some systems (eg Darwin). -AC_CACHE_CHECK([whether xattr interface takes additional options], - smb_attr_cv_xattr_add_opt, [ - old_LIBS=$LIBS - LIBS="$LIBS $ACL_LIBS" - AC_TRY_COMPILE([ - #include - #if HAVE_ATTR_XATTR_H - #include - #elif HAVE_SYS_XATTR_H - #include - #endif - ],[ - getxattr(0, 0, 0, 0, 0, 0); - ], - [smb_attr_cv_xattr_add_opt=yes], - [smb_attr_cv_xattr_add_opt=no;LIBS=$old_LIBS]) -]) -if test x"$smb_attr_cv_xattr_add_opt" = x"yes"; then - AC_DEFINE(XATTR_ADD_OPT, 1, [xattr functions have additional options]) -fi - -dnl Check for valgrind headers.. -AC_ARG_ENABLE(valgrind, - AC_HELP_STRING([--enable-valgrind], - [build with valgrind support.]), - AC_CHECK_HEADERS([valgrind/valgrind.h valgrind/memcheck.h]) - ) - -# allow user option of not using ssl.. -AC_ARG_ENABLE(openssl, - AC_HELP_STRING([--disable-openssl], - [disables openssl library usage.]), - with_openssl=$enableval, with_openssl="yes" ) - -# try checking for openssl using -if test "x$with_openssl" = "xyes"; then - # look for openssl using pkg-config first.. - PKG_CHECK_MODULES(OPENSSL, openssl >= 0.9.7, - with_openssl="yes", with_openssl="old-test") - - # If that fails, try checking via old methods - which isn't as robust when - # it comes to extra include paths, etc.. - if test "x$with_openssl" = "xold-test"; then - AC_CHECK_HEADER(openssl/ssl.h, - AC_CHECK_LIB(ssl,SSL_new, - [with_openssl="yes"])) - OPENSSL_LIBS="-lssl" - fi - - # if we have openssl, then examine available interfaces. - if test "x$with_openssl" = "xyes"; then - AC_DEFINE(HAVE_SSL, [1], [Linking with OpenSSL]) - - # add in the libs just for the test.. - oldflags=$CXXFLAGS - oldlibs=$LIBS - CXXFLAGS="$CXXFLAGS $OPENSSL_CFLAGS" - LIBS="$LIBS $OPENSSL_LIBS" - AC_CHECK_FUNCS(EVP_aes_128_cbc EVP_aes_192_cbc EVP_aes_256_cbc, - AC_DEFINE(HAVE_EVP_AES, [1], [Have EVP AES interfaces])) - AC_CHECK_FUNCS(EVP_bf_cbc, - AC_DEFINE(HAVE_EVP_BF, [1], [Have EVP Blowfish interfaces])) - AC_CHECK_FUNCS(EVP_CIPHER_CTX_set_padding, - with_opensslevp=yes, - AC_MSG_WARN([New SSL cipher code only enabled for OpenSSL 0.9.7 or later])) - AC_CHECK_FUNCS(HMAC_Init_ex, - AC_DEFINE(HAVE_HMAC_INIT_EX, [1], [Have HMAC_Init_ex function])) - - CXXFLAGS="$oldflags" - LIBS="$oldlibs" - - AC_SUBST(HAVE_EVP_AES) - AC_SUBST(HAVE_EVP_BF) - AC_SUBST(HAVE_HMAC_INIT_EX) - fi -fi -AM_CONDITIONAL( BUILD_OPENSSL, test "x$with_openssl" = "xyes" ) -AM_CONDITIONAL( BUILD_SSLCIPHER, test "x$with_opensslevp" = "xyes" ) -AC_SUBST(HAVE_SSL) - -if test "x$with_openssl" != "xyes"; then - AC_MSG_ERROR( [Encfs requires OpenSSL]) -fi - -# check for RLOG -PKG_CHECK_MODULES(RLOG, librlog >= 1.3, with_rlog="yes", with_rlog="test") - -# manual check for rlog, unless environment variable already set -if test "$with_rlog" = "test" && test "x$RLOG_LIBS" = "x"; then - AC_MSG_WARN([Checking for librlog the hard way]) - AC_CHECK_LIB(rlog, RLogVersion, [RLOG_LIBS="-lrlog"], - [AC_MSG_ERROR([EncFS depends on librlog])]) -fi - -# find Protocol Buffers -PKG_CHECK_MODULES(PROTOBUF, protobuf >= 2.0) -AC_PATH_PROG(PROTOC, protoc, [no]) -if test "$PROTOC" == "no"; then - AC_MSG_FAILURE([Protocol Buffers compiler 'protoc' is required to build.]) -fi - -# find TinyXML -AC_LANG_PUSH([C++]) -CPPFLAGS="$CPPFLAGS -DTIXML_USE_STL" -AC_CHECK_HEADER([tinyxml.h],,[AC_MSG_ERROR([tinyxml.h not found])]) -AC_CHECK_LIB([tinyxml],[main],, - [AC_MSG_ERROR([you must install libtinyxml dev])]) -AC_LANG_POP([C++]) - -# look for pod2man program for building man pages -AC_PATH_PROG(POD2MAN, pod2man, [no]) -AC_PATH_PROG(POD2HTML, pod2html, [no]) -AM_CONDITIONAL( BUILD_MAN, test "x$POD2MAN" != "xno" ) -AM_CONDITIONAL( BUILD_MANHTML, test "x$POD2HTML" != "xno" ) -AM_CONDITIONAL( BUILD_NLS, test "x$USE_NLS" != "xno" ) - - -AC_CONFIG_FILES([Makefile] \ - [encfs/Makefile] \ - [encfs.spec] \ - [makedist2.sh] \ - [m4/Makefile] \ - [po/Makefile.in] \ - [po/Makefile]) - -AC_OUTPUT - diff --git a/devmode b/devmode new file mode 100644 index 0000000..5d53278 --- /dev/null +++ b/devmode @@ -0,0 +1,2 @@ +mkdir build +cd build && cmake .. -DCMAKE_BUILD_TYPE=Debug $@ diff --git a/encfs.spec.in b/encfs.spec.in deleted file mode 100644 index bab0b81..0000000 --- a/encfs.spec.in +++ /dev/null @@ -1,207 +0,0 @@ -Name: encfs -Summary: Encrypted pass-thru filesystem for Linux -Version: @VERSION@ -Release: @RELEASE@ -License: GPL -Group: System/Filesystems -Source: %{name}-%{version}-%{release}.tgz -BuildRoot: %{_tmppath}/build-root-%{name} -Packager: Valient Gough -#Distribution: Suse 9.1 -Prefix: /usr -Url: http://pobox.com/~vgough/encfs -Provides: encfs -Provides: encfsctl -Provides: libencfs.1 - -Requires: rlog >= 1.3 -Requires: openssl -Requires: fuse >= 2.2 - -%description -EncFS implements an encrypted filesystem in userspace using FUSE. FUSE -provides a Linux kernel module which allows virtual filesystems to be written -in userspace. EncFS encrypts all data and filenames in the filesystem and -passes access through to the underlying filesystem. Similar to CFS except that -it does not use NFS. - -%changelog -* Fri Nov 11 2005 Valient Gough -- Release 1.2.5 -- Fix race condition when using newer versions of GCC. Fixes problem reported - by Chris at x.nu. -- add encfssh script, thanks to David Rosenstrauch -* Fri Aug 26 2005 Valient Gough -- Release 1.2.4 -- fix segfault if small invalid filenames were encountered in the encrypted - directory, reported by paulgfx. -- try and detect if user tries to mount the filesystem over the top of the - encrypted directory, problem reported by paulgfx. -- environment variable ENCFS5_CONFIG can be used to override the location of - the .encfs5 configuration file. -- add encfsctl 'export' command, patch from Janne Hellsten - -* Tue Apr 19 2005 Valient Gough -- Release 1.2.1 -- add --public mount option -- add --stdinpass option to read password from stdin for scripting -- import latest rosetta translation updates - -* Thu Feb 10 2005 Valient Gough -- Release 1.2.0 -- Fix bug with MAC headers and files > 2GB, reported by Damian Frank -- Fix bug with external password interface which could result in problems - communicating with external password program. Found by Olivier Dournaux. -- Switch to FUSE 2.2 API -- support for FUSE 1.x has been dropped. -- Add support for inode numbering pass-thru (when used 'use_ino' option to - fuse). This allows encoded filesystem to use the same inode numbers as the - underlying filesystem. - -* Wed Jan 12 2005 Valient Gough -- Release 1.1.11 -- add internationalization support. Thanks to lots of contributors, there are - translations for serveral languages. -- added workaround for libfuse mount failure with FUSE 1.4 -- fix compile failure with FUSE 1.4 - -* Mon Nov 8 2004 Valient Gough -- Release 1.1.10 -- fix problems with recursive rename -- fix incorrect error codes from xattr functions - -* Tue Aug 15 2004 Valient Gough -- Release 1.1.9 -- fix another rename bug (affected filesystems with 'paranoia' configuration) - -* Mon Aug 14 2004 Valient Gough -- Release 1.1.8 -- Improve MAC block header processing. - -* Sat Aug 12 2004 Valient Gough -- Release 1.1.7 -- fix bug in truncate() for unopened files. - -* Mon Aug 9 2004 Valient Gough -- Release 1.1.6 -- fix header IV creation when truncate() used to create files. -- add support for IV chaining to old 0.x filesystem support code (useful for - systems with old OpenSSL, like RedHat 7.x). - -* Tue Jul 22 2004 Valient Gough -- Release 1.1.5 - -* Sat Jul 10 2004 Valient Gough -- Release 1.1.4 -- add external password prompt support. - -* Thu Jun 24 2004 Valient Gough -- Release 1.1.3 - -* Fri May 28 2004 Valient Gough -- Release 1.1.2 -- Fix bug affecting filesystems with small empty directories (like XFS) -- Updates to recursive rename code to undo all changes on failure. -- Fix OpenSSL dependency path inclusion in build. - -* Wed May 19 2004 Valient Gough -- Release 1.1.1 -- Fix MAC header memory size allocation error. -- Add file rename-while-open support needed for Evolution. - -* Thu May 13 2004 Valient Gough -- Second release candidate for version 1.1 -- Add support for block mode filename encryption. -- Add support for per-file initialization vectors. -- Add support for directory IV chaining for per-directory initialization - vectors. -- Add support for per-block MAC headers for file contents. -- Backward compatibility support dropped for filesystems created by version - 0.x. Maintains backward compatible support for versions 1.0.x. - -* Sun Apr 4 2004 Valient Gough -- Release 1.0.5 -- Allow truncate call to extend file (only shrinking was supported) - -* Fri Mar 26 2004 Valient Gough -- Release 1.0.4 -- Large speed improvement. -- Add support for FUSE major version 2 API. - -* Thu Mar 18 2004 Valient Gough -- Release 1.0.3 -- Fix bugs in truncation and padding code. - -* Sat Mar 13 2004 Valient Gough -- Release 1.0.2 -- Use pkg-config to check for OpenSSL and RLog build settings -- Add support for '--' argument to encfs to pass arbitrary options to FUSE / - fusermount. -- Add man pages. - -* Tue Mar 2 2004 Valient Gough -- Release 1.0.1 -- Fix problem with using OpenSSL's EVP_BytesToKey function with variable - key length ciphers like Blowfish, as it would only generate 128 bit keys. -- Some configure script changes to make it possible to use --with-extra-include - configure option to pick up any necessary directories for OpenSSL. - -* Fri Feb 27 2004 Valient Gough -- Release 1.0 -- Added some pre-defined configuration options at startup to make filesystem - creation a bit more user friendly. - -* Mon Feb 23 2004 Valient Gough -- Merge development branch to mainline. Source modularized to make it easier - to support different algorithms. -- Added encfsctl program which can show information about an encrypted - directory and can change the user password used to store the volume key. -- Added support for AES and BlowFish with user specified keys and block sizes - (when building with OpenSSL >= 0.9.7). -- Backward compatible with old format, but new filesystems store configuration - information in a new format which is not readable by old encfs versions. - -* Sat Feb 7 2004 Valient Gough -- Improved performance by fixing cache bug which caused cached data to not be - used as often as it could have been. Random seek performance improved by - 600% according to Bonnie++ benchmark. -- Fixed bugs preventing files larger then 2GB. Limit should now be around - 128GB (untested - I don't have that much drive space). > 2GB also requires - recent version of FUSE module (from Feb 6 or later) and an underlying - filesystem which supports large files. -- Release 0.6 - -%prep -rm -rf $RPM_BUILD_ROOT -mkdir $RPM_BUILD_ROOT - -%setup -q - -%build -CFLAGS="$RPM_OPT_FLAGS" CXXFLAGS="$RPM_OPT_FLAGS" \ -./configure --enable-debug=no --prefix=%{prefix} --mandir=%{_mandir} -make SED=/usr/bin/sed -j 2 - -%install -make DESTDIR=$RPM_BUILD_ROOT install-strip - -cd $RPM_BUILD_ROOT - -find . -type d -fprint $RPM_BUILD_DIR/file.list.%{name}.dirs -find . -type f -fprint $RPM_BUILD_DIR/file.list.%{name}.files.tmp -sed '/\/man\//s/$/.gz/g' $RPM_BUILD_DIR/file.list.%{name}.files.tmp > $RPM_BUILD_DIR/file.list.%{name}.files -find . -type l -fprint $RPM_BUILD_DIR/file.list.%{name}.libs -sed '1,2d;s,^\.,\%attr(-\,root\,root) \%dir ,' $RPM_BUILD_DIR/file.list.%{name}.dirs > $RPM_BUILD_DIR/file.list.%{name} -sed 's,^\.,\%attr(-\,root\,root) ,' $RPM_BUILD_DIR/file.list.%{name}.files >> $RPM_BUILD_DIR/file.list.%{name} -sed 's,^\.,\%attr(-\,root\,root) ,' $RPM_BUILD_DIR/file.list.%{name}.libs >> $RPM_BUILD_DIR/file.list.%{name} - -%clean -case "$RPM_BUILD_ROOT" in build-root-*) rm -rf $RPM_BUILD_ROOT ;; esac -rm -f $RPM_BUILD_DIR/file.list.%{name} -rm -f $RPM_BUILD_DIR/file.list.%{name}.libs -rm -f $RPM_BUILD_DIR/file.list.%{name}.files -rm -f $RPM_BUILD_DIR/file.list.%{name}.files.tmp -rm -f $RPM_BUILD_DIR/file.list.%{name}.dirs - -%files -f ../file.list.%{name} - -%defattr(-,root,root,0755) diff --git a/encfs/BlockFileIO.cpp b/encfs/BlockFileIO.cpp deleted file mode 100644 index c3e91e4..0000000 --- a/encfs/BlockFileIO.cpp +++ /dev/null @@ -1,433 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2004, Valient Gough - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "BlockFileIO.h" - -#include "MemoryPool.h" -#include "config.pb.h" - -#include -#include - -#include "i18n.h" - -template -inline Type min( Type A, Type B ) -{ - return (B < A) ? B : A; -} - -static void clearCache( IORequest &req, int blockSize ) -{ - memset( req.data, 0, blockSize ); - req.dataLen = 0; -} - -BlockFileIO::BlockFileIO( int blockSize, const FSConfigPtr &cfg ) - : _blockSize( blockSize ) - , _allowHoles( cfg->config->allow_holes() ) -{ - rAssert( _blockSize > 1 ); - _cache.data = new unsigned char [ _blockSize ]; -} - -BlockFileIO::~BlockFileIO() -{ - clearCache( _cache, _blockSize ); - delete[] _cache.data; -} - -ssize_t BlockFileIO::cacheReadOneBlock( const IORequest &req ) const -{ - // we can satisfy the request even if _cache.dataLen is too short, because - // we always request a full block during reads.. - if((req.offset == _cache.offset) && (_cache.dataLen != 0)) - { - // satisfy request from cache - int len = req.dataLen; - if(_cache.dataLen < len) - len = _cache.dataLen; - memcpy( req.data, _cache.data, len ); - return len; - } else - { - if(_cache.dataLen > 0) - clearCache( _cache, _blockSize ); - - // cache results of read -- issue reads for full blocks - IORequest tmp; - tmp.offset = req.offset; - tmp.data = _cache.data; - tmp.dataLen = _blockSize; - ssize_t result = readOneBlock( tmp ); - if(result > 0) - { - _cache.offset = req.offset; - _cache.dataLen = result; // the amount we really have - if(result > req.dataLen) - result = req.dataLen; // only as much as requested - memcpy( req.data, _cache.data, result ); - } - return result; - } -} - -bool BlockFileIO::cacheWriteOneBlock( const IORequest &req ) -{ - // cache results of write (before pass-thru, because it may be modified - // in-place) - memcpy( _cache.data, req.data, req.dataLen ); - _cache.offset = req.offset; - _cache.dataLen = req.dataLen; - bool ok = writeOneBlock( req ); - if(!ok) - clearCache( _cache, _blockSize ); - return ok; -} - -ssize_t BlockFileIO::read( const IORequest &req ) const -{ - rAssert( _blockSize != 0 ); - - int partialOffset = req.offset % _blockSize; - off_t blockNum = req.offset / _blockSize; - ssize_t result = 0; - - if(partialOffset == 0 && req.dataLen <= _blockSize) - { - // read completely within a single block -- can be handled as-is by - // readOneBloc(). - return cacheReadOneBlock( req ); - } else - { - size_t size = req.dataLen; - - // if the request is larger then a block, then request each block - // individually - MemBlock mb; // in case we need to allocate a temporary block.. - IORequest blockReq; // for requests we may need to make - blockReq.dataLen = _blockSize; - blockReq.data = NULL; - - unsigned char *out = req.data; - while( size ) - { - blockReq.offset = blockNum * _blockSize; - - // if we're reading a full block, then read directly into the - // result buffer instead of using a temporary - if(partialOffset == 0 && size >= (size_t)_blockSize) - blockReq.data = out; - else - { - if(!mb.data) - mb = MemoryPool::allocate( _blockSize ); - blockReq.data = mb.data; - } - - ssize_t readSize = cacheReadOneBlock( blockReq ); - if(unlikely(readSize <= partialOffset)) - break; // didn't get enough bytes - - int cpySize = min( (size_t)(readSize - partialOffset), size ); - rAssert(cpySize <= readSize); - - // if we read to a temporary buffer, then move the data - if(blockReq.data != out) - memcpy( out, blockReq.data + partialOffset, cpySize ); - - result += cpySize; - size -= cpySize; - out += cpySize; - ++blockNum; - partialOffset = 0; - - if(unlikely(readSize < _blockSize)) - break; - } - - if(mb.data) - MemoryPool::release( mb ); - } - - return result; -} - -bool BlockFileIO::write( const IORequest &req ) -{ - rAssert( _blockSize != 0 ); - - off_t fileSize = getSize(); - - // where write request begins - off_t blockNum = req.offset / _blockSize; - int partialOffset = req.offset % _blockSize; - - // last block of file (for testing write overlaps with file boundary) - off_t lastFileBlock = fileSize / _blockSize; - ssize_t lastBlockSize = fileSize % _blockSize; - - off_t lastNonEmptyBlock = lastFileBlock; - if(lastBlockSize == 0) - --lastNonEmptyBlock; - - if( req.offset > fileSize ) - { - // extend file first to fill hole with 0's.. - const bool forceWrite = false; - padFile( fileSize, req.offset, forceWrite ); - } - - // check against edge cases where we can just let the base class handle the - // request as-is.. - if(partialOffset == 0 && req.dataLen <= _blockSize) - { - // if writing a full block.. pretty safe.. - if( req.dataLen == _blockSize ) - return cacheWriteOneBlock( req ); - - // if writing a partial block, but at least as much as what is - // already there.. - if(blockNum == lastFileBlock && req.dataLen >= lastBlockSize) - return cacheWriteOneBlock( req ); - } - - // have to merge data with existing block(s).. - MemBlock mb; - - IORequest blockReq; - blockReq.data = NULL; - blockReq.dataLen = _blockSize; - - bool ok = true; - size_t size = req.dataLen; - unsigned char *inPtr = req.data; - while( size ) - { - blockReq.offset = blockNum * _blockSize; - int toCopy = min((size_t)(_blockSize - partialOffset), size); - - // if writing an entire block, or writing a partial block that requires - // no merging with existing data.. - if( (toCopy == _blockSize) - ||(partialOffset == 0 && blockReq.offset + toCopy >= fileSize)) - { - // write directly from buffer - blockReq.data = inPtr; - blockReq.dataLen = toCopy; - } else - { - // need a temporary buffer, since we have to either merge or pad - // the data. - if(!mb.data) - mb = MemoryPool::allocate( _blockSize ); - memset( mb.data, 0, _blockSize ); - blockReq.data = mb.data; - - if(blockNum > lastNonEmptyBlock) - { - // just pad.. - blockReq.dataLen = toCopy + partialOffset; - } else - { - // have to merge with existing block data.. - blockReq.dataLen = _blockSize; - blockReq.dataLen = cacheReadOneBlock( blockReq ); - - // extend data if necessary.. - if( partialOffset + toCopy > blockReq.dataLen ) - blockReq.dataLen = partialOffset + toCopy; - } - // merge in the data to be written.. - memcpy( blockReq.data + partialOffset, inPtr, toCopy ); - } - - // Finally, write the damn thing! - if(!cacheWriteOneBlock( blockReq )) - { - ok = false; - break; - } - - // prepare to start all over with the next block.. - size -= toCopy; - inPtr += toCopy; - ++blockNum; - partialOffset = 0; - } - - if(mb.data) - MemoryPool::release( mb ); - - return ok; -} - -int BlockFileIO::blockSize() const -{ - return _blockSize; -} - -void BlockFileIO::padFile( off_t oldSize, off_t newSize, bool forceWrite ) -{ - off_t oldLastBlock = oldSize / _blockSize; - off_t newLastBlock = newSize / _blockSize; - int newBlockSize = newSize % _blockSize; - - IORequest req; - MemBlock mb; - - if(oldLastBlock == newLastBlock) - { - // when the real write occurs, it will have to read in the existing - // data and pad it anyway, so we won't do it here (unless we're - // forced). - if( forceWrite ) - { - mb = MemoryPool::allocate( _blockSize ); - req.data = mb.data; - - req.offset = oldLastBlock * _blockSize; - req.dataLen = oldSize % _blockSize; - int outSize = newSize % _blockSize; // outSize > req.dataLen - - if(outSize) - { - memset( mb.data, 0, outSize ); - cacheReadOneBlock( req ); - req.dataLen = outSize; - cacheWriteOneBlock( req ); - } - } else - rDebug("optimization: not padding last block"); - } else - { - mb = MemoryPool::allocate( _blockSize ); - req.data = mb.data; - - // 1. extend the first block to full length - // 2. write the middle empty blocks - // 3. write the last block - - req.offset = oldLastBlock * _blockSize; - req.dataLen = oldSize % _blockSize; - - // 1. req.dataLen == 0, iff oldSize was already a multiple of blocksize - if(req.dataLen != 0) - { - rDebug("padding block %" PRIi64, oldLastBlock); - memset( mb.data, 0, _blockSize ); - cacheReadOneBlock( req ); - req.dataLen = _blockSize; // expand to full block size - cacheWriteOneBlock( req ); - ++oldLastBlock; - } - - // 2, pad zero blocks unless holes are allowed - if(!_allowHoles) - { - for(; oldLastBlock != newLastBlock; ++oldLastBlock) - { - rDebug("padding block %" PRIi64, oldLastBlock); - req.offset = oldLastBlock * _blockSize; - req.dataLen = _blockSize; - memset( mb.data, 0, req.dataLen ); - cacheWriteOneBlock( req ); - } - } - - // 3. only necessary if write is forced and block is non 0 length - if(forceWrite && newBlockSize) - { - req.offset = newLastBlock * _blockSize; - req.dataLen = newBlockSize; - memset( mb.data, 0, req.dataLen ); - cacheWriteOneBlock( req ); - } - } - - if(mb.data) - MemoryPool::release( mb ); -} - -int BlockFileIO::truncate( off_t size, FileIO *base ) -{ - int partialBlock = size % _blockSize; - int res = 0; - - off_t oldSize = getSize(); - - if( size > oldSize ) - { - // truncate can be used to extend a file as well. truncate man page - // states that it will pad with 0's. - // do the truncate so that the underlying filesystem can allocate - // the space, and then we'll fill it in padFile.. - if(base) - base->truncate( size ); - - const bool forceWrite = true; - padFile( oldSize, size, forceWrite ); - } else - if( size == oldSize ) - { - // the easiest case, but least likely.... - } else - if( partialBlock ) - { - // partial block after truncate. Need to read in the block being - // truncated before the truncate. Then write it back out afterwards, - // since the encoding will change.. - off_t blockNum = size / _blockSize; - MemBlock mb = MemoryPool::allocate( _blockSize ); - - IORequest req; - req.offset = blockNum * _blockSize; - req.dataLen = _blockSize; - req.data = mb.data; - - ssize_t rdSz = cacheReadOneBlock( req ); - - // do the truncate - if(base) - res = base->truncate( size ); - - // write back out partial block - req.dataLen = partialBlock; - bool wrRes = cacheWriteOneBlock( req ); - - if((rdSz < 0) || (!wrRes)) - { - // rwarning - unlikely to ever occur.. - rWarning(_("truncate failure: read %i bytes, partial block of %i"), - (int)rdSz, partialBlock ); - } - - MemoryPool::release( mb ); - } else - { - // truncating on a block bounday. No need to re-encode the last - // block.. - if(base) - res = base->truncate( size ); - } - - return res; -} - diff --git a/encfs/BlockNameIO.cpp b/encfs/BlockNameIO.cpp deleted file mode 100644 index b318965..0000000 --- a/encfs/BlockNameIO.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2004-2011, Valient Gough - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "BlockNameIO.h" - -#include "Cipher.h" -#include "base64.h" - -#include -#include -#include -#include - -#include "i18n.h" - -using namespace rlog; - -static RLogChannel * Info = DEF_CHANNEL( "info/nameio", Log_Info ); - - -static shared_ptr NewBlockNameIO( const Interface &iface, - const shared_ptr &cipher, const CipherKey &key ) -{ - int blockSize = 8; - if(cipher) - blockSize = cipher->cipherBlockSize(); - - return shared_ptr( - new BlockNameIO( iface, cipher, key, blockSize, false)); -} - -static shared_ptr NewBlockNameIO32( const Interface &iface, - const shared_ptr &cipher, const CipherKey &key ) -{ - int blockSize = 8; - if(cipher) - blockSize = cipher->cipherBlockSize(); - - return shared_ptr( - new BlockNameIO( iface, cipher, key, blockSize, true)); -} - -static bool BlockIO_registered = NameIO::Register("Block", - // description of block name encoding algorithm.. - // xgroup(setup) - gettext_noop("Block encoding, hides file name size somewhat"), - BlockNameIO::CurrentInterface(false), - NewBlockNameIO); - -static bool BlockIO32_registered = NameIO::Register("Block32", - // description of block name encoding algorithm.. - // xgroup(setup) - gettext_noop("Block encoding with base32 output for case-sensitive systems"), - BlockNameIO::CurrentInterface(true), - NewBlockNameIO32); - -/* - - Version 1.0 computed MAC over the filename, but not the padding bytes. - This version was from pre-release 1.1, never publically released, so no - backward compatibility necessary. - - - Version 2.0 includes padding bytes in MAC computation. This way the MAC - computation uses the same number of bytes regardless of the number of - padding bytes. - - - Version 3.0 uses full 64 bit initialization vector during IV chaining. - Prior versions used only the output from the MAC_16 call, giving a 1 in - 2^16 chance of the same name being produced. Using the full 64 bit IV - changes that to a 1 in 2^64 chance.. - - - Version 4.0 adds support for base32, creating names more suitable for - case-insensitive filesystems (eg Mac). -*/ -Interface BlockNameIO::CurrentInterface(bool caseSensitive) -{ - // implement major version 4 plus support for two prior versions - if (caseSensitive) - return makeInterface("nameio/block32", 4, 0, 2); - else - return makeInterface("nameio/block", 4, 0, 2); -} - -BlockNameIO::BlockNameIO( const Interface &iface, - const shared_ptr &cipher, - const CipherKey &key, int blockSize, - bool caseSensitiveEncoding ) - : _interface( iface.major() ) - , _bs( blockSize ) - , _cipher( cipher ) - , _key( key ) - , _caseSensitive( caseSensitiveEncoding ) -{ - // just to be safe.. - rAssert( blockSize < 128 ); -} - -BlockNameIO::~BlockNameIO() -{ -} - -Interface BlockNameIO::interface() const -{ - return CurrentInterface(_caseSensitive); -} - -int BlockNameIO::maxEncodedNameLen( int plaintextNameLen ) const -{ - // number of blocks, rounded up.. Only an estimate at this point, err on - // the size of too much space rather then too little. - int numBlocks = ( plaintextNameLen + _bs ) / _bs; - int encodedNameLen = numBlocks * _bs + 2; // 2 checksum bytes - if (_caseSensitive) - return B256ToB32Bytes( encodedNameLen ); - else - return B256ToB64Bytes( encodedNameLen ); -} - -int BlockNameIO::maxDecodedNameLen( int encodedNameLen ) const -{ - int decLen256 = _caseSensitive ? - B32ToB256Bytes( encodedNameLen ) : - B64ToB256Bytes( encodedNameLen ); - return decLen256 - 2; // 2 checksum bytes removed.. -} - -int BlockNameIO::encodeName( const char *plaintextName, int length, - uint64_t *iv, char *encodedName ) const -{ - // copy the data into the encoding buffer.. - memcpy( encodedName+2, plaintextName, length ); - - // Pad encryption buffer to block boundary.. - int padding = _bs - length % _bs; - if(padding == 0) - padding = _bs; // padding a full extra block! - - memset( encodedName+length+2, (unsigned char)padding, padding ); - - // store the IV before it is modified by the MAC call. - uint64_t tmpIV = 0; - if( iv && _interface >= 3 ) - tmpIV = *iv; - - // include padding in MAC computation - unsigned int mac = _cipher->MAC_16( (unsigned char *)encodedName+2, - length+padding, _key, iv ); - - // add checksum bytes - encodedName[0] = (mac >> 8) & 0xff; - encodedName[1] = (mac ) & 0xff; - - _cipher->blockEncode( (unsigned char *)encodedName+2, length+padding, - (uint64_t)mac ^ tmpIV, _key); - - // convert to base 64 ascii - int encodedStreamLen = length + 2 + padding; - int encLen; - - if (_caseSensitive) - { - encLen = B256ToB32Bytes( encodedStreamLen ); - - changeBase2Inline( (unsigned char *)encodedName, encodedStreamLen, - 8, 5, true ); - B32ToAscii( (unsigned char *)encodedName, encLen ); - } else - { - encLen = B256ToB64Bytes( encodedStreamLen ); - - changeBase2Inline( (unsigned char *)encodedName, encodedStreamLen, - 8, 6, true ); - B64ToAscii( (unsigned char *)encodedName, encLen ); - } - - return encLen; -} - -int BlockNameIO::decodeName( const char *encodedName, int length, - uint64_t *iv, char *plaintextName ) const -{ - int decLen256 = _caseSensitive ? - B32ToB256Bytes( length ) : - B64ToB256Bytes( length ); - int decodedStreamLen = decLen256 - 2; - - // don't bother trying to decode files which are too small - if(decodedStreamLen < _bs) - throw ERROR("Filename too small to decode"); - - BUFFER_INIT( tmpBuf, 32, (unsigned int)length ); - - // decode into tmpBuf, - if (_caseSensitive) - { - AsciiToB32((unsigned char *)tmpBuf, (unsigned char *)encodedName, length); - changeBase2Inline((unsigned char *)tmpBuf, length, 5, 8, false); - } else - { - AsciiToB64((unsigned char *)tmpBuf, (unsigned char *)encodedName, length); - changeBase2Inline((unsigned char *)tmpBuf, length, 6, 8, false); - } - - // pull out the header information - unsigned int mac = ((unsigned int)((unsigned char)tmpBuf[0])) << 8 - | ((unsigned int)((unsigned char)tmpBuf[1])); - - uint64_t tmpIV = 0; - if( iv && _interface >= 3 ) - tmpIV = *iv; - - _cipher->blockDecode( (unsigned char *)tmpBuf+2, decodedStreamLen, - (uint64_t)mac ^ tmpIV, _key); - - // find out true string length - int padding = (unsigned char)tmpBuf[2+decodedStreamLen-1]; - int finalSize = decodedStreamLen - padding; - - // might happen if there is an error decoding.. - if(padding > _bs || finalSize < 0) - { - rDebug("padding, _bx, finalSize = %i, %i, %i", padding, - _bs, finalSize); - throw ERROR( "invalid padding size" ); - } - - // copy out the result.. - memcpy(plaintextName, tmpBuf+2, finalSize); - plaintextName[finalSize] = '\0'; - - // check the mac - unsigned int mac2 = _cipher->MAC_16((const unsigned char *)tmpBuf+2, - decodedStreamLen, _key, iv); - - BUFFER_RESET( tmpBuf ); - - if(mac2 != mac) - { - rDebug("checksum mismatch: expected %u, got %u", mac, mac2); - rDebug("on decode of %i bytes", finalSize); - throw ERROR( "checksum mismatch in filename decode" ); - } - - return finalSize; -} - -bool BlockNameIO::Enabled() -{ - return true; -} - diff --git a/encfs/CMakeLists.txt b/encfs/CMakeLists.txt new file mode 100644 index 0000000..ef21be2 --- /dev/null +++ b/encfs/CMakeLists.txt @@ -0,0 +1,39 @@ + +include_directories (${Encfs_SOURCE_DIR}/base) +link_directories (${Encfs_BINARY_DIR}/base) + +include_directories (${Encfs_SOURCE_DIR}/cipher) +link_directories (${Encfs_BINARY_DIR}/cipher) + +include_directories (${Encfs_SOURCE_DIR}/fs) +link_directories (${Encfs_BINARY_DIR}/fs) + +# TODO: move FUSE code into encfs-fs. +find_package (FUSE REQUIRED) +include_directories (${FUSE_INCLUDE_DIR}) + +include_directories (${CMAKE_BINARY_DIR}/base) + +add_executable (encfs + main.cpp) +target_link_libraries (encfs + encfs-fs + encfs-cipher + encfs-base + ${GLOG_LIBRARIES} + ${FUSE_LIBRARIES} +) + +if (POD2MAN) + add_custom_target(man ALL + COMMAND ${POD2MAN} -u --section=1 --release=${ENCFS_VERSION} + --center="Encrypted Filesystem" + ${CMAKE_CURRENT_SOURCE_DIR}/encfs.pod + encfs.1) + install (FILES ${CMAKE_CURRENT_BINARY_DIR}/encfs.1 + DESTINATION + share/man/man1) +endif (POD2MAN) + +install (TARGETS encfs DESTINATION bin) + diff --git a/encfs/CipherFileIO.cpp b/encfs/CipherFileIO.cpp deleted file mode 100644 index 4cbb8e8..0000000 --- a/encfs/CipherFileIO.cpp +++ /dev/null @@ -1,438 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2004, Valient Gough - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "CipherFileIO.h" - -#include "Cipher.h" -#include "MemoryPool.h" -#include "config.pb.h" - -#include -#include - -#include -#include - -/* - - Version 2:0 adds support for a per-file initialization vector with a - fixed 8 byte header. The headers are enabled globally within a - filesystem at the filesystem configuration level. - When headers are disabled, 2:0 is compatible with version 1:0. -*/ -static Interface CipherFileIO_iface = makeInterface("FileIO/Cipher", 2, 0, 1); - -const int HEADER_SIZE = 8; // 64 bit initialization vector.. - -static bool checkSize( int fsBlockSize, int cipherBlockSize ) -{ - int blockBoundary = fsBlockSize % cipherBlockSize ; - if(blockBoundary != 0) - { - rError("CipherFileIO: blocks should be multiple of cipher block size"); - return true; - } else - return false; -} - -CipherFileIO::CipherFileIO( const shared_ptr &_base, - const FSConfigPtr &cfg) - : BlockFileIO( cfg->config->block_size(), cfg ) - , base( _base ) - , haveHeader( cfg->config->unique_iv() ) - , externalIV( 0 ) - , fileIV( 0 ) - , lastFlags( 0 ) -{ - fsConfig = cfg; - cipher = cfg->cipher; - key = cfg->key; - - static bool warnOnce = false; - - if(!warnOnce) - warnOnce = checkSize( fsConfig->config->block_size(), - fsConfig->cipher->cipherBlockSize() ); -} - -CipherFileIO::~CipherFileIO() -{ -} - -Interface CipherFileIO::interface() const -{ - return CipherFileIO_iface; -} - -int CipherFileIO::open( int flags ) -{ - int res = base->open( flags ); - - if( res >= 0 ) - lastFlags = flags; - - return res; -} - -void CipherFileIO::setFileName( const char *fileName ) -{ - base->setFileName( fileName ); -} - -const char *CipherFileIO::getFileName() const -{ - return base->getFileName(); -} - -bool CipherFileIO::setIV( uint64_t iv ) -{ - rDebug("in setIV, current IV = %" PRIu64 ", new IV = %" PRIu64 - ", fileIV = %" PRIu64, - externalIV, iv, fileIV); - if(externalIV == 0) - { - // we're just being told about which IV to use. since we haven't - // initialized the fileIV, there is no need to just yet.. - externalIV = iv; - if(fileIV != 0) - rWarning("fileIV initialized before externalIV! (%" PRIu64 - ", %" PRIu64 ")", fileIV, externalIV); - } else - if(haveHeader) - { - // we have an old IV, and now a new IV, so we need to update the fileIV - // on disk. - if(fileIV == 0) - { - // ensure the file is open for read/write.. - int newFlags = lastFlags | O_RDWR; - int res = base->open( newFlags ); - if(res < 0) - { - if(res == -EISDIR) - { - // duh -- there are no file headers for directories! - externalIV = iv; - return base->setIV( iv ); - } else - { - rDebug("writeHeader failed to re-open for write"); - return false; - } - } - initHeader(); - } - - uint64_t oldIV = externalIV; - externalIV = iv; - if(!writeHeader()) - { - externalIV = oldIV; - return false; - } - } - - return base->setIV( iv ); -} - -int CipherFileIO::getAttr( struct stat *stbuf ) const -{ - int res = base->getAttr( stbuf ); - // adjust size if we have a file header - if((res == 0) && haveHeader && - S_ISREG(stbuf->st_mode) && (stbuf->st_size > 0)) - { - rAssert(stbuf->st_size >= HEADER_SIZE); - stbuf->st_size -= HEADER_SIZE; - } - - return res; -} - -off_t CipherFileIO::getSize() const -{ - off_t size = base->getSize(); - // No check on S_ISREG here -- don't call getSize over getAttr unless this - // is a normal file! - if(haveHeader && size > 0) - { - rAssert(size >= HEADER_SIZE); - size -= HEADER_SIZE; - } - return size; -} - -void CipherFileIO::initHeader( ) -{ - // check if the file has a header, and read it if it does.. Otherwise, - // create one. - off_t rawSize = base->getSize(); - if(rawSize >= HEADER_SIZE) - { - rDebug("reading existing header, rawSize = %" PRIi64, rawSize); - // has a header.. read it - unsigned char buf[8] = {0}; - - IORequest req; - req.offset = 0; - req.data = buf; - req.dataLen = 8; - base->read( req ); - - cipher->streamDecode( buf, sizeof(buf), - externalIV, key ); - - fileIV = 0; - for(int i=0; i<8; ++i) - fileIV = (fileIV << 8) | (uint64_t)buf[i]; - - rAssert(fileIV != 0); // 0 is never used.. - } else - { - rDebug("creating new file IV header"); - - unsigned char buf[8] = {0}; - do - { - if(!cipher->randomize( buf, 8, false )) - throw ERROR("Unable to generate a random file IV"); - - fileIV = 0; - for(int i=0; i<8; ++i) - fileIV = (fileIV << 8) | (uint64_t)buf[i]; - - if(fileIV == 0) - rWarning("Unexpected result: randomize returned 8 null bytes!"); - } while(fileIV == 0); // don't accept 0 as an option.. - - if( base->isWritable() ) - { - cipher->streamEncode( buf, sizeof(buf), externalIV, key ); - - IORequest req; - req.offset = 0; - req.data = buf; - req.dataLen = 8; - - base->write( req ); - } else - rDebug("base not writable, IV not written.."); - } - rDebug("initHeader finished, fileIV = %" PRIu64 , fileIV); -} - -bool CipherFileIO::writeHeader( ) -{ - if( !base->isWritable() ) - { - // open for write.. - int newFlags = lastFlags | O_RDWR; - if( base->open( newFlags ) < 0 ) - { - rDebug("writeHeader failed to re-open for write"); - return false; - } - } - - if(fileIV == 0) - rError("Internal error: fileIV == 0 in writeHeader!!!"); - rDebug("writing fileIV %" PRIu64 , fileIV); - - unsigned char buf[8] = {0}; - for(int i=0; i<8; ++i) - { - buf[sizeof(buf)-1-i] = (unsigned char)(fileIV & 0xff); - fileIV >>= 8; - } - - cipher->streamEncode( buf, sizeof(buf), externalIV, key ); - - IORequest req; - req.offset = 0; - req.data = buf; - req.dataLen = 8; - - base->write( req ); - - return true; -} - -ssize_t CipherFileIO::readOneBlock( const IORequest &req ) const -{ - // read raw data, then decipher it.. - int bs = blockSize(); - off_t blockNum = req.offset / bs; - - ssize_t readSize = 0; - IORequest tmpReq = req; - - if(haveHeader) - tmpReq.offset += HEADER_SIZE; - readSize = base->read( tmpReq ); - - bool ok; - if(readSize > 0) - { - if(haveHeader && fileIV == 0) - const_cast(this)->initHeader(); - - if(readSize != bs) - { - ok = streamRead( tmpReq.data, (int)readSize, blockNum ^ fileIV); - } else - { - ok = blockRead( tmpReq.data, (int)readSize, blockNum ^ fileIV); - } - - if(!ok) - { - rDebug("decodeBlock failed for block %" PRIi64 ", size %i", - blockNum, (int)readSize ); - readSize = -1; - } - } else - rDebug("readSize zero for offset %" PRIi64, req.offset); - - return readSize; -} - - -bool CipherFileIO::writeOneBlock( const IORequest &req ) -{ - int bs = blockSize(); - off_t blockNum = req.offset / bs; - - if(haveHeader && fileIV == 0) - initHeader(); - - bool ok; - if( req.dataLen != bs ) - { - ok = streamWrite( req.data, (int)req.dataLen, - blockNum ^ fileIV ); - } else - { - ok = blockWrite( req.data, (int)req.dataLen, - blockNum ^ fileIV ); - } - - if( ok ) - { - if(haveHeader) - { - IORequest tmpReq = req; - tmpReq.offset += HEADER_SIZE; - ok = base->write( tmpReq ); - } else - ok = base->write( req ); - } else - { - rDebug("encodeBlock failed for block %" PRIi64 ", size %i", - blockNum, req.dataLen); - ok = false; - } - return ok; -} - -bool CipherFileIO::blockWrite( unsigned char *buf, int size, - uint64_t _iv64 ) const -{ - if (!fsConfig->reverseEncryption) - return cipher->blockEncode( buf, size, _iv64, key ); - else - return cipher->blockDecode( buf, size, _iv64, key ); -} - -bool CipherFileIO::streamWrite( unsigned char *buf, int size, - uint64_t _iv64 ) const -{ - if (!fsConfig->reverseEncryption) - return cipher->streamEncode( buf, size, _iv64, key ); - else - return cipher->streamDecode( buf, size, _iv64, key ); -} - - -bool CipherFileIO::blockRead( unsigned char *buf, int size, - uint64_t _iv64 ) const -{ - if (fsConfig->reverseEncryption) - return cipher->blockEncode( buf, size, _iv64, key ); - else - { - if(_allowHoles) - { - // special case - leave all 0's alone - for(int i=0; iblockDecode( buf, size, _iv64, key ); - - return true; - } else - return cipher->blockDecode( buf, size, _iv64, key ); - } -} - -bool CipherFileIO::streamRead( unsigned char *buf, int size, - uint64_t _iv64 ) const -{ - if (fsConfig->reverseEncryption) - return cipher->streamEncode( buf, size, _iv64, key ); - else - return cipher->streamDecode( buf, size, _iv64, key ); -} - - - -int CipherFileIO::truncate( off_t size ) -{ - int res = 0; - if(!haveHeader) - { - res = BlockFileIO::truncate( size, base.get() ); - } else - { - if(0 == fileIV) - { - // empty file.. create the header.. - if( !base->isWritable() ) - { - // open for write.. - int newFlags = lastFlags | O_RDWR; - if( base->open( newFlags ) < 0 ) - rDebug("writeHeader failed to re-open for write"); - } - initHeader(); - } - - // can't let BlockFileIO call base->truncate(), since it would be using - // the wrong size.. - res = BlockFileIO::truncate( size, 0 ); - - if(res == 0) - base->truncate( size + HEADER_SIZE ); - } - return res; -} - -bool CipherFileIO::isWritable() const -{ - return base->isWritable(); -} - diff --git a/encfs/ConfigReader.cpp b/encfs/ConfigReader.cpp deleted file mode 100644 index 8218e59..0000000 --- a/encfs/ConfigReader.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2004, Valient Gough - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "ConfigReader.h" - -#include - -#include -#include -#include -#include -#include - - -using namespace std; -using namespace rlog; - - -ConfigReader::ConfigReader() -{ -} - -ConfigReader::~ConfigReader() -{ -} - -// read the entire file into a ConfigVar instance and then use that to decode -// into mapped variables. -bool -ConfigReader::load(const char *fileName) -{ - struct stat stbuf; - memset( &stbuf, 0, sizeof(struct stat)); - if( lstat( fileName, &stbuf ) != 0) - return false; - - int size = stbuf.st_size; - - int fd = open( fileName, O_RDONLY ); - if(fd < 0) - return false; - - char *buf = new char[size]; - - int res = ::read( fd, buf, size ); - close( fd ); - - if( res != size ) - { - rWarning("Partial read of config file, expecting %i bytes, got %i", - size, res); - delete[] buf; - return false; - } - - ConfigVar in; - in.write( (unsigned char *)buf, size ); - delete[] buf; - - return loadFromVar( in ); -} - -bool -ConfigReader::loadFromVar(ConfigVar &in) -{ - in.resetOffset(); - - // parse. - int numEntries = in.readInt(); - - for(int i=0; i> key >> value; - - if(key.length() == 0) - { - rError("Invalid key encoding in buffer"); - return false; - } - ConfigVar newVar( value ); - vars.insert( make_pair( key, newVar ) ); - } - - return true; -} - -bool -ConfigReader::save(const char *fileName) const -{ - // write everything to a ConfigVar, then output to disk - ConfigVar out = toVar(); - - int fd = ::open( fileName, O_RDWR | O_CREAT, 0640 ); - if(fd >= 0) - { - int retVal = ::write( fd, out.buffer(), out.size() ); - close( fd ); - if(retVal != out.size()) - { - rError("Error writing to config file %s", fileName); - return false; - } - } else - { - rError("Unable to open or create file %s", fileName); - return false; - } - - return true; -} - -ConfigVar -ConfigReader::toVar() const -{ - // write everything to a ConfigVar, then output to disk - ConfigVar out; - out.writeInt( vars.size() ); - map::const_iterator it; - for(it = vars.begin(); it != vars.end(); ++it) - { - out.writeInt( it->first.size() ); - out.write( (unsigned char*)it->first.data(), it->first.size() ); - out.writeInt( it->second.size() ); - out.write( (unsigned char*)it->second.buffer(), it->second.size() ); - } - - return out; -} - -ConfigVar ConfigReader::operator[] ( const std::string &varName ) const -{ - // read only - map::const_iterator it = vars.find( varName ); - if( it == vars.end() ) - return ConfigVar(); - else - return it->second; -} - -ConfigVar &ConfigReader::operator[] ( const std::string &varName ) -{ - return vars[ varName ]; -} - diff --git a/encfs/ConfigVar.cpp b/encfs/ConfigVar.cpp deleted file mode 100644 index a677236..0000000 --- a/encfs/ConfigVar.cpp +++ /dev/null @@ -1,252 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2004, Valient Gough - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "ConfigVar.h" -#include - -#include - -using namespace rlog; - -#ifndef MIN -inline int MIN(int a, int b) -{ - return (a < b) ? a : b; -} -#endif - - -ConfigVar::ConfigVar() - : pd( new ConfigVarData ) -{ - pd->offset = 0; -} - -ConfigVar::ConfigVar(const std::string &buf) - : pd( new ConfigVarData ) -{ - pd->buffer = buf; - pd->offset = 0; -} - -ConfigVar::ConfigVar(const ConfigVar &src) -{ - pd = src.pd; -} - -ConfigVar::~ConfigVar() -{ - pd.reset(); -} - -ConfigVar & ConfigVar::operator = (const ConfigVar &src) -{ - if(src.pd == pd) - return *this; - else - pd = src.pd; - - return *this; -} - -void ConfigVar::resetOffset() -{ - pd->offset = 0; -} - -int ConfigVar::read(unsigned char *buffer_, int bytes) const -{ - int toCopy = MIN( bytes, pd->buffer.size() - pd->offset ); - - if(toCopy > 0) - memcpy( buffer_, pd->buffer.data() + pd->offset, toCopy ); - - pd->offset += toCopy; - - return toCopy; -} - -int ConfigVar::write(const unsigned char *data, int bytes) -{ - if(pd->buffer.size() == (unsigned int)pd->offset) - { - pd->buffer.append( (const char *)data, bytes ); - } else - { - pd->buffer.insert( pd->offset, (const char *)data, bytes ); - } - - pd->offset += bytes; - - return bytes; -} - -int ConfigVar::size() const -{ - return pd->buffer.size(); -} - -const char *ConfigVar::buffer() const -{ - return pd->buffer.data(); -} - -int ConfigVar::at() const -{ - return pd->offset; -} - -void ConfigVar::writeString(const char *data, int bytes) -{ - writeInt( bytes ); - write( (const unsigned char *)data, bytes ); -} - - -// convert integer to BER encoded integer -void ConfigVar::writeInt(int val) -{ - // we can represent 7 bits per char output, so a 32bit number may take up - // to 5 bytes. - // first byte: 0x0000007f 0111,1111 - // second byte: 0x00003f80 0011,1111 1000,0000 - // third byte: 0x001fb000 0000,0000 0001,1111 1100,0000 0000,0000 - // fourth byte: 0x0fe00000 0000,1111 1110,0000 - // fifth byte: 0xf0000000 1111,0000 - unsigned char digit[5]; - - digit[4] = (unsigned char)((val & 0x0000007f)); - digit[3] = 0x80 | (unsigned char)((val & 0x00003f80) >> 7); - digit[2] = 0x80 | (unsigned char)((val & 0x001fc000) >> 14); - digit[1] = 0x80 | (unsigned char)((val & 0x0fe00000) >> 21); - digit[0] = 0x80 | (unsigned char)((val & 0xf0000000) >> 28); - - // find the starting point - we only need to output starting at the most - // significant non-zero digit.. - int start = 0; - while(digit[start] == 0x80) - ++start; - - write( digit + start, 5-start ); -} - -int ConfigVar::readInt() const -{ - const unsigned char * buf = (const unsigned char *)buffer(); - int bytes = this->size(); - int offset = at(); - int value = 0; - bool highBitSet; - - rAssert( offset < bytes ); - - do - { - unsigned char tmp = buf[offset++]; - highBitSet = tmp & 0x80; - - value = (value << 7) | (int)(tmp & 0x7f); - } while(highBitSet && offset < bytes); - - pd->offset = offset; - - // should never end up with a negative number.. - rAssert( value >= 0 ); - - return value; -} - -int ConfigVar::readInt( int defaultValue ) const -{ - int bytes = this->size(); - int offset = at(); - - if(offset >= bytes) - return defaultValue; - else - return readInt(); -} - -bool ConfigVar::readBool( bool defaultValue ) const -{ - int tmp = readInt( defaultValue ? 1 : 0 ); - return (tmp != 0); -} - -ConfigVar & operator << (ConfigVar &src, bool value) -{ - src.writeInt( value ? 1 : 0 ); - return src; -} - -ConfigVar & operator << (ConfigVar &src, int var) -{ - src.writeInt( var ); - return src; -} - -ConfigVar & operator << (ConfigVar &src, const std::string &str) -{ - src.writeString( str.data(), str.length() ); - return src; -} - -const ConfigVar & operator >> (const ConfigVar &src, bool &result) -{ - int tmp = src.readInt(); - result = (tmp != 0); - return src; -} - -const ConfigVar & operator >> (const ConfigVar &src, int &result) -{ - result = src.readInt(); - return src; -} - -const ConfigVar & operator >> (const ConfigVar &src, std::string &result) -{ - int length = src.readInt(); - //rAssert(length > 0); - - int readLen; - - unsigned char tmpBuf[32]; - if(length > (int)sizeof(tmpBuf)) - { - unsigned char *ptr = new unsigned char[length]; - readLen = src.read( ptr, length ); - result.assign( (char*)ptr, length ); - delete[] ptr; - } else - { - readLen = src.read( tmpBuf, length ); - result.assign( (char*)tmpBuf, length ); - } - - if(readLen != length) - { - rDebug("string encoded as size %i bytes, read %i", length, readLen ); - } - rAssert(readLen == length); - - return src; -} - diff --git a/encfs/Context.cpp b/encfs/Context.cpp deleted file mode 100644 index 3eba9e5..0000000 --- a/encfs/Context.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2007, Valient Gough - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "FileNode.h" -#include "Context.h" -#include "Mutex.h" -#include "FileUtils.h" -#include "DirNode.h" - -#include - -using namespace rel; -using namespace rlog; - -EncFS_Context::EncFS_Context() -{ - pthread_cond_init( &wakeupCond, 0 ); - pthread_mutex_init( &wakeupMutex, 0 ); - pthread_mutex_init( &contextMutex, 0 ); - - usageCount = 0; -} - -EncFS_Context::~EncFS_Context() -{ - pthread_mutex_destroy( &contextMutex ); - pthread_mutex_destroy( &wakeupMutex ); - pthread_cond_destroy( &wakeupCond ); - - // release all entries from map - openFiles.clear(); -} - -shared_ptr EncFS_Context::getRoot(int *errCode) -{ - shared_ptr ret; - do - { - { - Lock lock( contextMutex ); - ret = root; - ++usageCount; - } - - if(!ret) - { - int res = remountFS( this ); - if(res != 0) - { - *errCode = res; - break; - } - } - } while(!ret); - - return ret; -} - -void EncFS_Context::setRoot(const shared_ptr &r) -{ - Lock lock( contextMutex ); - - root = r; - if(r) - rootCipherDir = r->rootDirectory(); -} - -bool EncFS_Context::isMounted() -{ - return root; -} - -int EncFS_Context::getAndResetUsageCounter() -{ - Lock lock( contextMutex ); - - int count = usageCount; - usageCount = 0; - - return count; -} - -int EncFS_Context::openFileCount() const -{ - Lock lock( contextMutex ); - - return openFiles.size(); -} - -shared_ptr EncFS_Context::lookupNode(const char *path) -{ - Lock lock( contextMutex ); - - FileMap::iterator it = openFiles.find( std::string(path) ); - if(it != openFiles.end()) - { - // all the items in the set point to the same node.. so just use the - // first - return (*it->second.begin())->node; - } else - { - return shared_ptr(); - } -} - -void EncFS_Context::renameNode(const char *from, const char *to) -{ - Lock lock( contextMutex ); - - FileMap::iterator it = openFiles.find( std::string(from) ); - if(it != openFiles.end()) - { - std::set val = it->second; - openFiles.erase(it); - openFiles[ std::string(to) ] = val; - } -} - -shared_ptr EncFS_Context::getNode(void *pl) -{ - Placeholder *ph = (Placeholder*)pl; - return ph->node; -} - -void *EncFS_Context::putNode(const char *path, - const shared_ptr &node) -{ - Lock lock( contextMutex ); - Placeholder *pl = new Placeholder( node ); - openFiles[ std::string(path) ].insert(pl); - - return (void *)pl; -} - -void EncFS_Context::eraseNode(const char *path, void *pl) -{ - Lock lock( contextMutex ); - - Placeholder *ph = (Placeholder *)pl; - - FileMap::iterator it = openFiles.find( std::string(path) ); - rAssert(it != openFiles.end()); - - int rmCount = it->second.erase( ph ); - - rAssert(rmCount == 1); - - // if no more references to this file, remove the record all together - if(it->second.empty()) - { - // attempts to make use of shallow copy to clear memory used to hold - // unencrypted filenames.. not sure this does any good.. - std::string storedName = it->first; - openFiles.erase( it ); - storedName.assign( storedName.length(), '\0' ); - } - - delete ph; -} - diff --git a/encfs/DirNode.cpp b/encfs/DirNode.cpp deleted file mode 100644 index 169c580..0000000 --- a/encfs/DirNode.cpp +++ /dev/null @@ -1,831 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2003-2004, Valient Gough - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "encfs.h" - -#include "DirNode.h" -#include "FileUtils.h" - -#include -#include -#include -#include -#include -#include -#include -#ifdef linux -#include -#endif - -#include - -#include "Context.h" -#include "Cipher.h" -#include "Mutex.h" -#include -#include - -#include - -using namespace std; -using namespace rel; -using namespace rlog; - -static RLogChannel *Info = DEF_CHANNEL( "info/DirNode", Log_Info ); - -class DirDeleter -{ -public: - void operator () ( DIR *d ) - { - ::closedir( d ); - } -}; - - -DirTraverse::DirTraverse(const shared_ptr &_dirPtr, - uint64_t _iv, const shared_ptr &_naming) - : dir( _dirPtr ) - , iv( _iv ) - , naming( _naming ) -{ -} - -DirTraverse::DirTraverse(const DirTraverse &src) - : dir( src.dir ) - , iv( src.iv ) - , naming( src.naming ) -{ -} - -DirTraverse &DirTraverse::operator = (const DirTraverse &src) -{ - dir = src.dir; - iv = src.iv; - naming = src.naming; - - return *this; -} - -DirTraverse::~DirTraverse() -{ - dir.reset(); - iv = 0; - naming.reset(); -} - -static -bool _nextName(struct dirent *&de, const shared_ptr &dir, - int *fileType, ino_t *inode) -{ - de = ::readdir( dir.get() ); - - if(de) - { - if(fileType) - { -#if defined(_DIRENT_HAVE_D_TYPE) || defined(__FreeBSD__) - *fileType = de->d_type; -#else -#warning "struct dirent.d_type not supported" - *fileType = 0; -#endif - } - if(inode) - *inode = de->d_ino; - return true; - } else - { - if(fileType) - *fileType = 0; - return false; - } -} - - -std::string DirTraverse::nextPlaintextName(int *fileType, ino_t *inode) -{ - struct dirent *de=0; - while(_nextName(de, dir, fileType, inode)) - { - try - { - uint64_t localIv = iv; - return naming->decodePath( de->d_name, &localIv ); - } catch ( rlog::Error &ex ) - { - // .. .problem decoding, ignore it and continue on to next name.. - rDebug("error decoding filename: %s", de->d_name); - } - } - - return string(); -} - -std::string DirTraverse::nextInvalid() -{ - struct dirent *de=0; - // find the first name which produces a decoding error... - while(_nextName(de, dir, (int*)0, (ino_t*)0)) - { - try - { - uint64_t localIv = iv; - naming->decodePath( de->d_name, &localIv ); - continue; - } catch( rlog::Error &ex ) - { - return string( de->d_name ); - } - } - - return string(); -} - -struct RenameEl -{ - // ciphertext names - string oldCName; - string newCName; // intermediate name (not final cname) - - // plaintext names - string oldPName; - string newPName; - - bool isDirectory; -}; - -class RenameOp -{ -private: - DirNode *dn; - shared_ptr< list > renameList; - list::const_iterator last; - -public: - RenameOp( DirNode *_dn, const shared_ptr< list > &_renameList ) - : dn(_dn), renameList(_renameList) - { - last = renameList->begin(); - } - - RenameOp(const RenameOp &src) - : dn(src.dn) - , renameList(src.renameList) - , last(src.last) - { - } - - ~RenameOp(); - - operator bool () const - { - return renameList; - } - - bool apply(); - void undo(); -}; - -RenameOp::~RenameOp() -{ - if(renameList) - { - // got a bunch of decoded filenames sitting in memory.. do a little - // cleanup before leaving.. - list::iterator it; - for(it = renameList->begin(); it != renameList->end(); ++it) - { - it->oldPName.assign( it->oldPName.size(), ' ' ); - it->newPName.assign( it->newPName.size(), ' ' ); - } - } -} - -bool RenameOp::apply() -{ - try - { - while(last != renameList->end()) - { - // backing store rename. - rDebug("renaming %s -> %s", - last->oldCName.c_str(), last->newCName.c_str()); - - struct stat st; - bool preserve_mtime = ::stat(last->oldCName.c_str(), &st) == 0; - - // internal node rename.. - dn->renameNode( last->oldPName.c_str(), - last->newPName.c_str() ); - - // rename on disk.. - if(::rename( last->oldCName.c_str(), - last->newCName.c_str() ) == -1) - { - rWarning("Error renaming %s: %s", - last->oldCName.c_str(), strerror( errno )); - dn->renameNode( last->newPName.c_str(), - last->oldPName.c_str(), false ); - return false; - } - - if(preserve_mtime) - { - struct utimbuf ut; - ut.actime = st.st_atime; - ut.modtime = st.st_mtime; - ::utime(last->newCName.c_str(), &ut); - } - - ++last; - } - - return true; - } catch( rlog::Error &err ) - { - err.log( _RLWarningChannel ); - return false; - } -} - -void RenameOp::undo() -{ - rDebug("in undoRename"); - - if(last == renameList->begin()) - { - rDebug("nothing to undo"); - return; // nothing to undo - } - - // list has to be processed backwards, otherwise we may rename - // directories and directory contents in the wrong order! - int undoCount = 0; - list::const_iterator it = last; - - while( it != renameList->begin() ) - { - --it; - - rDebug("undo: renaming %s -> %s", - it->newCName.c_str(), it->oldCName.c_str()); - - ::rename( it->newCName.c_str(), it->oldCName.c_str() ); - try - { - dn->renameNode( it->newPName.c_str(), - it->oldPName.c_str(), false ); - } catch( rlog::Error &err ) - { - err.log( _RLWarningChannel ); - // continue on anyway... - } - ++undoCount; - }; - - rWarning("Undo rename count: %i", undoCount); -} - -DirNode::DirNode(EncFS_Context *_ctx, - const string &sourceDir, - const FSConfigPtr &_config) -{ - pthread_mutex_init( &mutex, 0 ); - - Lock _lock( mutex ); - - ctx = _ctx; - rootDir = sourceDir; - fsConfig = _config; - - // make sure rootDir ends in '/', so that we can form a path by appending - // the rest.. - if( rootDir[ rootDir.length()-1 ] != '/' ) - rootDir.append( 1, '/'); - - naming = fsConfig->nameCoding; -} - -DirNode::~DirNode() -{ -} - -bool -DirNode::hasDirectoryNameDependency() const -{ - return naming ? naming->getChainedNameIV() : false; -} - -string -DirNode::rootDirectory() -{ - // don't update last access here, otherwise 'du' would cause lastAccess to - // be reset. - // chop off '/' terminator from root dir. - return string( rootDir, 0, rootDir.length()-1 ); -} - -string -DirNode::cipherPath( const char *plaintextPath ) -{ - return rootDir + naming->encodePath( plaintextPath ); -} - -string -DirNode::cipherPathWithoutRoot( const char *plaintextPath ) -{ - return naming->encodePath( plaintextPath ); -} - -string -DirNode::plainPath( const char *cipherPath_ ) -{ - try - { - if( !strncmp( cipherPath_, rootDir.c_str(), - rootDir.length() ) ) - { - return naming->decodePath( cipherPath_ + rootDir.length() ); - } else - { - if ( cipherPath_[0] == '+' ) - { - // decode as fully qualified path - return string("/") + naming->decodeName( cipherPath_+1, - strlen(cipherPath_+1) ); - } else - { - return naming->decodePath( cipherPath_ ); - } - } - - } catch( rlog::Error &err ) - { - rError("decode err: %s", err.message()); - err.log( _RLWarningChannel ); - - return string(); - } -} - -string -DirNode::relativeCipherPath( const char *plaintextPath ) -{ - try - { - if(plaintextPath[0] == '/') - { - // mark with '+' to indicate special decoding.. - return string("+") + naming->encodeName(plaintextPath+1, - strlen(plaintextPath+1)); - } else - { - return naming->encodePath( plaintextPath ); - } - } catch( rlog::Error &err ) - { - rError("encode err: %s", err.message()); - err.log( _RLWarningChannel ); - - return string(); - } -} - -DirTraverse DirNode::openDir(const char *plaintextPath) -{ - string cyName = rootDir + naming->encodePath( plaintextPath ); - //rDebug("openDir on %s", cyName.c_str() ); - - DIR *dir = ::opendir( cyName.c_str() ); - if(dir == NULL) - { - rDebug("opendir error %s", strerror(errno)); - return DirTraverse( shared_ptr(), 0, shared_ptr() ); - } else - { - shared_ptr dp( dir, DirDeleter() ); - - uint64_t iv = 0; - // if we're using chained IV mode, then compute the IV at this - // directory level.. - try - { - if( naming->getChainedNameIV() ) - naming->encodePath( plaintextPath, &iv ); - } catch( rlog::Error &err ) - { - rError("encode err: %s", err.message()); - err.log( _RLWarningChannel ); - } - return DirTraverse( dp, iv, naming ); - } -} - -bool DirNode::genRenameList( list &renameList, - const char *fromP, const char *toP ) -{ - uint64_t fromIV = 0, toIV = 0; - - // compute the IV for both paths - string fromCPart = naming->encodePath( fromP, &fromIV ); - string toCPart = naming->encodePath( toP, &toIV ); - - // where the files live before the rename.. - string sourcePath = rootDir + fromCPart; - - // ok..... we wish it was so simple.. should almost never happen - if(fromIV == toIV) - return true; - - // generate the real destination path, where we expect to find the files.. - rDebug("opendir %s", sourcePath.c_str() ); - shared_ptr dir = shared_ptr( - opendir( sourcePath.c_str() ), DirDeleter() ); - if(!dir) - return false; - - struct dirent *de = NULL; - while((de = ::readdir( dir.get() )) != NULL) - { - // decode the name using the oldIV - uint64_t localIV = fromIV; - string plainName; - - if((de->d_name[0] == '.') && - ((de->d_name[1] == '\0') - || ((de->d_name[1] == '.') && (de->d_name[2] == '\0')))) - { - // skip "." and ".." - continue; - } - - try - { - plainName = naming->decodePath( de->d_name, &localIV ); - } catch( rlog::Error &ex ) - { - // if filename can't be decoded, then ignore it.. - continue; - } - - // any error in the following will trigger a rename failure. - try - { - // re-encode using the new IV.. - localIV = toIV; - string newName = naming->encodePath( plainName.c_str(), &localIV ); - - // store rename information.. - string oldFull = sourcePath + '/' + de->d_name; - string newFull = sourcePath + '/' + newName; - - RenameEl ren; - ren.oldCName = oldFull; - ren.newCName = newFull; - ren.oldPName = string(fromP) + '/' + plainName; - ren.newPName = string(toP) + '/' + plainName; - - bool isDir; -#if defined(_DIRENT_HAVE_D_TYPE) - if(de->d_type != DT_UNKNOWN) - { - isDir = (de->d_type == DT_DIR); - } else -#endif - { - isDir = isDirectory( oldFull.c_str() ); - } - - ren.isDirectory = isDir; - - if(isDir) - { - // recurse.. We want to add subdirectory elements before the - // parent, as that is the logical rename order.. - if(!genRenameList( renameList, - ren.oldPName.c_str(), - ren.newPName.c_str())) - { - return false; - } - } - - rDebug("adding file %s to rename list", - oldFull.c_str()); - - renameList.push_back( ren ); - - } catch( rlog::Error &err ) - { - // We can't convert this name, because we don't have a valid IV for - // it (or perhaps a valid key).. It will be inaccessible.. - rWarning("Aborting rename: error on file: %s", - fromCPart.append(1, '/').append(de->d_name).c_str()); - err.log( _RLDebugChannel ); - - // abort.. Err on the side of safety and disallow rename, rather - // then loosing files.. - return false; - } - } - - return true; -} - - -/* - A bit of a pain.. If a directory is renamed in a filesystem with - directory initialization vector chaining, then we have to recursively - rename every descendent of this directory, as all initialization vectors - will have changed.. - - Returns a list of renamed items on success, a null list on failure. -*/ -shared_ptr -DirNode::newRenameOp( const char *fromP, const char *toP ) -{ - // Do the rename in two stages to avoid chasing our tail - // Undo everything if we encounter an error! - shared_ptr< list > renameList(new list); - if(!genRenameList( *renameList.get(), fromP, toP )) - { - rWarning("Error during generation of recursive rename list"); - return shared_ptr(); - } else - return shared_ptr( new RenameOp(this, renameList) ); -} - - -int DirNode::mkdir(const char *plaintextPath, mode_t mode, - uid_t uid, gid_t gid) -{ - string cyName = rootDir + naming->encodePath( plaintextPath ); - rAssert( !cyName.empty() ); - - rLog( Info, "mkdir on %s", cyName.c_str() ); - - // if uid or gid are set, then that should be the directory owner - int olduid = -1; - int oldgid = -1; - if(uid != 0) - olduid = setfsuid( uid ); - if(gid != 0) - oldgid = setfsgid( gid ); - - int res = ::mkdir( cyName.c_str(), mode ); - - if(olduid >= 0) - setfsuid( olduid ); - if(oldgid >= 0) - setfsgid( oldgid ); - - if(res == -1) - { - int eno = errno; - rWarning("mkdir error on %s mode %i: %s", cyName.c_str(), - mode, strerror(eno)); - res = -eno; - } else - res = 0; - - return res; -} - -int -DirNode::rename( const char *fromPlaintext, const char *toPlaintext ) -{ - Lock _lock( mutex ); - - string fromCName = rootDir + naming->encodePath( fromPlaintext ); - string toCName = rootDir + naming->encodePath( toPlaintext ); - rAssert( !fromCName.empty() ); - rAssert( !toCName.empty() ); - - rLog( Info, "rename %s -> %s", fromCName.c_str(), toCName.c_str() ); - - shared_ptr toNode = findOrCreate( toPlaintext ); - - shared_ptr renameOp; - if( hasDirectoryNameDependency() && isDirectory( fromCName.c_str() )) - { - rLog( Info, "recursive rename begin" ); - renameOp = newRenameOp( fromPlaintext, toPlaintext ); - - if(!renameOp || !renameOp->apply()) - { - if(renameOp) - renameOp->undo(); - - rWarning("rename aborted"); - return -EACCES; - } - rLog( Info, "recursive rename end" ); - } - - int res = 0; - try - { - struct stat st; - bool preserve_mtime = ::stat(fromCName.c_str(), &st) == 0; - - renameNode( fromPlaintext, toPlaintext ); - res = ::rename( fromCName.c_str(), toCName.c_str() ); - - if(res == -1) - { - // undo - res = -errno; - renameNode( toPlaintext, fromPlaintext, false ); - - if(renameOp) - renameOp->undo(); - } else if(preserve_mtime) - { - struct utimbuf ut; - ut.actime = st.st_atime; - ut.modtime = st.st_mtime; - ::utime(toCName.c_str(), &ut); - } - } catch( rlog::Error &err ) - { - // exception from renameNode, just show the error and continue.. - err.log( _RLWarningChannel ); - res = -EIO; - } - - if(res != 0) - { - rLog( Info, "rename failed: %s", strerror( errno )); - res = -errno; - } - - return res; -} - -int DirNode::link( const char *from, const char *to ) -{ - Lock _lock( mutex ); - - string fromCName = rootDir + naming->encodePath( from ); - string toCName = rootDir + naming->encodePath( to ); - - rAssert( !fromCName.empty() ); - rAssert( !toCName.empty() ); - - rLog(Info, "link %s -> %s", fromCName.c_str(), toCName.c_str()); - - int res = -EPERM; - if( fsConfig->config->external_iv() ) - { - rLog(Info, "hard links not supported with external IV chaining!"); - } else - { - res = ::link( fromCName.c_str(), toCName.c_str() ); - if(res == -1) - res = -errno; - else - res = 0; - } - - return res; -} - -/* - The node is keyed by filename, so a rename means the internal node names - must be changed. -*/ -shared_ptr DirNode::renameNode( const char *from, const char *to ) -{ - return renameNode( from, to, true ); -} - -shared_ptr DirNode::renameNode( const char *from, const char *to, - bool forwardMode ) -{ - shared_ptr node = findOrCreate( from ); - - if(node) - { - uint64_t newIV = 0; - string cname = rootDir + naming->encodePath( to, &newIV ); - - rLog(Info, "renaming internal node %s -> %s", - node->cipherName(), cname.c_str()); - - if(node->setName( to, cname.c_str(), newIV, forwardMode )) - { - if(ctx) - ctx->renameNode( from, to ); - } else - { - // rename error! - put it back - rError("renameNode failed"); - throw ERROR("Internal node name change failed!"); - } - } - - return node; -} - -shared_ptr DirNode::findOrCreate( const char *plainName) -{ - shared_ptr node; - if(ctx) - node = ctx->lookupNode( plainName ); - - if(!node) - { - uint64_t iv = 0; - string cipherName = naming->encodePath( plainName, &iv ); - node.reset( new FileNode( this, fsConfig, - plainName, - (rootDir + cipherName).c_str())); - - if(fsConfig->config->external_iv()) - node->setName(0, 0, iv); - - rLog(Info, "created FileNode for %s", node->cipherName()); - } - - return node; -} - -shared_ptr -DirNode::lookupNode( const char *plainName, const char * requestor ) -{ - (void)requestor; - Lock _lock( mutex ); - - shared_ptr node = findOrCreate( plainName ); - - return node; -} - -/* - Similar to lookupNode, except that we also call open() and only return a - node on sucess.. This is done in one step to avoid any race conditions - with the stored state of the file. -*/ -shared_ptr -DirNode::openNode( const char *plainName, const char * requestor, int flags, - int *result ) -{ - (void)requestor; - rAssert( result != NULL ); - Lock _lock( mutex ); - - shared_ptr node = findOrCreate( plainName ); - - if( node && (*result = node->open( flags )) >= 0 ) - return node; - else - return shared_ptr(); -} - -int DirNode::unlink( const char *plaintextName ) -{ - string cyName = naming->encodePath( plaintextName ); - rLog( Info, "unlink %s", cyName.c_str() ); - - Lock _lock( mutex ); - - int res = 0; - if(ctx && ctx->lookupNode( plaintextName )) - { - // If FUSE is running with "hard_remove" option where it doesn't - // hide open files for us, then we can't allow an unlink of an open - // file.. - rWarning("Refusing to unlink open file: %s, hard_remove option " - "is probably in effect", cyName.c_str() ); - res = -EBUSY; - } else - { - string fullName = rootDir + cyName; - res = ::unlink( fullName.c_str() ); - if(res == -1) - { - res = -errno; - rDebug("unlink error: %s", strerror(errno)); - } - } - - return res; -} diff --git a/encfs/FileNode.cpp b/encfs/FileNode.cpp deleted file mode 100644 index 4363423..0000000 --- a/encfs/FileNode.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2003-2004, Valient Gough - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -// Include encfs first, because we need to include fuse.h before any inclusion -// of sys/stat.h or other system headers (to be safe) -#include "encfs.h" - -#include -#include -#include -#include -#include -#ifdef linux -#include -#endif - -#include - -#include "config.h" - -#include "FileNode.h" -#include "FileUtils.h" -#include "Cipher.h" -#include "CipherFileIO.h" -#include "RawFileIO.h" -#include "MACFileIO.h" -#include "DirNode.h" - -#include "FileIO.h" -#include "MemoryPool.h" -#include "Mutex.h" - -#include -#include - -using namespace std; -using namespace rel; -using namespace rlog; - -/* - TODO: locking at the FileNode level is inefficient, since this precludes - multiple IO operations from going on concurrently within the same file. - - There is no reason why simultainous reads cannot be satisfied, or why one - read has to wait for the decoding of the previous read before it can be - sent to the IO subsystem! -*/ - -static RLogChannel *Info = DEF_CHANNEL("info/FileNode", Log_Info); - -FileNode::FileNode(DirNode *parent_, const FSConfigPtr &cfg, - const char *plaintextName_, const char *cipherName_) -{ - pthread_mutex_init( &mutex, 0 ); - - Lock _lock( mutex ); - - this->_pname = plaintextName_; - this->_cname = cipherName_; - this->parent = parent_; - - this->fsConfig = cfg; - - // chain RawFileIO & CipherFileIO - shared_ptr rawIO( new RawFileIO( _cname ) ); - io = shared_ptr( new CipherFileIO( rawIO, fsConfig )); - - if(cfg->config->block_mac_bytes() || cfg->config->block_mac_rand_bytes()) - io = shared_ptr(new MACFileIO(io, fsConfig)); -} - -FileNode::~FileNode() -{ - // FileNode mutex should be locked before the destructor is called - //pthread_mutex_lock( &mutex ); - - _pname.assign( _pname.length(), '\0' ); - _cname.assign( _cname.length(), '\0' ); - io.reset(); - - pthread_mutex_destroy( &mutex ); -} - -const char *FileNode::cipherName() const -{ - return _cname.c_str(); -} - -const char *FileNode::plaintextName() const -{ - return _pname.c_str(); -} - -string FileNode::plaintextParent() const -{ - return parentDirectory( _pname ); -} - -static bool setIV(const shared_ptr &io, uint64_t iv) -{ - struct stat stbuf; - if((io->getAttr(&stbuf) < 0) || S_ISREG(stbuf.st_mode)) - return io->setIV( iv ); - else - return true; -} - -bool FileNode::setName( const char *plaintextName_, const char *cipherName_, - uint64_t iv, bool setIVFirst ) -{ - //Lock _lock( mutex ); - rDebug("calling setIV on %s", cipherName_); - if(setIVFirst) - { - if(fsConfig->config->external_iv() && !setIV(io, iv)) - return false; - - // now change the name.. - if(plaintextName_) - this->_pname = plaintextName_; - if(cipherName_) - { - this->_cname = cipherName_; - io->setFileName( cipherName_ ); - } - } else - { - std::string oldPName = _pname; - std::string oldCName = _cname; - - if(plaintextName_) - this->_pname = plaintextName_; - if(cipherName_) - { - this->_cname = cipherName_; - io->setFileName( cipherName_ ); - } - - if(fsConfig->config->external_iv() && !setIV(io, iv)) - { - _pname = oldPName; - _cname = oldCName; - return false; - } - } - - return true; -} - -int FileNode::mknod(mode_t mode, dev_t rdev, uid_t uid, gid_t gid) -{ - Lock _lock( mutex ); - - int res; - int olduid = -1; - int oldgid = -1; - if(uid != 0) - { - olduid = setfsuid( uid ); - if(olduid == -1) - { - rInfo("setfsuid error: %s", strerror(errno)); - return -EPERM; - } - } - if(gid != 0) - { - oldgid = setfsgid( gid ); - if(oldgid == -1) - { - rInfo("setfsgid error: %s", strerror(errno)); - return -EPERM; - } - } - - /* - * cf. xmp_mknod() in fusexmp.c - * The regular file stuff could be stripped off if there - * were a create method (advised to have) - */ - if (S_ISREG( mode )) { - res = ::open( _cname.c_str(), O_CREAT | O_EXCL | O_WRONLY, mode ); - if (res >= 0) - res = ::close( res ); - } else if (S_ISFIFO( mode )) - res = ::mkfifo( _cname.c_str(), mode ); - else - res = ::mknod( _cname.c_str(), mode, rdev ); - - if(olduid >= 0) - setfsuid( olduid ); - if(oldgid >= 0) - setfsgid( oldgid ); - - if(res == -1) - { - int eno = errno; - rDebug("mknod error: %s", strerror(eno)); - res = -eno; - } - - return res; -} - -int FileNode::open(int flags) const -{ - Lock _lock( mutex ); - - int res = io->open( flags ); - return res; -} - -int FileNode::getAttr(struct stat *stbuf) const -{ - Lock _lock( mutex ); - - int res = io->getAttr( stbuf ); - return res; -} - -off_t FileNode::getSize() const -{ - Lock _lock( mutex ); - - int res = io->getSize(); - return res; -} - -ssize_t FileNode::read( off_t offset, unsigned char *data, ssize_t size ) const -{ - IORequest req; - req.offset = offset; - req.dataLen = size; - req.data = data; - - Lock _lock( mutex ); - - return io->read( req ); -} - -bool FileNode::write(off_t offset, unsigned char *data, ssize_t size) -{ - rLog(Info, "FileNode::write offset %" PRIi64 ", data size %i", - offset, (int)size); - - IORequest req; - req.offset = offset; - req.dataLen = size; - req.data = data; - - Lock _lock( mutex ); - - return io->write( req ); -} - -int FileNode::truncate( off_t size ) -{ - Lock _lock( mutex ); - - return io->truncate( size ); -} - -int FileNode::sync(bool datasync) -{ - Lock _lock( mutex ); - - int fh = io->open( O_RDONLY ); - if(fh >= 0) - { - int res = -EIO; -#ifdef linux - if(datasync) - res = fdatasync( fh ); - else - res = fsync( fh ); -#else - // no fdatasync support - // TODO: use autoconfig to check for it.. - res = fsync(fh); -#endif - - if(res == -1) - res = -errno; - - return res; - } else - return fh; -} - diff --git a/encfs/MACFileIO.cpp b/encfs/MACFileIO.cpp deleted file mode 100644 index b0002d2..0000000 --- a/encfs/MACFileIO.cpp +++ /dev/null @@ -1,304 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2004, Valient Gough - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "MACFileIO.h" - -#include "MemoryPool.h" -#include "FileUtils.h" -#include "config.pb.h" - -#include -#include -#include - -#include - -#include "i18n.h" - -using namespace rlog; -using namespace std; - -static RLogChannel *Info = DEF_CHANNEL("info/MACFileIO", Log_Info); -// -// Version 1.0 worked on blocks of size (blockSize + headerSize). -// That is, it took [blockSize] worth of user data and added headers. -// Version 2.0 takes [blockSize - headerSize] worth of user data and writes -// [blockSize] bytes. That way the size going into the crypto engine is -// valid from what was selected based on the crypto module allowed ranges! -// Version 2.1 allows per-block rand bytes to be used without enabling MAC. -// -// The information about MACFileIO currently does not make its way into the -// configuration file, so there is no easy way to make this backward -// compatible, except at a high level by checking a revision number for the -// filesystem... -// -static Interface MACFileIO_iface = makeInterface("FileIO/MAC", 2, 1, 0); - -int dataBlockSize(const FSConfigPtr &cfg) -{ - return cfg->config->block_size() - - cfg->config->block_mac_bytes() - - cfg->config->block_mac_rand_bytes(); -} - -MACFileIO::MACFileIO( const shared_ptr &_base, - const FSConfigPtr &cfg ) - : BlockFileIO( dataBlockSize( cfg ), cfg ) - , base( _base ) - , cipher( cfg->cipher ) - , key( cfg->key ) - , macBytes( cfg->config->block_mac_bytes() ) - , randBytes( cfg->config->block_mac_rand_bytes() ) - , warnOnly( cfg->opts->forceDecode ) -{ - rAssert( macBytes >= 0 && macBytes <= 8 ); - rAssert( randBytes >= 0 ); - rLog(Info, "fs block size = %i, macBytes = %i, randBytes = %i", - cfg->config->block_size(), - cfg->config->block_mac_bytes(), - cfg->config->block_mac_rand_bytes()); -} - -MACFileIO::~MACFileIO() -{ -} - -Interface MACFileIO::interface() const -{ - return MACFileIO_iface; -} - -int MACFileIO::open( int flags ) -{ - return base->open( flags ); -} - -void MACFileIO::setFileName( const char *fileName ) -{ - base->setFileName( fileName ); -} - -const char *MACFileIO::getFileName() const -{ - return base->getFileName(); -} - -bool MACFileIO::setIV( uint64_t iv ) -{ - return base->setIV( iv ); -} - -inline static off_t roundUpDivide( off_t numerator, int denominator ) -{ - // integer arithmetic always rounds down, so we can round up by adding - // enough so that any value other then a multiple of denominator gets - // rouned to the next highest value. - return ( numerator + denominator - 1 ) / denominator; -} - -// Convert from a location in the raw file to a location when MAC headers are -// interleved with the data. -// So, if the filesystem stores/encrypts [blockSize] bytes per block, then -// [blockSize - headerSize] of those bytes will contain user-supplied data, -// and the rest ([headerSize]) will contain the MAC header for this block. -// Example, offset points to second block (of user-data) -// offset = blockSize - headerSize -// ... blockNum = 1 -// ... partialBlock = 0 -// ... adjLoc = 1 * blockSize -static off_t locWithHeader( off_t offset, int blockSize, int headerSize ) -{ - off_t blockNum = roundUpDivide( offset , blockSize - headerSize ); - return offset + blockNum * headerSize; -} - -// convert from a given location in the stream containing headers, and return a -// location in the user-data stream (which doesn't contain MAC headers).. -// The output value will always be less then the input value, because the -// headers are stored at the beginning of the block, so even the first data is -// offset by the size of the header. -static off_t locWithoutHeader( off_t offset, int blockSize, int headerSize ) -{ - off_t blockNum = roundUpDivide( offset , blockSize ); - return offset - blockNum * headerSize; -} - -int MACFileIO::getAttr( struct stat *stbuf ) const -{ - int res = base->getAttr( stbuf ); - - if(res == 0 && S_ISREG(stbuf->st_mode)) - { - // have to adjust size field.. - int headerSize = macBytes + randBytes; - int bs = blockSize() + headerSize; - stbuf->st_size = locWithoutHeader( stbuf->st_size, bs, headerSize ); - } - - return res; -} - -off_t MACFileIO::getSize() const -{ - // adjust the size to hide the header overhead we tack on.. - int headerSize = macBytes + randBytes; - int bs = blockSize() + headerSize; - - off_t size = base->getSize(); - if(size > 0) - size = locWithoutHeader( size, bs, headerSize ); - - return size; -} - -ssize_t MACFileIO::readOneBlock( const IORequest &req ) const -{ - int headerSize = macBytes + randBytes; - - int bs = blockSize() + headerSize; - - MemBlock mb = MemoryPool::allocate( bs ); - - IORequest tmp; - tmp.offset = locWithHeader( req.offset, bs, headerSize ); - tmp.data = mb.data; - tmp.dataLen = headerSize + req.dataLen; - - // get the data from the base FileIO layer - ssize_t readSize = base->read( tmp ); - - // don't store zeros if configured for zero-block pass-through - bool skipBlock = true; - if( _allowHoles ) - { - for(int i=0; i 0) - skipBlock = false; - - if(readSize > headerSize) - { - if(!skipBlock) - { - // At this point the data has been decoded. So, compute the MAC of - // the block and check against the checksum stored in the header.. - uint64_t mac = cipher->MAC_64( tmp.data + macBytes, - readSize - macBytes, key ); - - for(int i=0; i>= 8) - { - int test = mac & 0xff; - int stored = tmp.data[i]; - if(test != stored) - { - // uh oh.. - long blockNum = req.offset / bs; - rWarning(_("MAC comparison failure in block %li"), - blockNum); - if( !warnOnly ) - { - MemoryPool::release( mb ); - throw ERROR( - _("MAC comparison failure, refusing to read")); - } - break; - } - } - } - - // now copy the data to the output buffer - readSize -= headerSize; - memcpy( req.data, tmp.data + headerSize, readSize ); - } else - { - rDebug("readSize %i at offset %" PRIi64, (int)readSize, req.offset); - if(readSize > 0) - readSize = 0; - } - - MemoryPool::release( mb ); - - return readSize; -} - -bool MACFileIO::writeOneBlock( const IORequest &req ) -{ - int headerSize = macBytes + randBytes; - - int bs = blockSize() + headerSize; - - // we have the unencrypted data, so we need to attach a header to it. - MemBlock mb = MemoryPool::allocate( bs ); - - IORequest newReq; - newReq.offset = locWithHeader( req.offset, bs, headerSize ); - newReq.data = mb.data; - newReq.dataLen = headerSize + req.dataLen; - - memset( newReq.data, 0, headerSize ); - memcpy( newReq.data + headerSize, req.data, req.dataLen ); - if(randBytes > 0) - { - if(!cipher->randomize( newReq.data+macBytes, randBytes, false )) - return false; - } - - if(macBytes > 0) - { - // compute the mac (which includes the random data) and fill it in - uint64_t mac = cipher->MAC_64( newReq.data+macBytes, - req.dataLen + randBytes, key ); - - for(int i=0; i>= 8; - } - } - - // now, we can let the next level have it.. - bool ok = base->write( newReq ); - - MemoryPool::release( mb ); - - return ok; -} - -int MACFileIO::truncate( off_t size ) -{ - int headerSize = macBytes + randBytes; - int bs = blockSize() + headerSize; - - int res = BlockFileIO::truncate( size, 0 ); - - if(res == 0) - base->truncate( locWithHeader( size, bs, headerSize ) ); - - return res; -} - -bool MACFileIO::isWritable() const -{ - return base->isWritable(); -} diff --git a/encfs/Makefile.am b/encfs/Makefile.am deleted file mode 100644 index c2cd157..0000000 --- a/encfs/Makefile.am +++ /dev/null @@ -1,163 +0,0 @@ - -include $(top_srcdir)/Makefile.common - -ALL_INCLUDES = @RLOG_CFLAGS@ @OPENSSL_CFLAGS@ -ALL_INCLUDES += @PROTOBUF_CFLAGS@ -ALL_LDFLAGS = @RLOG_LIBS@ @OPENSSL_LIBS@ -ALL_LDFLAGS += @PROTOBUF_LIBS@ - -INCLUDES = $(all_includes) -I../intl - -AM_CXXFLAGS = -DRLOG_COMPONENT="encfs" $(ALL_INCLUDES) - -if DARWIN -# needed to select correct API in fuse.h -AM_CXXFLAGS += -D__FreeBSD__=10 -endif - -if BUILD_NLS -# define a C macro LOCALEDIR indicating where catalogs will be installed -#localedir = $(datadir)/locale - -AM_CXXFLAGS += -DLOCALEDIR=\"$(localedir)\" -ALL_LDFLAGS += @LIBINTL@ -endif - -lib_LTLIBRARIES = libencfs.la -bin_PROGRAMS = encfs encfsctl -dist_bin_SCRIPTS = encfssh -noinst_PROGRAMS = test makeKey - -all-local: encfs-man.html - -encfs_LDADD = libencfs.la $(ALL_LDFLAGS) -encfsctl_LDADD = libencfs.la $(ALL_LDFLAGS) -test_LDADD = libencfs.la $(ALL_LDFLAGS) -makeKey_LDADD = libencfs.la $(ALL_LDFLAGS) - -if BUILD_STATIC -encfs_LDFLAGS = -all-static -encfsctl_LDFLAGS = -all-static -test_LDFLAGS = -all-static -makeKey_LDFLAGS = -all-static -endif - -# CURRENT : REVISION : AGE -# +1 : 0 : +1 => new interface that does not break old one -# +1 : 0 : 0 => new interface that breaks old one -# : : 0 => no new interfaces, but breaks old apps -# : +1 : => internal changes, nothing breaks -# -libencfs_la_LDFLAGS = -version-info 7:0:0 -libencfs_la_LIBADD = @RLOG_LIBS@ @OPENSSL_LIBS@ - -EXTRASRC = ../intl/autosprintf.cpp -if BUILD_OPENSSL -if BUILD_SSLCIPHER -EXTRASRC += SSL_Cipher.cpp -endif -endif - -libencfs_la_SOURCES = \ - readpassphrase.cpp \ - base64.cpp \ - config.pb.cc \ - ConfigReader.cpp \ - ConfigVar.cpp \ - Context.cpp \ - Cipher.cpp \ - CipherKey.cpp \ - FileIO.cpp \ - RawFileIO.cpp \ - BlockFileIO.cpp \ - CipherFileIO.cpp \ - MACFileIO.cpp \ - NameIO.cpp \ - StreamNameIO.cpp \ - BlockNameIO.cpp \ - NullNameIO.cpp \ - Interface.cpp \ - MemoryPool.cpp \ - NullCipher.cpp \ - DirNode.cpp \ - FileNode.cpp \ - FileUtils.cpp \ - openssl.cpp \ - XmlReader.cpp \ - ${EXTRASRC} - - -encfs_SOURCES = \ - encfs.cpp \ - main.cpp - -test_SOURCES = \ - test.cpp - -makeKey_SOURCES = \ - makeKey.cpp - -encfsctl_SOURCES = \ - encfsctl.cpp - -noinst_HEADERS = \ - base64.h \ - BlockFileIO.h \ - BlockNameIO.h \ - CipherFileIO.h \ - Cipher.h \ - CipherKey.h \ - ConfigReader.h \ - ConfigVar.h \ - config.pb.h \ - Context.h \ - DirNode.h \ - encfs.h \ - FileIO.h \ - FileNode.h \ - FileUtils.h \ - FSConfig.h \ - Interface.h \ - i18n.h \ - MACFileIO.h \ - MemoryPool.h \ - Mutex.h \ - NameIO.h \ - NullCipher.h \ - NullNameIO.h \ - openssl.h \ - Range.h \ - RawFileIO.h \ - readpassphrase.h \ - SSL_Cipher.h \ - StreamNameIO.h - -man_MANS=encfs.1 encfsctl.1 -EXTRA_DIST = encfs.pod encfsctl.pod encfs.1 encfsctl.1 encfs-man.html - -SUFFIXES = .1 .pod .proto - -config.pb.cc: config.pb.h - -config.pb.h: config.proto - @PROTOC@ --cpp_out=. config.proto - -if BUILD_MAN -# since we have POD2MAN, we can specify how to rebuild encfs.1 if necessary -.pod.1: - @POD2MAN@ --section=1 --release=@VERSION@ --center="Encrypted Filesystem" $< $@ - -CLEANFILES = encfs.1 encfsctl.1 -endif - -if BUILD_MANHTML -encfs-man.html: encfs.pod - @POD2HTML@ encfs.pod > $@ -endif - -tests: - perl -MTest::Harness -e '$$Test::Harness::verbose=0; runtests @ARGV;' *.t - -tests-verbose: - perl -MTest::Harness -e '$$Test::Harness::verbose=1; runtests @ARGV;' *.t - diff --git a/encfs/MemoryPool.cpp b/encfs/MemoryPool.cpp deleted file mode 100644 index 4d983b0..0000000 --- a/encfs/MemoryPool.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2003, Valient Gough - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "MemoryPool.h" -#include - -#include -#include - -#include "config.h" -#include - -#include - -#ifdef HAVE_VALGRIND_MEMCHECK_H -#include -#else -#define VALGRIND_MAKE_MEM_NOACCESS( a, b ) -#define VALGRIND_MAKE_MEM_UNDEFINED( a, b ) -#endif - -using namespace rlog; - -# include -# include -#define BLOCKDATA( BLOCK ) (unsigned char*)BLOCK->data->data - - -struct BlockList -{ - BlockList *next; - int size; - BUF_MEM *data; -}; - -static BlockList *allocBlock( int size ) -{ - BlockList *block = new BlockList; - block->size = size; - block->data = BUF_MEM_new( ); - BUF_MEM_grow( block->data, size ); - VALGRIND_MAKE_MEM_NOACCESS( block->data->data, block->data->max ); - - return block; -} - -static void freeBlock( BlockList *el ) -{ - VALGRIND_MAKE_MEM_UNDEFINED( el->data->data, el->data->max ); - BUF_MEM_free( el->data ); - - delete el; -} - -static pthread_mutex_t gMPoolMutex = PTHREAD_MUTEX_INITIALIZER; -static BlockList *gMemPool = NULL; - - - -MemBlock MemoryPool::allocate( int size ) -{ - pthread_mutex_lock( &gMPoolMutex ); - - BlockList *parent = NULL; - BlockList *block = gMemPool; - // check if we already have a large enough block available.. - while(block != NULL && block->size < size) - { - parent = block; - block = block->next; - } - - // unlink block from list - if(block) - { - if(!parent) - gMemPool = block->next; - else - parent->next = block->next; - } - pthread_mutex_unlock( &gMPoolMutex ); - - if(!block) - block = allocBlock( size ); - block->next = NULL; - - MemBlock result; - result.data = BLOCKDATA(block); - result.internalData = block; - - VALGRIND_MAKE_MEM_UNDEFINED( result.data, size ); - - return result; -} - -void MemoryPool::release( const MemBlock &mb ) -{ - pthread_mutex_lock( &gMPoolMutex ); - - BlockList *block = (BlockList*)mb.internalData; - - // just to be sure there's nothing important left in buffers.. - VALGRIND_MAKE_MEM_UNDEFINED( block->data->data, block->size ); - memset( BLOCKDATA(block) , 0, block->size); - VALGRIND_MAKE_MEM_NOACCESS( block->data->data, block->data->max ); - - block->next = gMemPool; - gMemPool = block; - - pthread_mutex_unlock( &gMPoolMutex ); -} - -void MemoryPool::destroyAll() -{ - pthread_mutex_lock( &gMPoolMutex ); - - BlockList *block = gMemPool; - gMemPool = NULL; - - pthread_mutex_unlock( &gMPoolMutex ); - - while(block != NULL) - { - BlockList *next = block->next; - - freeBlock( block ); - block = next; - } -} - -SecureMem::SecureMem(int len) -{ - data = (char *)OPENSSL_malloc(len); - if (data) - { - size = len; - mlock(data, size); - memset(data, '\0', size); - VALGRIND_MAKE_MEM_UNDEFINED( data, size ); - } else - { - size = 0; - } -} - -SecureMem::~SecureMem() -{ - if (size) - { - memset(data, '\0', size); - munlock(data, size); - - OPENSSL_free(data); - VALGRIND_MAKE_MEM_NOACCESS( data, size ); - - data = NULL; - size = 0; - } -} - - diff --git a/encfs/NameIO.cpp b/encfs/NameIO.cpp deleted file mode 100644 index b9705fc..0000000 --- a/encfs/NameIO.cpp +++ /dev/null @@ -1,351 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2004, Valient Gough - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "NameIO.h" -#include "config.h" - -#include -#include - -#include -#include - -// for static build. Need to reference the modules which are registered at -// run-time, to ensure that the linker doesn't optimize them away. -#include -#include "BlockNameIO.h" -#include "StreamNameIO.h" -#include "NullNameIO.h" - -using namespace std; -using namespace rlog; - -#define REF_MODULE(TYPE) \ - if(!TYPE::Enabled() ) \ - cerr << "referenceModule: should never happen\n"; - -static -void AddSymbolReferences() -{ - REF_MODULE(BlockNameIO) - REF_MODULE(StreamNameIO) - REF_MODULE(NullNameIO) -} - - -struct NameIOAlg -{ - bool hidden; - NameIO::Constructor constructor; - string description; - Interface iface; -}; - -typedef multimap< string, NameIOAlg > NameIOMap_t; -static NameIOMap_t *gNameIOMap = 0; - - -list< NameIO::Algorithm > -NameIO::GetAlgorithmList( bool includeHidden ) -{ - AddSymbolReferences(); - - list< Algorithm > result; - if(gNameIOMap) - { - NameIOMap_t::const_iterator it; - NameIOMap_t::const_iterator end = gNameIOMap->end(); - for(it = gNameIOMap->begin(); it != end; ++it) - { - if(includeHidden || !it->second.hidden) - { - Algorithm tmp; - tmp.name = it->first; - tmp.description = it->second.description; - tmp.iface = it->second.iface; - - result.push_back( tmp ); - } - } - } - - return result; -} - -bool NameIO::Register( const char *name, const char *description, - const Interface &iface, Constructor constructor, - bool hidden ) -{ - if( !gNameIOMap ) - gNameIOMap = new NameIOMap_t; - - NameIOAlg alg; - alg.hidden = hidden; - alg.constructor = constructor; - alg.description = description; - alg.iface = iface; - - gNameIOMap->insert( make_pair( string(name), alg )); - return true; -} - -shared_ptr NameIO::New( const string &name, - const shared_ptr &cipher, const CipherKey &key) -{ - shared_ptr result; - if(gNameIOMap) - { - NameIOMap_t::const_iterator it = gNameIOMap->find( name ); - if(it != gNameIOMap->end()) - { - Constructor fn = it->second.constructor; - result = (*fn)( it->second.iface, cipher, key ); - } - } - return result; -} - -shared_ptr NameIO::New( const Interface &iface, - const shared_ptr &cipher, const CipherKey &key ) -{ - shared_ptr result; - if(gNameIOMap) - { - NameIOMap_t::const_iterator it; - NameIOMap_t::const_iterator end = gNameIOMap->end(); - for(it = gNameIOMap->begin(); it != end; ++it) - { - if( implements(it->second.iface, iface )) - { - Constructor fn = it->second.constructor; - result = (*fn)( iface, cipher, key ); - break; - } - } - } - return result; -} - - - -NameIO::NameIO() - : chainedNameIV( false ), reverseEncryption( false ) -{ -} - -NameIO::~NameIO() -{ -} - -void NameIO::setChainedNameIV( bool enable ) -{ - chainedNameIV = enable; -} - -bool NameIO::getChainedNameIV() const -{ - return chainedNameIV; -} - -void NameIO::setReverseEncryption( bool enable ) -{ - reverseEncryption = enable; -} - -bool NameIO::getReverseEncryption() const -{ - return reverseEncryption; -} - - -std::string NameIO::recodePath( const char *path, - int (NameIO::*_length)(int) const, - int (NameIO::*_code)(const char*, int, uint64_t *, char*) const, - uint64_t *iv ) const -{ - string output; - - while( *path ) - { - if( *path == '/' ) - { - if( !output.empty() ) // don't start the string with '/' - output += '/'; - ++path; - } else - { - bool isDotFile = (*path == '.'); - const char *next = strchr( path, '/' ); - int len = next ? next - path : strlen( path ); - - // at this point we know that len > 0 - if( isDotFile && (path[len-1] == '.') && (len <= 2) ) - { - output.append(len, '.'); // append [len] copies of '.' - path += len; - continue; - } - - // figure out buffer sizes - int approxLen = (this->*_length)( len ); - if(approxLen <= 0) - throw ERROR("Filename too small to decode"); - - BUFFER_INIT( codeBuf, 32, (unsigned int)approxLen+1 ) - - // code the name - int codedLen = (this->*_code)( path, len, iv, codeBuf ); - rAssert( codedLen <= approxLen ); - rAssert( codeBuf[codedLen] == '\0' ); - path += len; - - // append result to string - output += (char*)codeBuf; - - BUFFER_RESET( codeBuf ) - } - } - - return output; -} - -std::string NameIO::encodePath( const char *plaintextPath ) const -{ - uint64_t iv = 0; - return encodePath( plaintextPath, &iv); -} - -std::string NameIO::decodePath( const char *cipherPath ) const -{ - uint64_t iv = 0; - return decodePath( cipherPath, &iv ); -} - -std::string NameIO::_encodePath( const char *plaintextPath, uint64_t *iv ) const -{ - // if chaining is not enabled, then the iv pointer is not used.. - if(!chainedNameIV) - iv = 0; - return recodePath( plaintextPath, - &NameIO::maxEncodedNameLen, &NameIO::encodeName, iv); -} - -std::string NameIO::_decodePath( const char *cipherPath, uint64_t *iv ) const -{ - // if chaining is not enabled, then the iv pointer is not used.. - if(!chainedNameIV) - iv = 0; - return recodePath( cipherPath, - &NameIO::maxDecodedNameLen, &NameIO::decodeName, iv); -} - -std::string NameIO::encodePath( const char *path, uint64_t *iv ) const -{ - return getReverseEncryption() ? - _decodePath( path, iv ) : - _encodePath( path, iv ); -} - -std::string NameIO::decodePath( const char *path, uint64_t *iv ) const -{ - return getReverseEncryption() ? - _encodePath( path, iv ) : - _decodePath( path, iv ); -} - - -int NameIO::encodeName( const char *input, int length, char *output ) const -{ - return encodeName( input, length, (uint64_t*)0, output ); -} - -int NameIO::decodeName( const char *input, int length, char *output ) const -{ - return decodeName( input, length, (uint64_t*)0, output ); -} - -std::string NameIO::_encodeName( const char *plaintextName, int length ) const -{ - int approxLen = maxEncodedNameLen( length ); - - BUFFER_INIT( codeBuf, 32, (unsigned int)approxLen+1 ) - - // code the name - int codedLen = encodeName( plaintextName, length, 0, codeBuf ); - rAssert( codedLen <= approxLen ); - rAssert( codeBuf[codedLen] == '\0' ); - - // append result to string - std::string result = (char*)codeBuf; - - BUFFER_RESET( codeBuf ) - - return result; -} - -std::string NameIO::_decodeName( const char *encodedName, int length ) const -{ - int approxLen = maxDecodedNameLen( length ); - - BUFFER_INIT( codeBuf, 32, (unsigned int)approxLen+1 ) - - // code the name - int codedLen = decodeName( encodedName, length, 0, codeBuf ); - rAssert( codedLen <= approxLen ); - rAssert( codeBuf[codedLen] == '\0' ); - - // append result to string - std::string result = (char*)codeBuf; - - BUFFER_RESET( codeBuf ) - - return result; -} - -std::string NameIO::encodeName( const char *path, int length ) const -{ - return getReverseEncryption() ? - _decodeName( path, length ) : - _encodeName( path, length ); -} - -std::string NameIO::decodeName( const char *path, int length ) const -{ - return getReverseEncryption() ? - _encodeName( path, length ) : - _decodeName( path, length ); -} -/* -int NameIO::encodeName( const char *path, int length, - char *output ) const -{ - return getReverseEncryption() ? - _decodeName( path, length, output ) : - _encodeName( path, length, output ); -} - -int NameIO::decodeName( const char *path, int length, - char *output ) const -{ - return getReverseEncryption() ? - _encodeName( path, length, output ) : - _decodeName( path, length, output ); -} -*/ diff --git a/encfs/RawFileIO.cpp b/encfs/RawFileIO.cpp deleted file mode 100644 index cb7515f..0000000 --- a/encfs/RawFileIO.cpp +++ /dev/null @@ -1,325 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2004, Valient Gough - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#ifdef linux -#define _XOPEN_SOURCE 500 // pick up pread , pwrite -#endif -#include - -#include "RawFileIO.h" - -#include - -#include -#include -#include -#include - -#include - -using namespace std; - -static Interface RawFileIO_iface = makeInterface("FileIO/Raw", 1, 0, 0); - -FileIO *NewRawFileIO( const Interface &iface ) -{ - (void)iface; - return new RawFileIO(); -} - -inline void swap( int &x, int &y ) -{ - int tmp = x; - x = y; - y = tmp; -} - -RawFileIO::RawFileIO( ) - : knownSize( false ) - , fileSize(0) - , fd( -1 ) - , oldfd( -1 ) - , canWrite( false ) -{ -} - -RawFileIO::RawFileIO( const std::string &fileName ) - : name( fileName ) - , knownSize( false ) - , fileSize( 0 ) - , fd( -1 ) - , oldfd( -1 ) - , canWrite( false ) -{ -} - -RawFileIO::~RawFileIO() -{ - int _fd = -1; - int _oldfd = -1; - - swap( _fd, fd ); - swap( _oldfd, oldfd ); - - if( _oldfd != -1 ) - close( _oldfd ); - - if( _fd != -1 ) - close( _fd ); -} - -Interface RawFileIO::interface() const -{ - return RawFileIO_iface; -} - -/* - Workaround for opening a file for write when permissions don't allow. - Since the kernel has already checked permissions, we can assume it is ok to - provide access. So force it by changing permissions temporarily. Should - be called with a lock around it so that there won't be a race condition - with calls to lstat picking up the wrong permissions. -*/ -static int open_readonly_workaround(const char *path, int flags) -{ - int fd = -1; - struct stat stbuf; - memset(&stbuf, 0, sizeof(struct stat)); - if(lstat( path, &stbuf ) != -1) - { - // make sure user has read/write permission.. - chmod( path , stbuf.st_mode | 0600 ); - fd = ::open( path , flags ); - chmod( path , stbuf.st_mode ); - } else - { - rInfo("can't stat file %s", path ); - } - - return fd; -} - -/* - We shouldn't have to support all possible open flags, so untaint the flags - argument by only taking ones we understand and accept. - - Since the kernel has already done permission tests before calling us, we - shouldn't have to worry about access control. - - Basically we just need to distinguish between read and write flags - - Also keep the O_LARGEFILE flag, in case the underlying filesystem needs - it.. -*/ -int RawFileIO::open(int flags) -{ - bool requestWrite = ((flags & O_RDWR) || (flags & O_WRONLY)); - - rDebug("open call for %s file", requestWrite ? "writable" : "read only"); - - int result = 0; - - // if we have a descriptor and it is writable, or we don't need writable.. - if((fd >= 0) && (canWrite || !requestWrite)) - { - rDebug("using existing file descriptor"); - result = fd; // success - } else - { - int finalFlags = requestWrite ? O_RDWR : O_RDONLY; - -#if defined(O_LARGEFILE) - if( flags & O_LARGEFILE ) - finalFlags |= O_LARGEFILE; -#else -#warning O_LARGEFILE not supported -#endif - - int newFd = ::open( name.c_str(), finalFlags ); - - rDebug("open file with flags %i, result = %i", finalFlags, newFd); - - if((newFd == -1) && (errno == EACCES)) - { - rDebug("using readonly workaround for open"); - newFd = open_readonly_workaround( name.c_str(), finalFlags ); - } - - if(newFd >= 0) - { - if(oldfd >= 0) - { - rError("leaking FD?: oldfd = %i, fd = %i, newfd = %i", - oldfd, fd, newFd); - } - - // the old fd might still be in use, so just keep it around for - // now. - canWrite = requestWrite; - oldfd = fd; - result = fd = newFd; - } else - { - result = -errno; - rInfo("::open error: %s", strerror(errno)); - } - } - - if(result < 0) - rInfo("file %s open failure: %i", name.c_str(), -result); - - return result; -} - -int RawFileIO::getAttr( struct stat *stbuf ) const -{ - int res = lstat( name.c_str(), stbuf ); - int eno = errno; - - if(res < 0) - rInfo("getAttr error on %s: %s", name.c_str(), strerror( eno )); - - return ( res < 0 ) ? -eno : 0; -} - -void RawFileIO::setFileName( const char *fileName ) -{ - name = fileName; -} - -const char *RawFileIO::getFileName() const -{ - return name.c_str(); -} - -off_t RawFileIO::getSize() const -{ - if(!knownSize) - { - struct stat stbuf; - memset( &stbuf, 0, sizeof( struct stat )); - int res = lstat( name.c_str(), &stbuf ); - - if(res == 0) - { - const_cast(this)->fileSize = stbuf.st_size; - const_cast(this)->knownSize = true; - return fileSize; - } else - return -1; - } else - { - return fileSize; - } -} - -ssize_t RawFileIO::read( const IORequest &req ) const -{ - rAssert( fd >= 0 ); - - ssize_t readSize = pread( fd, req.data, req.dataLen, req.offset ); - - if(readSize < 0) - { - rInfo("read failed at offset %" PRIi64 " for %i bytes: %s", - req.offset, req.dataLen, strerror( errno )); - } - - return readSize; -} - -bool RawFileIO::write( const IORequest &req ) -{ - rAssert( fd >= 0 ); - rAssert( true == canWrite ); - - int retrys = 10; - void *buf = req.data; - ssize_t bytes = req.dataLen; - off_t offset = req.offset; - - while( bytes && retrys > 0 ) - { - ssize_t writeSize = ::pwrite( fd, buf, bytes, offset ); - - if( writeSize < 0 ) - { - knownSize = false; - rInfo("write failed at offset %" PRIi64 " for %i bytes: %s", - offset, (int)bytes, strerror( errno )); - return false; - } - - bytes -= writeSize; - offset += writeSize; - buf = (void*)((char*)buf + writeSize); - --retrys; - } - - if(bytes != 0) - { - rError("Write error: wrote %i bytes of %i, max retries reached\n", - (int)(req.dataLen - bytes), req.dataLen ); - knownSize = false; - return false; - } else - { - if(knownSize) - { - off_t last = req.offset + req.dataLen; - if(last > fileSize) - fileSize = last; - } - - return true; - } -} - -int RawFileIO::truncate( off_t size ) -{ - int res; - - if(fd >= 0 && canWrite) - { - res = ::ftruncate( fd, size ); -#ifndef __FreeBSD__ - ::fdatasync( fd ); -#endif - } else - res = ::truncate( name.c_str(), size ); - - if(res < 0) - { - int eno = errno; - rInfo("truncate failed for %s (%i) size %" PRIi64 ", error %s", - name.c_str(), fd, size, strerror(eno)); - res = -eno; - knownSize = false; - } else - { - res = 0; - fileSize = size; - knownSize = true; - } - - return res; -} - -bool RawFileIO::isWritable() const -{ - return canWrite; -} diff --git a/encfs/SSL_Cipher.cpp b/encfs/SSL_Cipher.cpp deleted file mode 100644 index 10f2d61..0000000 --- a/encfs/SSL_Cipher.cpp +++ /dev/null @@ -1,927 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2004, Valient Gough - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "encfs.h" - -#include "config.h" - -#include -#include -#include -#include -#include - -#include "SSL_Cipher.h" -#include "Range.h" -#include "MemoryPool.h" -#include "Mutex.h" - -#include -#include - -#include -#include - -#include -#include - -#include "i18n.h" - -using namespace std; -using namespace rel; -using namespace rlog; - -const int MAX_KEYLENGTH = 32; // in bytes (256 bit) -const int MAX_IVLENGTH = 16; -const int KEY_CHECKSUM_BYTES = 4; - -#ifndef MIN -inline int MIN(int a, int b) -{ - return (a < b) ? a : b; -} -#endif - -/* - This produces the same result as OpenSSL's EVP_BytesToKey. The difference - is that here we can explicitly specify the key size, instead of relying on - the state of EVP_CIPHER struct. EVP_BytesToKey will only produce 128 bit - keys for the EVP Blowfish interface, which is not what we want. - - DEPRECATED: this is here for backward compatibilty only. Use PBKDF -*/ -int BytesToKey( int keyLen, int ivLen, const EVP_MD *md, - const unsigned char *data, int dataLen, - unsigned int rounds, unsigned char *key, unsigned char *iv) -{ - if( data == NULL || dataLen == 0 ) - return 0; // OpenSSL returns nkey here, but why? It is a failure.. - - unsigned char mdBuf[ EVP_MAX_MD_SIZE ]; - unsigned int mds=0; - int addmd =0; - int nkey = key ? keyLen : 0; - int niv = iv ? ivLen : 0; - - EVP_MD_CTX cx; - EVP_MD_CTX_init( &cx ); - - for(;;) - { - EVP_DigestInit_ex( &cx, md, NULL ); - if( addmd++ ) - EVP_DigestUpdate( &cx, mdBuf, mds ); - EVP_DigestUpdate( &cx, data, dataLen ); - EVP_DigestFinal_ex( &cx, mdBuf, &mds ); - - for(unsigned int i=1; i < rounds; ++i) - { - EVP_DigestInit_ex( &cx, md, NULL ); - EVP_DigestUpdate( &cx, mdBuf, mds ); - EVP_DigestFinal_ex( &cx, mdBuf, &mds ); - } - - int offset = 0; - int toCopy = MIN( nkey, (int)mds - offset ); - if( toCopy ) - { - memcpy( key, mdBuf+offset, toCopy ); - key += toCopy; - nkey -= toCopy; - offset += toCopy; - } - toCopy = MIN( niv, (int)mds - offset ); - if( toCopy ) - { - memcpy( iv, mdBuf+offset, toCopy ); - iv += toCopy; - niv -= toCopy; - offset += toCopy; - } - if((nkey == 0) && (niv == 0)) break; - } - EVP_MD_CTX_cleanup( &cx ); - OPENSSL_cleanse( mdBuf, sizeof(mdBuf) ); - - return keyLen; -} - -long time_diff(const timeval &end, const timeval &start) -{ - return (end.tv_sec - start.tv_sec) * 1000 * 1000 + - (end.tv_usec - start.tv_usec); -} - -int TimedPBKDF2(const char *pass, int passlen, - const unsigned char *salt, int saltlen, - int keylen, unsigned char *out, - long desiredPDFTime) -{ - int iter = 1000; - timeval start, end; - - for(;;) - { - gettimeofday( &start, 0 ); - int res = PKCS5_PBKDF2_HMAC_SHA1( - pass, passlen, const_cast(salt), saltlen, - iter, keylen, out); - if(res != 1) - return -1; - - gettimeofday( &end, 0 ); - - long delta = time_diff(end, start); - if(delta < desiredPDFTime / 8) - { - iter *= 4; - } else if(delta < (5 * desiredPDFTime / 6)) - { - // estimate number of iterations to get close to desired time - iter = (int)((double)iter * (double)desiredPDFTime - / (double)delta); - } else - return iter; - } -} - - -// - Version 1:0 used EVP_BytesToKey, which didn't do the right thing for -// Blowfish key lengths > 128 bit. -// - Version 2:0 uses BytesToKey. -// We support both 2:0 and 1:0, hence current:revision:age = 2:0:1 -// - Version 2:1 adds support for Message Digest function interface -// - Version 2:2 adds PBKDF2 for password derivation -// - Version 3:0 adds a new IV mechanism -static Interface BlowfishInterface = makeInterface( "ssl/blowfish", 3, 0, 2 ); -static Interface AESInterface = makeInterface( "ssl/aes", 3, 0, 2 ); - -#if defined(HAVE_EVP_BF) - -static Range BFKeyRange(128,256,32); -static Range BFBlockRange(64,4096,8); - -static shared_ptr NewBFCipher( const Interface &iface, int keyLen ) -{ - if( keyLen <= 0 ) - keyLen = 160; - - keyLen = BFKeyRange.closest( keyLen ); - - const EVP_CIPHER *blockCipher = EVP_bf_cbc(); - const EVP_CIPHER *streamCipher = EVP_bf_cfb(); - - return shared_ptr( new SSL_Cipher(iface, BlowfishInterface, - blockCipher, streamCipher, keyLen / 8) ); -} - -static bool BF_Cipher_registered = Cipher::Register("Blowfish", - // xgroup(setup) - gettext_noop("8 byte block cipher"), - BlowfishInterface, BFKeyRange, BFBlockRange, NewBFCipher); -#endif - - -#if defined(HAVE_EVP_AES) - -static Range AESKeyRange(128,256,64); -static Range AESBlockRange(64,4096,16); - -static shared_ptr NewAESCipher( const Interface &iface, int keyLen ) -{ - if( keyLen <= 0 ) - keyLen = 192; - - keyLen = AESKeyRange.closest( keyLen ); - - const EVP_CIPHER *blockCipher = 0; - const EVP_CIPHER *streamCipher = 0; - - switch(keyLen) - { - case 128: - blockCipher = EVP_aes_128_cbc(); - streamCipher = EVP_aes_128_cfb(); - break; - - case 192: - blockCipher = EVP_aes_192_cbc(); - streamCipher = EVP_aes_192_cfb(); - break; - - case 256: - default: - blockCipher = EVP_aes_256_cbc(); - streamCipher = EVP_aes_256_cfb(); - break; - } - - return shared_ptr( new SSL_Cipher(iface, AESInterface, - blockCipher, streamCipher, keyLen / 8) ); -} - -static bool AES_Cipher_registered = Cipher::Register("AES", - "16 byte block cipher", - AESInterface, AESKeyRange, AESBlockRange, NewAESCipher); -#endif - -class SSLKey : public AbstractCipherKey -{ -public: - pthread_mutex_t mutex; - - unsigned int keySize; // in bytes - unsigned int ivLength; - - // key data is first _keySize bytes, - // followed by iv of _ivLength bytes, - unsigned char *buffer; - - EVP_CIPHER_CTX block_enc; - EVP_CIPHER_CTX block_dec; - EVP_CIPHER_CTX stream_enc; - EVP_CIPHER_CTX stream_dec; - - HMAC_CTX mac_ctx; - - SSLKey(int keySize, int ivLength); - ~SSLKey(); -}; - -SSLKey::SSLKey(int keySize_, int ivLength_) -{ - this->keySize = keySize_; - this->ivLength = ivLength_; - pthread_mutex_init( &mutex, 0 ); - buffer = (unsigned char *)OPENSSL_malloc( keySize + ivLength ); - - // most likely fails unless we're running as root, or a user-page-lock - // kernel patch is applied.. - mlock( buffer, keySize + ivLength ); - - memset( buffer, 0, keySize + ivLength ); -} - -SSLKey::~SSLKey() -{ - memset( buffer, 0, keySize + ivLength ); - - munlock( buffer, keySize + ivLength ); - OPENSSL_free( buffer ); - - keySize = 0; - ivLength = 0; - buffer = 0; - - EVP_CIPHER_CTX_cleanup( &block_enc ); - EVP_CIPHER_CTX_cleanup( &block_dec ); - EVP_CIPHER_CTX_cleanup( &stream_enc ); - EVP_CIPHER_CTX_cleanup( &stream_dec ); - - HMAC_CTX_cleanup( &mac_ctx ); - - pthread_mutex_destroy( &mutex ); -} - - -inline unsigned char* KeyData( const shared_ptr &key ) -{ - return key->buffer; -} -inline unsigned char* IVData( const shared_ptr &key ) -{ - return key->buffer + key->keySize; -} - -void initKey(const shared_ptr &key, const EVP_CIPHER *_blockCipher, - const EVP_CIPHER *_streamCipher, int _keySize) -{ - Lock lock( key->mutex ); - // initialize the cipher context once so that we don't have to do it for - // every block.. - EVP_CIPHER_CTX_init( &key->block_enc ); - EVP_CIPHER_CTX_init( &key->block_dec ); - EVP_CIPHER_CTX_init( &key->stream_enc ); - EVP_CIPHER_CTX_init( &key->stream_dec ); - - EVP_EncryptInit_ex( &key->block_enc, _blockCipher, NULL, NULL, NULL); - EVP_DecryptInit_ex( &key->block_dec, _blockCipher, NULL, NULL, NULL); - EVP_EncryptInit_ex( &key->stream_enc, _streamCipher, NULL, NULL, NULL); - EVP_DecryptInit_ex( &key->stream_dec, _streamCipher, NULL, NULL, NULL); - - EVP_CIPHER_CTX_set_key_length( &key->block_enc, _keySize ); - EVP_CIPHER_CTX_set_key_length( &key->block_dec, _keySize ); - EVP_CIPHER_CTX_set_key_length( &key->stream_enc, _keySize ); - EVP_CIPHER_CTX_set_key_length( &key->stream_dec, _keySize ); - - EVP_CIPHER_CTX_set_padding( &key->block_enc, 0 ); - EVP_CIPHER_CTX_set_padding( &key->block_dec, 0 ); - EVP_CIPHER_CTX_set_padding( &key->stream_enc, 0 ); - EVP_CIPHER_CTX_set_padding( &key->stream_dec, 0 ); - - EVP_EncryptInit_ex( &key->block_enc, NULL, NULL, KeyData(key), NULL); - EVP_DecryptInit_ex( &key->block_dec, NULL, NULL, KeyData(key), NULL); - EVP_EncryptInit_ex( &key->stream_enc, NULL, NULL, KeyData(key), NULL); - EVP_DecryptInit_ex( &key->stream_dec, NULL, NULL, KeyData(key), NULL); - - HMAC_CTX_init( &key->mac_ctx ); - HMAC_Init_ex( &key->mac_ctx, KeyData(key), _keySize, EVP_sha1(), 0 ); -} - - -static RLogChannel * CipherInfo = DEF_CHANNEL( "info/cipher", Log_Info ); - -SSL_Cipher::SSL_Cipher(const Interface &iface_, - const Interface &realIface_, - const EVP_CIPHER *blockCipher, - const EVP_CIPHER *streamCipher, - int keySize_) -{ - this->iface = iface_; - this->realIface = realIface_; - this->_blockCipher = blockCipher; - this->_streamCipher = streamCipher; - this->_keySize = keySize_; - this->_ivLength = EVP_CIPHER_iv_length( _blockCipher ); - - rAssert(_ivLength == 8 || _ivLength == 16); - - rLog(CipherInfo, "allocated cipher %s, keySize %i, ivlength %i", - iface.name().c_str(), _keySize, _ivLength); - - if( (EVP_CIPHER_key_length( _blockCipher ) != (int )_keySize) - && iface.major() == 1) - { - rWarning("Running in backward compatibilty mode for 1.0 - \n" - "key is really %i bits, not %i.\n" - "Consider creating a new filesystem and moving your data.", - EVP_CIPHER_key_length( _blockCipher ) * 8, - _keySize * 8 ); - } -} - -SSL_Cipher::~SSL_Cipher() -{ -} - -Interface SSL_Cipher::interface() const -{ - return realIface; -} - -/* - create a key from the password. - Use SHA to distribute entropy from the password into the key. - - This algorithm must remain constant for backward compatibility, as this key - is used to encipher/decipher the master key. -*/ -CipherKey SSL_Cipher::newKey(const char *password, int passwdLength, - int &iterationCount, long desiredDuration, - const unsigned char *salt, int saltLen) -{ - shared_ptr key( new SSLKey( _keySize, _ivLength) ); - - if(iterationCount == 0) - { - // timed run, fills in iteration count - int res = TimedPBKDF2(password, passwdLength, - salt, saltLen, - _keySize+_ivLength, KeyData(key), - 1000 * desiredDuration); - if(res <= 0) - { - rWarning("openssl error, PBKDF2 failed"); - return CipherKey(); - } else - iterationCount = res; - } else - { - // known iteration length - if(PKCS5_PBKDF2_HMAC_SHA1( - password, passwdLength, - const_cast(salt), saltLen, - iterationCount, _keySize + _ivLength, KeyData(key)) != 1) - { - rWarning("openssl error, PBKDF2 failed"); - return CipherKey(); - } - } - - initKey( key, _blockCipher, _streamCipher, _keySize ); - - return key; -} - -CipherKey SSL_Cipher::newKey(const char *password, int passwdLength) -{ - shared_ptr key( new SSLKey( _keySize, _ivLength) ); - - int bytes = 0; - if( iface.major() > 1 ) - { - // now we use BytesToKey, which can deal with Blowfish keys larger then - // 128 bits. - bytes = BytesToKey( _keySize, _ivLength, EVP_sha1(), - (unsigned char *)password, passwdLength, 16, - KeyData(key), IVData(key) ); - - // the reason for moving from EVP_BytesToKey to BytesToKey function.. - if(bytes != (int)_keySize) - { - rWarning("newKey: BytesToKey returned %i, expecting %i key bytes", - bytes, _keySize); - } - } else - { - // for backward compatibility with filesystems created with 1:0 - bytes = EVP_BytesToKey( _blockCipher, EVP_sha1(), NULL, - (unsigned char *)password, passwdLength, 16, - KeyData(key), IVData(key) ); - } - - initKey( key, _blockCipher, _streamCipher, _keySize ); - - return key; -} - -/* - Create a random key. - We use the OpenSSL library to generate random bytes, then take the hash of - those bytes to use as the key. - - This algorithm can change at any time without affecting backward - compatibility. -*/ -CipherKey SSL_Cipher::newRandomKey() -{ - const int bufLen = MAX_KEYLENGTH; - unsigned char tmpBuf[ bufLen ]; - int saltLen = 20; - unsigned char saltBuf[ saltLen ]; - - if(!randomize(tmpBuf, bufLen, true) || - !randomize(saltBuf, saltLen, true)) - return CipherKey(); - - shared_ptr key( new SSLKey( _keySize, _ivLength) ); - - // doesn't need to be versioned, because a random key is a random key.. - // Doesn't need to be reproducable.. - if(PKCS5_PBKDF2_HMAC_SHA1((char*)tmpBuf, bufLen, saltBuf, saltLen, - 1000, _keySize + _ivLength, KeyData(key)) != 1) - { - rWarning("openssl error, PBKDF2 failed"); - return CipherKey(); - } - - OPENSSL_cleanse(tmpBuf, bufLen); - - initKey( key, _blockCipher, _streamCipher, _keySize ); - - return key; -} - -/* - compute a 64-bit check value for the data using HMAC. -*/ -static uint64_t _checksum_64( SSLKey *key, - const unsigned char *data, int dataLen, uint64_t *chainedIV) -{ - rAssert( dataLen > 0 ); - Lock lock( key->mutex ); - - unsigned char md[EVP_MAX_MD_SIZE]; - unsigned int mdLen = EVP_MAX_MD_SIZE; - - HMAC_Init_ex( &key->mac_ctx, 0, 0, 0, 0 ); - HMAC_Update( &key->mac_ctx, data, dataLen ); - if(chainedIV) - { - // toss in the chained IV as well - uint64_t tmp = *chainedIV; - unsigned char h[8]; - for(unsigned int i=0; i<8; ++i) - { - h[i] = tmp & 0xff; - tmp >>= 8; - } - - HMAC_Update( &key->mac_ctx, h, 8 ); - } - - HMAC_Final( &key->mac_ctx, md, &mdLen ); - - rAssert(mdLen >= 8); - - // chop this down to a 64bit value.. - unsigned char h[8] = {0,0,0,0,0,0,0,0}; - for(unsigned int i=0; i<(mdLen-1); ++i) - h[i%8] ^= (unsigned char)(md[i]); - - uint64_t value = (uint64_t)h[0]; - for(int i=1; i<8; ++i) - value = (value << 8) | (uint64_t)h[i]; - - return value; -} - -bool SSL_Cipher::randomize( unsigned char *buf, int len, - bool strongRandom ) const -{ - // to avoid warnings of uninitialized data from valgrind - memset(buf, 0, len); - int result; - if(strongRandom) - result = RAND_bytes( buf, len ); - else - result = RAND_pseudo_bytes( buf, len ); - - if(result != 1) - { - char errStr[120]; // specs require string at least 120 bytes long.. - unsigned long errVal = 0; - if((errVal = ERR_get_error()) != 0) - rWarning("openssl error: %s", ERR_error_string( errVal, errStr )); - - return false; - } else - return true; -} - -uint64_t SSL_Cipher::MAC_64( const unsigned char *data, int len, - const CipherKey &key, uint64_t *chainedIV ) const -{ - shared_ptr mk = dynamic_pointer_cast(key); - uint64_t tmp = _checksum_64( mk.get(), data, len, chainedIV ); - - if(chainedIV) - *chainedIV = tmp; - - return tmp; -} - -CipherKey SSL_Cipher::readKey(const unsigned char *data, - const CipherKey &masterKey, bool checkKey) -{ - shared_ptr mk = dynamic_pointer_cast(masterKey); - rAssert(mk->keySize == _keySize); - - unsigned char tmpBuf[ MAX_KEYLENGTH + MAX_IVLENGTH ]; - - // First N bytes are checksum bytes. - unsigned int checksum = 0; - for(int i=0; i key( new SSLKey( _keySize, _ivLength) ); - - memcpy( key->buffer, tmpBuf, _keySize + _ivLength ); - memset( tmpBuf, 0, sizeof(tmpBuf) ); - - initKey( key, _blockCipher, _streamCipher, _keySize ); - - return key; -} - -void SSL_Cipher::writeKey(const CipherKey &ckey, unsigned char *data, - const CipherKey &masterKey) -{ - shared_ptr key = dynamic_pointer_cast(ckey); - rAssert(key->keySize == _keySize); - rAssert(key->ivLength == _ivLength); - - shared_ptr mk = dynamic_pointer_cast(masterKey); - rAssert(mk->keySize == _keySize); - rAssert(mk->ivLength == _ivLength); - - unsigned char tmpBuf[ MAX_KEYLENGTH + MAX_IVLENGTH ]; - - int bufLen = _keySize + _ivLength; - memcpy( tmpBuf, key->buffer, bufLen ); - - unsigned int checksum = MAC_32( tmpBuf, bufLen, masterKey ); - - streamEncode(tmpBuf, bufLen, checksum, masterKey); - memcpy( data+KEY_CHECKSUM_BYTES, tmpBuf, bufLen ); - - // first N bytes contain HMAC derived checksum.. - for(int i=1; i<=KEY_CHECKSUM_BYTES; ++i) - { - data[KEY_CHECKSUM_BYTES-i] = checksum & 0xff; - checksum >>= 8; - } - - memset( tmpBuf, 0, sizeof(tmpBuf) ); -} - -bool SSL_Cipher::compareKey( const CipherKey &A, const CipherKey &B) const -{ - shared_ptr key1 = dynamic_pointer_cast(A); - shared_ptr key2 = dynamic_pointer_cast(B); - - rAssert(key1->keySize == _keySize); - rAssert(key2->keySize == _keySize); - - if(memcmp(key1->buffer, key2->buffer, _keySize + _ivLength) != 0) - return false; - else - return true; -} - -int SSL_Cipher::encodedKeySize() const -{ - return _keySize + _ivLength + KEY_CHECKSUM_BYTES; -} - -int SSL_Cipher::keySize() const -{ - return _keySize; -} - -int SSL_Cipher::cipherBlockSize() const -{ - return EVP_CIPHER_block_size( _blockCipher ); -} - -void SSL_Cipher::setIVec( unsigned char *ivec, uint64_t seed, - const shared_ptr &key) const -{ - if (iface.major() >= 3) - { - memcpy( ivec, IVData(key), _ivLength ); - - unsigned char md[EVP_MAX_MD_SIZE]; - unsigned int mdLen = EVP_MAX_MD_SIZE; - - for(int i=0; i<8; ++i) - { - md[i] = (unsigned char)(seed & 0xff); - seed >>= 8; - } - - // combine ivec and seed with HMAC - HMAC_Init_ex( &key->mac_ctx, 0, 0, 0, 0 ); - HMAC_Update( &key->mac_ctx, ivec, _ivLength ); - HMAC_Update( &key->mac_ctx, md, 8 ); - HMAC_Final( &key->mac_ctx, md, &mdLen ); - rAssert(mdLen >= _ivLength); - - memcpy( ivec, md, _ivLength ); - } else - { - setIVec_old(ivec, seed, key); - } -} - -/** Deprecated: For backward compatibility only. - A watermark attack was discovered against this IV setup. If an attacker - could get a victim to store a carefully crafted file, they could later - determine if the victim had the file in encrypted storage (without - decrypting the file). - */ -void SSL_Cipher::setIVec_old(unsigned char *ivec, - unsigned int seed, - const shared_ptr &key) const -{ - unsigned int var1 = 0x060a4011 * seed; - unsigned int var2 = 0x0221040d * (seed ^ 0xD3FEA11C); - - memcpy( ivec, IVData(key), _ivLength ); - - ivec[0] ^= (var1 >> 24) & 0xff; - ivec[1] ^= (var2 >> 16) & 0xff; - ivec[2] ^= (var1 >> 8 ) & 0xff; - ivec[3] ^= (var2 ) & 0xff; - ivec[4] ^= (var2 >> 24) & 0xff; - ivec[5] ^= (var1 >> 16) & 0xff; - ivec[6] ^= (var2 >> 8 ) & 0xff; - ivec[7] ^= (var1 ) & 0xff; - - if(_ivLength > 8) - { - ivec[8+0] ^= (var1 ) & 0xff; - ivec[8+1] ^= (var2 >> 8 ) & 0xff; - ivec[8+2] ^= (var1 >> 16) & 0xff; - ivec[8+3] ^= (var2 >> 24) & 0xff; - ivec[8+4] ^= (var1 >> 24) & 0xff; - ivec[8+5] ^= (var2 >> 16) & 0xff; - ivec[8+6] ^= (var1 >> 8 ) & 0xff; - ivec[8+7] ^= (var2 ) & 0xff; - } -} - -static void flipBytes(unsigned char *buf, int size) -{ - unsigned char revBuf[64]; - - int bytesLeft = size; - while(bytesLeft) - { - int toFlip = MIN( sizeof(revBuf), bytesLeft ); - - for(int i=0; i 0 ); - shared_ptr key = dynamic_pointer_cast(ckey); - rAssert(key->keySize == _keySize); - rAssert(key->ivLength == _ivLength); - - Lock lock( key->mutex ); - - unsigned char ivec[ MAX_IVLENGTH ]; - int dstLen=0, tmpLen=0; - - shuffleBytes( buf, size ); - - setIVec( ivec, iv64, key ); - EVP_EncryptInit_ex( &key->stream_enc, NULL, NULL, NULL, ivec); - EVP_EncryptUpdate( &key->stream_enc, buf, &dstLen, buf, size ); - EVP_EncryptFinal_ex( &key->stream_enc, buf+dstLen, &tmpLen ); - - flipBytes( buf, size ); - shuffleBytes( buf, size ); - - setIVec( ivec, iv64 + 1, key ); - EVP_EncryptInit_ex( &key->stream_enc, NULL, NULL, NULL, ivec); - EVP_EncryptUpdate( &key->stream_enc, buf, &dstLen, buf, size ); - EVP_EncryptFinal_ex( &key->stream_enc, buf+dstLen, &tmpLen ); - - dstLen += tmpLen; - if(dstLen != size) - { - rError("encoding %i bytes, got back %i (%i in final_ex)", - size, dstLen, tmpLen); - } - - return true; -} - -bool SSL_Cipher::streamDecode(unsigned char *buf, int size, - uint64_t iv64, const CipherKey &ckey) const -{ - rAssert( size > 0 ); - shared_ptr key = dynamic_pointer_cast(ckey); - rAssert(key->keySize == _keySize); - rAssert(key->ivLength == _ivLength); - - Lock lock( key->mutex ); - - unsigned char ivec[ MAX_IVLENGTH ]; - int dstLen=0, tmpLen=0; - - setIVec( ivec, iv64 + 1, key ); - EVP_DecryptInit_ex( &key->stream_dec, NULL, NULL, NULL, ivec); - EVP_DecryptUpdate( &key->stream_dec, buf, &dstLen, buf, size ); - EVP_DecryptFinal_ex( &key->stream_dec, buf+dstLen, &tmpLen ); - - unshuffleBytes( buf, size ); - flipBytes( buf, size ); - - setIVec( ivec, iv64, key ); - EVP_DecryptInit_ex( &key->stream_dec, NULL, NULL, NULL, ivec); - EVP_DecryptUpdate( &key->stream_dec, buf, &dstLen, buf, size ); - EVP_DecryptFinal_ex( &key->stream_dec, buf+dstLen, &tmpLen ); - - unshuffleBytes( buf, size ); - - dstLen += tmpLen; - if(dstLen != size) - { - rError("encoding %i bytes, got back %i (%i in final_ex)", - size, dstLen, tmpLen); - } - - return true; -} - - -bool SSL_Cipher::blockEncode(unsigned char *buf, int size, - uint64_t iv64, const CipherKey &ckey ) const -{ - rAssert( size > 0 ); - shared_ptr key = dynamic_pointer_cast(ckey); - rAssert(key->keySize == _keySize); - rAssert(key->ivLength == _ivLength); - - // data must be integer number of blocks - const int blockMod = size % EVP_CIPHER_CTX_block_size( &key->block_enc ); - if(blockMod != 0) - throw ERROR("Invalid data size, not multiple of block size"); - - Lock lock( key->mutex ); - - unsigned char ivec[ MAX_IVLENGTH ]; - - int dstLen = 0, tmpLen = 0; - setIVec( ivec, iv64, key ); - - EVP_EncryptInit_ex( &key->block_enc, NULL, NULL, NULL, ivec); - EVP_EncryptUpdate( &key->block_enc, buf, &dstLen, buf, size ); - EVP_EncryptFinal_ex( &key->block_enc, buf+dstLen, &tmpLen ); - dstLen += tmpLen; - - if(dstLen != size) - { - rError("encoding %i bytes, got back %i (%i in final_ex)", - size, dstLen, tmpLen); - } - - return true; -} - -bool SSL_Cipher::blockDecode(unsigned char *buf, int size, - uint64_t iv64, const CipherKey &ckey ) const -{ - rAssert( size > 0 ); - shared_ptr key = dynamic_pointer_cast(ckey); - rAssert(key->keySize == _keySize); - rAssert(key->ivLength == _ivLength); - - // data must be integer number of blocks - const int blockMod = size % EVP_CIPHER_CTX_block_size( &key->block_dec ); - if(blockMod != 0) - throw ERROR("Invalid data size, not multiple of block size"); - - Lock lock( key->mutex ); - - unsigned char ivec[ MAX_IVLENGTH ]; - - int dstLen = 0, tmpLen = 0; - setIVec( ivec, iv64, key ); - - EVP_DecryptInit_ex( &key->block_dec, NULL, NULL, NULL, ivec); - EVP_DecryptUpdate( &key->block_dec, buf, &dstLen, buf, size ); - EVP_DecryptFinal_ex( &key->block_dec, buf+dstLen, &tmpLen ); - dstLen += tmpLen; - - if(dstLen != size) - { - rError("decoding %i bytes, got back %i (%i in final_ex)", - size, dstLen, tmpLen); - } - - return true; -} - -bool SSL_Cipher::Enabled() -{ - return true; -} - diff --git a/encfs/SSL_Cipher.h b/encfs/SSL_Cipher.h deleted file mode 100644 index 375ae14..0000000 --- a/encfs/SSL_Cipher.h +++ /dev/null @@ -1,150 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2004, Valient Gough - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#ifndef _SSL_Cipher_incl_ -#define _SSL_Cipher_incl_ - -#include "Cipher.h" -#include "Interface.h" - -struct SSLKey; -#ifndef EVP_CIPHER -struct evp_cipher_st; -typedef struct evp_cipher_st EVP_CIPHER; -#endif - -/* - Implements Cipher interface for OpenSSL's ciphers. - - Design: - Variable algorithm, key size, and block size. - - Partial blocks, keys, and names are encrypted using the cipher in a pseudo - stream mode (CFB). - - Keys are encrypted with 2-4 (KEY_CHECKSUM_BYTES define) checksum bytes - derived from an HMAC over both they key data and the initial value vector - associated with the key. This allows a good chance at detecting an - incorrect password when we try and decrypt the master key. - - File names are encrypted in the same way, with 2 checksum bytes derived - from an HMAC over the filename. This is done not to allow checking the - results, but to make the output much more random. Changing one letter in a - filename should result in a completely different encrypted filename, to - help frustrate any attempt to guess information about files from their - encrypted names. - - Stream encryption involves two encryption passes over the data, implemented - as: - 1. shuffle - 2. encrypt - 3. reverse - 4. shuffle - 5. encrypt - The reason for the shuffle and reverse steps (and the second encrypt pass) - is to try and propogate any changed bits to a larger set. If only a single - pass was made with the stream cipher in CFB mode, then a change to one byte - may only affect one byte of output, allowing some XOR attacks. - - The shuffle/encrypt is used as above in filename encryption as well, - although it is not necessary as they have checksum bytes which augment the - initial value vector to randomize the output. But it makes the code - simpler to reuse the encryption algorithm as is. -*/ -class SSL_Cipher : public Cipher -{ - Interface iface; - Interface realIface; - const EVP_CIPHER *_blockCipher; - const EVP_CIPHER *_streamCipher; - unsigned int _keySize; // in bytes - unsigned int _ivLength; - -public: - SSL_Cipher(const Interface &iface, const Interface &realIface, - const EVP_CIPHER *blockCipher, const EVP_CIPHER *streamCipher, - int keyLength); - virtual ~SSL_Cipher(); - - // returns the real interface, not the one we're emulating (if any).. - virtual Interface interface() const; - - // create a new key based on a password - virtual CipherKey newKey(const char *password, int passwdLength, - int &iterationCount, long desiredDuration, - const unsigned char *salt, int saltLen); - // deprecated - for backward compatibility - virtual CipherKey newKey(const char *password, int passwdLength); - // create a new random key - virtual CipherKey newRandomKey(); - - // data must be len keySize() - virtual CipherKey readKey(const unsigned char *data, - const CipherKey &encodingKey, - bool checkKey); - virtual void writeKey(const CipherKey &key, unsigned char *data, - const CipherKey &encodingKey); - virtual bool compareKey( const CipherKey &A, - const CipherKey &B ) const; - - // meta-data about the cypher - virtual int keySize() const; - virtual int encodedKeySize() const; - virtual int cipherBlockSize() const; - - virtual bool randomize( unsigned char *buf, int len, - bool strongRandom ) const; - - virtual uint64_t MAC_64( const unsigned char *src, int len, - const CipherKey &key, uint64_t *augment ) const; - - // functional interfaces - /* - Stream encoding in-place. - */ - virtual bool streamEncode(unsigned char *in, int len, - uint64_t iv64, const CipherKey &key) const; - virtual bool streamDecode(unsigned char *in, int len, - uint64_t iv64, const CipherKey &key) const; - - /* - Block encoding is done in-place. Partial blocks are supported, but - blocks are always expected to begin on a block boundary. See - blockSize(). - */ - virtual bool blockEncode(unsigned char *buf, int size, - uint64_t iv64, const CipherKey &key) const; - virtual bool blockDecode(unsigned char *buf, int size, - uint64_t iv64, const CipherKey &key) const; - - // hack to help with static builds - static bool Enabled(); -private: - void setIVec( unsigned char *ivec, uint64_t seed, - const shared_ptr &key ) const; - - // deprecated - for backward compatibility - void setIVec_old( unsigned char *ivec, unsigned int seed, - const shared_ptr &key ) const; -}; - - -#endif - diff --git a/encfs/StreamNameIO.cpp b/encfs/StreamNameIO.cpp deleted file mode 100644 index 8e275de..0000000 --- a/encfs/StreamNameIO.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2004, Valient Gough - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "StreamNameIO.h" - -#include "Cipher.h" -#include "base64.h" - -#include -#include - -#include "i18n.h" -#include - -using namespace std; - -static shared_ptr NewStreamNameIO( const Interface &iface, - const shared_ptr &cipher, const CipherKey &key) -{ - return shared_ptr( new StreamNameIO( iface, cipher, key ) ); -} - -static bool StreamIO_registered = NameIO::Register("Stream", - gettext_noop("Stream encoding, keeps filenames as short as possible"), - StreamNameIO::CurrentInterface(), - NewStreamNameIO); - - -/* - - Version 0.1 is for EncFS 0.x support. The difference to 1.0 is that 0.x - stores the file checksums at the end of the encoded name, where 1.0 - stores them at the beginning. - - - Version 1.0 is the basic stream encoding mode used since the beginning of - EncFS. There is a slight difference in filename encodings from EncFS 0.x - to 1.0.x. This implements just the 1.0.x method. - - - Version 1.1 adds support for IV chaining. This is transparently - backward compatible, since older filesystems do not use IV chaining. - - - Version 2.0 uses full 64 bit IV during IV chaining mode. Prior versions - used only the 16 bit output from MAC_16. This reduces the theoretical - possibility (unlikely to make any difference in practice) of two files - with the same name in different directories ending up with the same - encrypted name. Added because there is no good reason to chop to 16 - bits. - - - Version 2.1 adds support for version 0 for EncFS 0.x compatibility. -*/ -Interface StreamNameIO::CurrentInterface() -{ - // implement major version 2, 1, and 0 - return makeInterface("nameio/stream", 2, 1, 2); -} - -StreamNameIO::StreamNameIO( const Interface &iface, - const shared_ptr &cipher, - const CipherKey &key ) - : _interface( iface.major() ) - , _cipher( cipher ) - , _key( key ) -{ - -} - -StreamNameIO::~StreamNameIO() -{ -} - -Interface StreamNameIO::interface() const -{ - return CurrentInterface(); -} - -int StreamNameIO::maxEncodedNameLen( int plaintextStreamLen ) const -{ - int encodedStreamLen = 2 + plaintextStreamLen; - return B256ToB64Bytes( encodedStreamLen ); -} - -int StreamNameIO::maxDecodedNameLen( int encodedStreamLen ) const -{ - int decLen256 = B64ToB256Bytes( encodedStreamLen ); - return decLen256 - 2; -} - -int StreamNameIO::encodeName( const char *plaintextName, int length, - uint64_t *iv, char *encodedName ) const -{ - uint64_t tmpIV = 0; - if( iv && _interface >= 2 ) - tmpIV = *iv; - - unsigned int mac = _cipher->MAC_16( (const unsigned char *)plaintextName, - length, _key, iv ); - - // add on checksum bytes - unsigned char *encodeBegin; - if(_interface >= 1) - { - // current versions store the checksum at the beginning - encodedName[0] = (mac >> 8) & 0xff; - encodedName[1] = (mac ) & 0xff; - encodeBegin = (unsigned char *)encodedName+2; - } else - { - // encfs 0.x stored checksums at the end. - encodedName[length] = (mac >> 8) & 0xff; - encodedName[length+1] = (mac ) & 0xff; - encodeBegin = (unsigned char *)encodedName; - } - - // stream encode the plaintext bytes - memcpy( encodeBegin, plaintextName, length ); - _cipher->nameEncode( encodeBegin, length, (uint64_t)mac ^ tmpIV, _key); - - // convert the entire thing to base 64 ascii.. - int encodedStreamLen = length + 2; - int encLen64 = B256ToB64Bytes( encodedStreamLen ); - - changeBase2Inline( (unsigned char *)encodedName, encodedStreamLen, - 8, 6, true ); - B64ToAscii( (unsigned char *)encodedName, encLen64 ); - - return encLen64; -} - -int StreamNameIO::decodeName( const char *encodedName, int length, - uint64_t *iv, char *plaintextName ) const -{ - rAssert(length > 2); - int decLen256 = B64ToB256Bytes( length ); - int decodedStreamLen = decLen256 - 2; - - if(decodedStreamLen <= 0) - throw ERROR("Filename too small to decode"); - - BUFFER_INIT( tmpBuf, 32, (unsigned int)length ); - - // decode into tmpBuf, because this step produces more data then we can fit - // into the result buffer.. - AsciiToB64( (unsigned char *)tmpBuf, (unsigned char *)encodedName, length ); - changeBase2Inline((unsigned char *)tmpBuf, length, 6, 8, false); - - // pull out the checksum value which is used as an initialization vector - uint64_t tmpIV = 0; - unsigned int mac; - if(_interface >= 1) - { - // current versions store the checksum at the beginning - mac = ((unsigned int)((unsigned char)tmpBuf[0])) << 8 - | ((unsigned int)((unsigned char)tmpBuf[1])); - - // version 2 adds support for IV chaining.. - if( iv && _interface >= 2 ) - tmpIV = *iv; - - memcpy( plaintextName, tmpBuf+2, decodedStreamLen ); - } else - { - // encfs 0.x stored checksums at the end. - mac = ((unsigned int)((unsigned char)tmpBuf[decodedStreamLen])) << 8 - | ((unsigned int)((unsigned char)tmpBuf[decodedStreamLen+1])); - - memcpy( plaintextName, tmpBuf, decodedStreamLen ); - } - - // use nameDeocde instead of streamDecode for backward compatibility - _cipher->nameDecode( (unsigned char *)plaintextName, decodedStreamLen, - (uint64_t)mac ^ tmpIV, _key); - - // compute MAC to check with stored value - unsigned int mac2 = _cipher->MAC_16((const unsigned char *)plaintextName, - decodedStreamLen, _key, iv); - - BUFFER_RESET( tmpBuf ); - if(mac2 != mac) - { - rDebug("checksum mismatch: expected %u, got %u", mac, mac2); - rDebug("on decode of %i bytes", decodedStreamLen); - throw ERROR( "checksum mismatch in filename decode" ); - } - - return decodedStreamLen; -} - -bool StreamNameIO::Enabled() -{ - return true; -} - diff --git a/encfs/docs/Makefile.am b/encfs/docs/Makefile.am deleted file mode 100644 index 271bd41..0000000 --- a/encfs/docs/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -####### kdevelop will overwrite this part!!! (begin)########## - - -####### kdevelop will overwrite this part!!! (end)############ diff --git a/encfs/docs/en/Makefile.am b/encfs/docs/en/Makefile.am deleted file mode 100644 index 271bd41..0000000 --- a/encfs/docs/en/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -####### kdevelop will overwrite this part!!! (begin)########## - - -####### kdevelop will overwrite this part!!! (end)############ diff --git a/encfs/encfs.cpp b/encfs/encfs.cpp deleted file mode 100644 index 69f3dd7..0000000 --- a/encfs/encfs.cpp +++ /dev/null @@ -1,807 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2003-2007, Valient Gough - * - * This program is free software; you can distribute it and/or modify it under - * the terms of the GNU General Public License (GPL), as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ - -#include "encfs.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#ifdef linux -#include -#endif - -#ifdef HAVE_ATTR_XATTR_H -#include -#elif HAVE_SYS_XATTR_H -#include -#endif - - -#include -#include - -#if HAVE_TR1_TUPLE -#include -using namespace std; -using namespace std::tr1; -#else -#include -using namespace std; -#endif - -#include "DirNode.h" -#include "MemoryPool.h" -#include "FileUtils.h" -#include "Mutex.h" -#include "Context.h" -#include "shared_ptr.h" - -#include -#include - -#ifndef MIN -#define MIN(a,b) (((a)<(b)) ? (a): (b)) -#endif - -#define ESUCCESS 0 - -using namespace rlog; -using rel::Lock; - -#define GET_FN(ctx, finfo) ctx->getNode((void*)(uintptr_t)finfo->fh) - -static RLogChannel *Info = DEF_CHANNEL("info", Log_Info); - -static EncFS_Context * context() -{ - return (EncFS_Context*)fuse_get_context()->private_data; -} - -// helper function -- apply a functor to a cipher path, given the plain path -template -static int withCipherPath( const char *opName, const char *path, - int (*op)(EncFS_Context *, const string &name, T data ), T data, - bool passReturnCode = false ) -{ - EncFS_Context *ctx = context(); - - int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; - - try - { - string cyName = FSRoot->cipherPath( path ); - rLog(Info, "%s %s", opName, cyName.c_str()); - - res = op( ctx, cyName, data ); - - if(res == -1) - { - int eno = errno; - rInfo("%s error: %s", opName, strerror(eno)); - res = -eno; - } else if(!passReturnCode) - res = ESUCCESS; - } catch( rlog::Error &err ) - { - rError("error caught in %s", opName); - err.log( _RLWarningChannel ); - } - return res; -} - -// helper function -- apply a functor to a node -template -static int withFileNode( const char *opName, - const char *path, struct fuse_file_info *fi, - int (*op)(FileNode *, T data ), T data ) -{ - EncFS_Context *ctx = context(); - - int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; - - try - { - shared_ptr fnode; - - if(fi != NULL) - fnode = GET_FN(ctx, fi); - else - fnode = FSRoot->lookupNode( path, opName ); - - rAssert(fnode != NULL); - rLog(Info, "%s %s", opName, fnode->cipherName()); - res = op( fnode.get(), data ); - - if(res < 0) - rInfo("%s error: %s", opName, strerror(-res)); - } catch( rlog::Error &err ) - { - rError("error caught in %s", opName); - err.log( _RLWarningChannel ); - } - return res; -} - -/* - The rLog messages below always print out encrypted filenames, not - plaintext. The reason is so that it isn't possible to leak information - about the encrypted data through rlog interfaces. - - - The purpose of this layer of code is to take the FUSE request and dispatch - to the internal interfaces. Any marshaling of arguments and return types - can be done here. -*/ - -int _do_getattr(FileNode *fnode, struct stat *stbuf) -{ - int res = fnode->getAttr(stbuf); - if(res == ESUCCESS && S_ISLNK(stbuf->st_mode)) - { - EncFS_Context *ctx = context(); - shared_ptr FSRoot = ctx->getRoot(&res); - if(FSRoot) - { - // determine plaintext link size.. Easiest to read and decrypt.. - vector buf(stbuf->st_size+1, 0); - - res = ::readlink( fnode->cipherName(), &buf[0], stbuf->st_size ); - if(res >= 0) - { - // other functions expect c-strings to be null-terminated, which - // readlink doesn't provide - buf[res] = '\0'; - - stbuf->st_size = FSRoot->plainPath( &buf[0] ).length(); - - res = ESUCCESS; - } else - res = -errno; - } - } - - return res; -} - -int encfs_getattr(const char *path, struct stat *stbuf) -{ - return withFileNode( "getattr", path, NULL, _do_getattr, stbuf ); -} - -int encfs_fgetattr(const char *path, struct stat *stbuf, - struct fuse_file_info *fi) -{ - return withFileNode( "fgetattr", path, fi, _do_getattr, stbuf ); -} - -int encfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler) -{ - EncFS_Context *ctx = context(); - - int res = ESUCCESS; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; - - try - { - - DirTraverse dt = FSRoot->openDir( path ); - - rLog(Info, "getdir on %s", FSRoot->cipherPath(path).c_str()); - - if(dt.valid()) - { - int fileType = 0; - ino_t inode = 0; - - std::string name = dt.nextPlaintextName( &fileType, &inode ); - while( !name.empty() ) - { - res = filler( h, name.c_str(), fileType, inode ); - - if(res != ESUCCESS) - break; - - name = dt.nextPlaintextName( &fileType, &inode ); - } - } else - { - rInfo("getdir request invalid, path: '%s'", path); - } - - return res; - } catch( rlog::Error &err ) - { - rError("Error caught in getdir"); - err.log( _RLWarningChannel ); - return -EIO; - } -} - -int encfs_mknod(const char *path, mode_t mode, dev_t rdev) -{ - EncFS_Context *ctx = context(); - - int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; - - try - { - shared_ptr fnode = FSRoot->lookupNode( path, "mknod" ); - - rLog(Info, "mknod on %s, mode %i, dev %" PRIi64, - fnode->cipherName(), mode, (int64_t)rdev); - - uid_t uid = 0; - gid_t gid = 0; - if(ctx->publicFilesystem) - { - fuse_context *context = fuse_get_context(); - uid = context->uid; - gid = context->gid; - } - res = fnode->mknod( mode, rdev, uid, gid ); - // Is this error due to access problems? - if(ctx->publicFilesystem && -res == EACCES) - { - // try again using the parent dir's group - string parent = fnode->plaintextParent(); - rInfo("trying public filesystem workaround for %s", parent.c_str()); - shared_ptr dnode = - FSRoot->lookupNode( parent.c_str(), "mknod" ); - - struct stat st; - if(dnode->getAttr( &st ) == 0) - res = fnode->mknod( mode, rdev, uid, st.st_gid ); - } - } catch( rlog::Error &err ) - { - rError("error caught in mknod"); - err.log( _RLWarningChannel ); - } - return res; -} - -int encfs_mkdir(const char *path, mode_t mode) -{ - fuse_context *fctx = fuse_get_context(); - EncFS_Context *ctx = context(); - - int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; - - try - { - uid_t uid = 0; - gid_t gid = 0; - if(ctx->publicFilesystem) - { - uid = fctx->uid; - gid = fctx->gid; - } - res = FSRoot->mkdir( path, mode, uid, gid ); - // Is this error due to access problems? - if(ctx->publicFilesystem && -res == EACCES) - { - // try again using the parent dir's group - string parent = parentDirectory( path ); - shared_ptr dnode = - FSRoot->lookupNode( parent.c_str(), "mkdir" ); - - struct stat st; - if(dnode->getAttr( &st ) == 0) - res = FSRoot->mkdir( path, mode, uid, st.st_gid ); - } - } catch( rlog::Error &err ) - { - rError("error caught in mkdir"); - err.log( _RLWarningChannel ); - } - return res; -} - -int encfs_unlink(const char *path) -{ - EncFS_Context *ctx = context(); - - int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; - - try - { - // let DirNode handle it atomically so that it can handle race - // conditions - res = FSRoot->unlink( path ); - } catch( rlog::Error &err ) - { - rError("error caught in unlink"); - err.log( _RLWarningChannel ); - } - return res; -} - - -int _do_rmdir(EncFS_Context *, const string &cipherPath, int ) -{ - return rmdir( cipherPath.c_str() ); -} - -int encfs_rmdir(const char *path) -{ - return withCipherPath( "rmdir", path, _do_rmdir, 0 ); -} - -int _do_readlink(EncFS_Context *ctx, const string &cyName, - tuple data ) -{ - char *buf = get<0>(data); - size_t size = get<1>(data); - - int res = ESUCCESS; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; - - res = ::readlink( cyName.c_str(), buf, size-1 ); - - if(res == -1) - return -errno; - - buf[res] = '\0'; // ensure null termination - string decodedName; - try - { - decodedName = FSRoot->plainPath( buf ); - } catch(...) { } - - if(!decodedName.empty()) - { - strncpy(buf, decodedName.c_str(), size-1); - buf[size-1] = '\0'; - - return ESUCCESS; - } else - { - rWarning("Error decoding link"); - return -1; - } -} - -int encfs_readlink(const char *path, char *buf, size_t size) -{ - return withCipherPath( "readlink", path, _do_readlink, - make_tuple(buf, size) ); -} - -int encfs_symlink(const char *from, const char *to) -{ - EncFS_Context *ctx = context(); - - int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; - - try - { - // allow fully qualified names in symbolic links. - string fromCName = FSRoot->relativeCipherPath( from ); - string toCName = FSRoot->cipherPath( to ); - - rLog(Info, "symlink %s -> %s", fromCName.c_str(), toCName.c_str()); - - // use setfsuid / setfsgid so that the new link will be owned by the - // uid/gid provided by the fuse_context. - int olduid = -1; - int oldgid = -1; - if(ctx->publicFilesystem) - { - fuse_context *context = fuse_get_context(); - olduid = setfsuid( context->uid ); - oldgid = setfsgid( context->gid ); - } - res = ::symlink( fromCName.c_str(), toCName.c_str() ); - if(olduid >= 0) - setfsuid( olduid ); - if(oldgid >= 0) - setfsgid( oldgid ); - - if(res == -1) - res = -errno; - else - res = ESUCCESS; - } catch( rlog::Error &err ) - { - rError("error caught in symlink"); - err.log( _RLWarningChannel ); - } - return res; -} - -int encfs_link(const char *from, const char *to) -{ - EncFS_Context *ctx = context(); - - int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; - - try - { - res = FSRoot->link( from, to ); - } catch( rlog::Error &err ) - { - rError("error caught in link"); - err.log( _RLWarningChannel ); - } - return res; -} - -int encfs_rename(const char *from, const char *to) -{ - EncFS_Context *ctx = context(); - - int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; - - try - { - res = FSRoot->rename( from, to ); - } catch( rlog::Error &err ) - { - rError("error caught in rename"); - err.log( _RLWarningChannel ); - } - return res; -} - -int _do_chmod(EncFS_Context *, const string &cipherPath, mode_t mode) -{ - return chmod( cipherPath.c_str(), mode ); -} - -int encfs_chmod(const char *path, mode_t mode) -{ - return withCipherPath( "chmod", path, _do_chmod, mode ); -} - -int _do_chown(EncFS_Context *, const string &cyName, - tuple data) -{ - int res = lchown( cyName.c_str(), get<0>(data), get<1>(data) ); - return (res == -1) ? -errno : ESUCCESS; -} - -int encfs_chown(const char *path, uid_t uid, gid_t gid) -{ - return withCipherPath( "chown", path, _do_chown, make_tuple(uid, gid)); -} - -int _do_truncate( FileNode *fnode, off_t size ) -{ - return fnode->truncate( size ); -} - -int encfs_truncate(const char *path, off_t size) -{ - return withFileNode( "truncate", path, NULL, _do_truncate, size ); -} - -int encfs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi) -{ - return withFileNode( "ftruncate", path, fi, _do_truncate, size ); -} - -int _do_utime(EncFS_Context *, const string &cyName, struct utimbuf *buf) -{ - int res = utime( cyName.c_str(), buf); - return (res == -1) ? -errno : ESUCCESS; -} - -int encfs_utime(const char *path, struct utimbuf *buf) -{ - return withCipherPath( "utime", path, _do_utime, buf ); -} - -int _do_utimens(EncFS_Context *, const string &cyName, - const struct timespec ts[2]) -{ - struct timeval tv[2]; - tv[0].tv_sec = ts[0].tv_sec; - tv[0].tv_usec = ts[0].tv_nsec / 1000; - tv[1].tv_sec = ts[1].tv_sec; - tv[1].tv_usec = ts[1].tv_nsec / 1000; - - int res = lutimes( cyName.c_str(), tv); - return (res == -1) ? -errno : ESUCCESS; -} - -int encfs_utimens(const char *path, const struct timespec ts[2] ) -{ - return withCipherPath( "utimens", path, _do_utimens, ts ); -} - -int encfs_open(const char *path, struct fuse_file_info *file) -{ - EncFS_Context *ctx = context(); - - int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; - - try - { - shared_ptr fnode = - FSRoot->openNode( path, "open", file->flags, &res ); - - if(fnode) - { - rLog(Info, "encfs_open for %s, flags %i", fnode->cipherName(), - file->flags); - - if( res >= 0 ) - { - file->fh = (uintptr_t)ctx->putNode(path, fnode); - res = ESUCCESS; - } - } - } catch( rlog::Error &err ) - { - rError("error caught in open"); - err.log( _RLWarningChannel ); - } - - return res; -} - -int _do_flush(FileNode *fnode, int ) -{ - /* Flush can be called multiple times for an open file, so it doesn't - close the file. However it is important to call close() for some - underlying filesystems (like NFS). - */ - int res = fnode->open( O_RDONLY ); - if(res >= 0) - { - int fh = res; - res = close(dup(fh)); - if(res == -1) - res = -errno; - } - - return res; -} - -int encfs_flush(const char *path, struct fuse_file_info *fi) -{ - return withFileNode( "flush", path, fi, _do_flush, 0 ); -} - -/* -Note: This is advisory -- it might benefit us to keep file nodes around for a -bit after they are released just in case they are reopened soon. But that -requires a cache layer. - */ -int encfs_release(const char *path, struct fuse_file_info *finfo) -{ - EncFS_Context *ctx = context(); - - try - { - ctx->eraseNode( path, (void*)(uintptr_t)finfo->fh ); - return ESUCCESS; - } catch( rlog::Error &err ) - { - rError("error caught in release"); - err.log( _RLWarningChannel ); - return -EIO; - } -} - -int _do_read(FileNode *fnode, tuple data) -{ - return fnode->read( get<2>(data), get<0>(data), get<1>(data)); -} - -int encfs_read(const char *path, char *buf, size_t size, off_t offset, - struct fuse_file_info *file) -{ - return withFileNode( "read", path, file, _do_read, - make_tuple((unsigned char *)buf, size, offset)); -} - -int _do_fsync(FileNode *fnode, int dataSync) -{ - return fnode->sync( dataSync != 0 ); -} - -int encfs_fsync(const char *path, int dataSync, - struct fuse_file_info *file) -{ - return withFileNode( "fsync", path, file, _do_fsync, dataSync ); -} - -int _do_write(FileNode *fnode, tuple data) -{ - size_t size = get<1>(data); - if(fnode->write( get<2>(data), (unsigned char *)get<0>(data), size )) - return size; - else - return -EIO; -} - -int encfs_write(const char *path, const char *buf, size_t size, - off_t offset, struct fuse_file_info *file) -{ - return withFileNode("write", path, file, _do_write, - make_tuple(buf, size, offset)); -} - -// statfs works even if encfs is detached.. -int encfs_statfs(const char *path, struct statvfs *st) -{ - EncFS_Context *ctx = context(); - - int res = -EIO; - try - { - (void)path; // path should always be '/' for now.. - rAssert( st != NULL ); - string cyName = ctx->rootCipherDir; - - rLog(Info, "doing statfs of %s", cyName.c_str()); - res = statvfs( cyName.c_str(), st ); - if(!res) - { - // adjust maximum name length.. - st->f_namemax = 6 * (st->f_namemax - 2) / 8; // approx.. - } - if(res == -1) - res = -errno; - } catch( rlog::Error &err ) - { - rError("error caught in statfs"); - err.log( _RLWarningChannel ); - } - return res; -} - -#ifdef HAVE_XATTR - - -#ifdef XATTR_ADD_OPT -int _do_setxattr(EncFS_Context *, const string &cyName, - tuple data) -{ - int options = 0; - return ::setxattr( cyName.c_str(), get<0>(data), get<1>(data), - get<2>(data), get<3>(data), options ); -} -int encfs_setxattr( const char *path, const char *name, - const char *value, size_t size, int flags, uint32_t position ) -{ - (void)flags; - return withCipherPath( "setxattr", path, _do_setxattr, - make_tuple(name, value, size, position) ); -} -#else -int _do_setxattr(EncFS_Context *, const string &cyName, - tuple data) -{ - return ::setxattr( cyName.c_str(), get<0>(data), get<1>(data), - get<2>(data), get<3>(data) ); -} -int encfs_setxattr( const char *path, const char *name, - const char *value, size_t size, int flags ) -{ - return withCipherPath( "setxattr", path, _do_setxattr, - make_tuple(name, value, size, flags) ); -} -#endif - - -#ifdef XATTR_ADD_OPT -int _do_getxattr(EncFS_Context *, const string &cyName, - tuple data) -{ - int options = 0; - return ::getxattr( cyName.c_str(), get<0>(data), - get<1>(data), get<2>(data), get<3>(data), options ); -} -int encfs_getxattr( const char *path, const char *name, - char *value, size_t size, uint32_t position ) -{ - return withCipherPath( "getxattr", path, _do_getxattr, - make_tuple(name, (void *)value, size, position), true ); -} -#else -int _do_getxattr(EncFS_Context *, const string &cyName, - tuple data) -{ - return ::getxattr( cyName.c_str(), get<0>(data), - get<1>(data), get<2>(data)); -} -int encfs_getxattr( const char *path, const char *name, - char *value, size_t size ) -{ - return withCipherPath( "getxattr", path, _do_getxattr, - make_tuple(name, (void *)value, size), true ); -} -#endif - - -int _do_listxattr(EncFS_Context *, const string &cyName, - tuple data) -{ -#ifdef XATTR_ADD_OPT - int options = 0; - int res = ::listxattr( cyName.c_str(), get<0>(data), get<1>(data), - options ); -#else - int res = ::listxattr( cyName.c_str(), get<0>(data), get<1>(data) ); -#endif - return (res == -1) ? -errno : res; -} - -int encfs_listxattr( const char *path, char *list, size_t size ) -{ - return withCipherPath( "listxattr", path, _do_listxattr, - make_tuple(list, size), true ); -} - -int _do_removexattr(EncFS_Context *, const string &cyName, const char *name) -{ -#ifdef XATTR_ADD_OPT - int options = 0; - int res = ::removexattr( cyName.c_str(), name, options ); -#else - int res = ::removexattr( cyName.c_str(), name ); -#endif - return (res == -1) ? -errno : res; -} - -int encfs_removexattr( const char *path, const char *name ) -{ - return withCipherPath( "removexattr", path, _do_removexattr, name ); -} - -#endif // HAVE_XATTR - diff --git a/encfs/encfs.pod b/encfs/encfs.pod index d519366..3807d2f 100644 --- a/encfs/encfs.pod +++ b/encfs/encfs.pod @@ -1,12 +1,3 @@ -=cut -Copyright (c) 2003-2008, Valient Gough -All rights reserved. - -EncFS is free software; you can distribute it and/or modify it under the terms -of the GNU General Public License (GPL), as published by the Free Software -Foundation; either version 3 of the License, or (at your option) any later -version. - =pod =head1 NAME @@ -174,9 +165,8 @@ the B help page for information on available commands. =item B<--no-default-flags> B adds the FUSE flags "use_ino" and "default_permissions" by default, as -of version 1.2.2, because that improves compatibility with some programs.. If -for some reason you need to disable one or both of these flags, use the option -B<--no-default-flags>. +that improves compatibility with some programs.. If you need to disable one or +both of these flags, use the option B<--no-default-flags>. The following command lines produce the same result: @@ -196,10 +186,6 @@ for a trailing newline (\n) which will be removed. For example, specifying B<--extpass>=I will cause B to use ssh's password prompt program. -B: B reads at most 2k of data from the password program, and it -removes any trailing newline. Versions before 1.4.x accepted only 64 bytes of -text. - =item B<-S>, B<--stdinpass> Read password from standard input, without prompting. This may be useful for @@ -209,6 +195,10 @@ Note that you should make sure the filesystem and mount points exist first. Otherwise encfs will prompt for the filesystem creation options, which may interfere with your script. +B: B reads a limited amount of data from the console (roughly 2k +bytes), and it removes any trailing newline. If your password is larger than +this, use --extpass. + =item B<--anykey> Turn off key validation checking. This allows B to be used with @@ -275,10 +265,10 @@ to decode filenames if desired. =head1 CAVEATS B is not a true filesystem. It does not deal with any of the actual -storage or maintenance of files. It simply translates requests (encrypting or +storage or maintenance of files. It translates requests (encrypting or decrypting as necessary) and passes the requests through to the underlying -host filesystem. Therefor any limitations of the host filesystem will likely -be inherited by B (or possibly be further limited). +host filesystem. Therefor any limitations of the host filesystem will be +inherited by B (or possibly be further limited). One such limitation is filename length. If your underlying filesystem limits you to N characters in a filename, then B will limit you to approximately @@ -328,20 +318,20 @@ they mean: =head1 Key Derivation Function -As of version 1.5, B now uses PBKDF2 as the default key derivation -function. The number of iterations in the keying function is selected based on -wall clock time to generate the key. In standard mode, a target time of 0.5 -seconds is used, and in paranoia mode a target of 3.0 seconds is used. +B uses PBKDF2 as the key derivation function. The number of iterations +in the keying function is selected based on wall clock time to generate the +key. In standard mode, a target time of 0.5 seconds is used, and in paranoia +mode a target of 3.0 seconds is used. On a 1.6Ghz AMD 64 system, it rougly 64k iterations of the key derivation function can be handled in half a second. The exact number of iterations to use is stored in the configuration file, as it is needed to remount the filesystem. -If an B filesystem configuration from 1.4.x is modified with version 1.5 -(such as when using encfsctl to change the password), then the new PBKDF2 -function will be used and the filesystem will no longer be readable by older -versions. +If an B filesystem configuration from 1.4.x is modified with a later +version (such as when using encfsctl to change the password), then the new +PBKDF2 function will be used and the filesystem will no longer be readable by +older versions. =over 4 @@ -375,18 +365,16 @@ read and decoded, so a large block size adds overhead to small requests. With write calls it is even worse, as a block must be read and decoded, the change applied and the block encoded and written back out. -The default is 512 bytes as of version 1.0. It was hard coded to 64 bytes in -version 0.x, which was not as efficient as the current setting for general -usage. +The default block size is currently 2k. =item I -B. A choice is given between stream encoding of filename and block -encoding. The advantage of stream encoding is that the encoded filenames will -be as short as possible. If you have a filename with a single letter, it will -be very short in the encoded form, where as block encoded filenames are always -rounded up to the block size of the encryption cipher (8 bytes for Blowfish and -16 bytes for AES). +A choice is given between stream encoding of filename and block encoding. The +advantage of stream encoding is that the encoded filenames will be as short as +possible. If you have a filename with a single letter, it will be very short +in the encoded form, where as block encoded filenames are always rounded up to +the block size of the encryption cipher (8 bytes for Blowfish and 16 bytes for +AES). The advantage of block encoding mode is that filename lenths all come out as a multiple of the cipher block size. This means that someone looking at your @@ -395,16 +383,14 @@ on by default, as it takes a similar amount of time to using the stream cipher. However stream cipher mode may be useful if you want shorter encrypted filenames for some reason. -Prior to version 1.1, only stream encoding was supported. - =item I -B. In previous versions of B, each filename element in -a path was encoded separately. So if "foo" encoded to "XXX", then it would -always encode that way (given the same encryption key), no matter if the path -was "a/b/foo", or "aa/foo/cc", etc. That meant it was possible for someone -looking at the encrypted data to see if two files in different directories had -the same name, even though they wouldn't know what that name decoded to. +In previous versions of B, each filename element in a path was encoded +separately. So if "foo" encoded to "XXX", then it would always encode that way +(given the same encryption key), no matter if the path was "a/b/foo", or +"aa/foo/cc", etc. That meant it was possible for someone looking at the +encrypted data to see if two files in different directories had the same name, +even though they wouldn't know what that name decoded to. With initialization vector chaining, each directory gets its own initialization vector. So "a/foo" and "b/foo" will have completely different encoded names @@ -422,11 +408,11 @@ rename will fail. =item I -B. In previous versions of B, each file was encoded in the -same way. Each block in a file has always had its own initialization vector, -but in a deterministic way so that block N in one file is encoded in the same -was as block N in another file. That made it possible for someone to tell if -two files were identical (or parts of the file were identical) by comparing the +In previous versions of B, each file was encoded in the same way. Each +block in a file has always had its own initialization vector, but in a +deterministic way so that block N in one file is encoded in the same was as +block N in another file. That made it possible for someone to tell if two +files were identical (or parts of the file were identical) by comparing the encoded data. With per-file initialization vectors, each file gets its own 64bit random @@ -436,10 +422,9 @@ This option is enabled by default. =item I -B. This option is closely related to Per-File Initialization -Vectors and Filename Initialization Vector Chaining. Basically it extends the -initialization vector chaining from filenames to the per-file initialization -vector. +This option is closely related to Per-File Initialization Vectors and Filename +Initialization Vector Chaining. Basically it extends the initialization vector +chaining from filenames to the per-file initialization vector. When this option is enabled, the per-file initialization vector is encoded using the initialization vector derived from the filename initialization vector @@ -461,11 +446,11 @@ Because of these limits, this option is disabled by default for standard mode =item I -B. If this is enabled, every block in every file is stored along -with a cryptographic checksum (Message Authentication Code). This makes it -virtually impossible to modify a file without the change being detected by -B. B will refuse to read data which does not pass the checksum, -and will log the error and return an IO error to the application. +If this is enabled, every block in every file is stored along with a +cryptographic checksum (Message Authentication Code). This makes it virtually +impossible to modify a file without the change being detected by B. +B will refuse to read data which does not pass the checksum, and will +log the error and return an IO error to the application. This adds substantial overhead (default being 8 bytes per filesystem block), plus computational overhead, and is not enabled by default except in paranoia @@ -498,28 +483,6 @@ filesystem contents along with the algorithms B supports to thwart them: =over 4 -=item B: modifying a few bytes of an encrypted file (without knowing -what they will decode to). - -B does not use any form of XOR encryption which would allow -single bytes to be modified without affecting others. Most modifications -would affect dozens or more bytes. Additionally, MAC Block headers can be -used to identify any changes to files. - -=item B: copying a random block of one file to a random block of another file. - -Each block has its own [deterministic] initialization vector. - -=item B: copying block N to block N of another file. - -When the Per-File Initialization Vector support is enabled (default -in 1.1.x filesystems), a copied block will not decode properly when copied to -another file. - -=item B: copying an entire file to another file. - -Can be prevented by enabling External IV Chaining mode. - =item B: determine if two filenames are the same by looking at encrypted names. Filename Initialization Vector chaining prevents this by giving each file a @@ -529,8 +492,39 @@ Filename Initialization Vector chaining prevents this by giving each file a Per-File Initialization Vector support prevents this. +=item B: copying an entire file to another file. + +Can be prevented by enabling External IV Chaining mode. + +=item B: copying a random block of one file to a random block of another file. + +Each block has its own [deterministic] initialization vector. + +=item B: copying block N to block N of another file. + +When the Per-File Initialization Vector support is enabled (the default), a +copied block will not decode properly when copied to another file. + +=item B: modifying a few bytes of an encrypted file (without knowing +what they will decode to). + +B does not use any form of XOR encryption which would allow +single bytes to be modified without affecting others. Most modifications +would affect dozens or more bytes. Additionally, MAC Block headers can be +used to identify any changes to files. + =back +=head1 LICENSE + +EncFS is free software; you can distribute it and/or modify it under the terms +of the GNU General Public License (GPL), as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +The library portion of EncFS is licensed under the LGPL version 3. See the +COPYING files in the source distribution for details. + =head1 DISCLAIMER This library is distributed in the hope that it will be useful, but WITHOUT ANY diff --git a/encfs/encfsctl.cpp b/encfs/encfsctl.cpp deleted file mode 100644 index c208769..0000000 --- a/encfs/encfsctl.cpp +++ /dev/null @@ -1,853 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2004, Valient Gough - * - * This program is free software; you can distribute it and/or modify it under - * the terms of the GNU General Public License (GPL), as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ - - -#include "encfs.h" - -#include "autosprintf.h" -#include "config.h" - -#include "FileUtils.h" -#include "Cipher.h" - -#include "Context.h" -#include "FileNode.h" -#include "DirNode.h" - -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#ifdef __FreeBSD__ -#include -#endif - -#include "i18n.h" - -#ifdef HAVE_SSL -#define NO_DES -#include -#endif - -using namespace rlog; -using namespace std; -using namespace gnu; - - -static int showInfo( int argc, char **argv ); -static int showVersion( int argc, char **argv ); -static int chpasswd( int argc, char **argv ); -static int chpasswdAutomaticly( int argc, char **argv ); -static int cmd_ls( int argc, char **argv ); -static int cmd_decode( int argc, char **argv ); -static int cmd_encode( int argc, char **argv ); -static int cmd_showcruft( int argc, char **argv ); -static int cmd_cat( int argc, char **argv ); -static int cmd_export( int argc, char **argv ); -static int cmd_showKey( int argc, char **argv ); - -struct CommandOpts -{ - const char *name; - int minOptions; - int maxOptions; - int (*func)(int argc, char **argv); - const char *argStr; - const char *usageStr; -} commands[] = -{ - {"info", 1, 1, showInfo, "(root dir)", - // xgroup(usage) - gettext_noop(" -- show information (Default command)")}, - {"showKey", 1, 1, cmd_showKey, "(root dir)", - // xgroup(usage) - gettext_noop(" -- show key")}, - {"passwd", 1, 1, chpasswd, "(root dir)", - // xgroup(usage) - gettext_noop(" -- change password for volume")}, - {"autopasswd", 1, 1, chpasswdAutomaticly, "(root dir)", - // xgroup(usage) - gettext_noop(" -- change password for volume, taking password" - " from standard input.\n\tNo prompts are issued.")}, - {"ls", 1, 2, cmd_ls, 0,0}, - {"showcruft", 1, 1, cmd_showcruft, "(root dir)", - // xgroup(usage) - gettext_noop(" -- show undecodable filenames in the volume")}, - {"cat", 2, 2, cmd_cat, "(root dir) path", - // xgroup(usage) - gettext_noop(" -- decodes the file and cats it to standard out")}, - {"decode", 1, 100, cmd_decode, "[--extpass=prog] (root dir) [encoded-name ...]", - // xgroup(usage) - gettext_noop(" -- decodes name and prints plaintext version")}, - {"encode", 1, 100, cmd_encode, "[--extpass=prog] (root dir) [plaintext-name ...]", - // xgroup(usage) - gettext_noop(" -- encodes a filename and print result")}, - {"export", 2, 2, cmd_export, "(root dir) path", - // xgroup(usage) - gettext_noop(" -- decrypts a volume and writes results to path")}, - {"--version", 0, 0, showVersion, "", - // xgroup(usage) - gettext_noop(" -- print version number and exit")}, - {0,0,0,0,0,0} -}; - - - -static -void usage(const char *name) -{ - cerr << autosprintf(_("encfsctl version %s"), VERSION) << "\n" - << _("Usage:\n") - // displays usage commands, eg "./encfs (root dir) ..." - // xgroup(usage) - << autosprintf(_("%s (root dir)\n" - " -- displays information about the filesystem, or \n"), name); - - int offset = 0; - while(commands[offset].name != 0) - { - if( commands[offset].argStr != 0 ) - { - cerr << "encfsctl " << commands[offset].name << " " - << commands[offset].argStr << "\n" - << gettext( commands[offset].usageStr ) << "\n"; - } - ++offset; - } - - cerr << "\n" - // xgroup(usage) - << autosprintf(_("Example: \n%s info ~/.crypt\n"), name) - << "\n"; -} - -static bool checkDir( string &rootDir ) -{ - if( !isDirectory( rootDir.c_str() )) - { - cerr << autosprintf(_("directory %s does not exist.\n"), - rootDir.c_str()); - return false; - } - if(rootDir[ rootDir.length()-1 ] != '/') - rootDir.append("/"); - - return true; -} - -static int showVersion( int argc, char **argv ) -{ - (void)argc; - (void)argv; - // xgroup(usage) - cerr << autosprintf(_("encfsctl version %s"), VERSION) << "\n"; - - return EXIT_SUCCESS; -} - -static int showInfo( int argc, char **argv ) -{ - (void)argc; - string rootDir = argv[1]; - if( !checkDir( rootDir )) - return EXIT_FAILURE; - - EncfsConfig config; - ConfigType type = readConfig( rootDir, config ); - - // show information stored in config.. - switch(type) - { - case Config_None: - // xgroup(diag) - cout << _("Unable to load or parse config file\n"); - return EXIT_FAILURE; - case Config_Prehistoric: - // xgroup(diag) - cout << _("A really old EncFS filesystem was found. \n" - "It is not supported in this EncFS build.\n"); - return EXIT_FAILURE; - case Config_V3: - // xgroup(diag) - cout << "\n" << autosprintf(_("Version 3 configuration; " - "created by %s\n"), config.creator().c_str()); - break; - case Config_V4: - // xgroup(diag) - cout << "\n" << autosprintf(_("Version 4 configuration; " - "created by %s\n"), config.creator().c_str()); - break; - case Config_V5: - case Config_V6: - case Config_V7: - // xgroup(diag) - cout << "\n" << autosprintf(_("Version %i configuration; " - "created by %s (revision %i)\n"), - type, - config.creator().c_str(), - config.revision()); - break; - } - - showFSInfo( config ); - - return EXIT_SUCCESS; -} - -static RootPtr initRootInfo(int &argc, char ** &argv) -{ - RootPtr result; - shared_ptr opts( new EncFS_Opts() ); - opts->createIfNotFound = false; - opts->checkKey = false; - - static struct option long_options[] = { - {"extpass", 1, 0, 'p'}, - {0,0,0,0} - }; - - for(;;) - { - int option_index = 0; - - int res = getopt_long( argc, argv, "", - long_options, &option_index); - if(res == -1) - break; - - switch(res) - { - case 'p': - opts->passwordProgram.assign(optarg); - break; - default: - rWarning(_("getopt error: %i"), res); - break; - } - } - - argc -= optind; - argv += optind; - - if(argc == 0) - { - cerr << _("Incorrect number of arguments") << "\n"; - } else - { - opts->rootDir = string( argv[0] ); - - --argc; - ++argv; - - if(checkDir( opts->rootDir )) - result = initFS( NULL, opts ); - - if(!result) - cerr << _("Unable to initialize encrypted filesystem - check path.\n"); - } - - return result; -} - -static RootPtr initRootInfo(const char* crootDir) -{ - string rootDir(crootDir); - RootPtr result; - - if(checkDir( rootDir )) - { - shared_ptr opts( new EncFS_Opts() ); - opts->rootDir = rootDir; - opts->createIfNotFound = false; - opts->checkKey = false; - result = initFS( NULL, opts ); - } - - if(!result) - cerr << _("Unable to initialize encrypted filesystem - check path.\n"); - - return result; -} - -static int cmd_showKey( int argc, char **argv ) -{ - RootPtr rootInfo = initRootInfo(argv[1]); - - if(!rootInfo) - return EXIT_FAILURE; - else - { - // encode with itself - string b64Key = rootInfo->cipher->encodeAsString( - rootInfo->volumeKey, rootInfo->volumeKey ); - - cout << b64Key << "\n"; - - return EXIT_SUCCESS; - } -} - -static int cmd_decode( int argc, char **argv ) -{ - RootPtr rootInfo = initRootInfo(argc, argv); - if(!rootInfo) - return EXIT_FAILURE; - - if(argc > 0) - { - for(int i=0; iroot->plainPath( argv[i] ); - cout << name << "\n"; - } - } else - { - char buf[PATH_MAX+1]; - while(cin.getline(buf,PATH_MAX)) - { - cout << rootInfo->root->plainPath( buf ) << "\n"; - } - } - return EXIT_SUCCESS; -} - -static int cmd_encode( int argc, char **argv ) -{ - RootPtr rootInfo = initRootInfo(argc, argv); - if(!rootInfo) - return EXIT_FAILURE; - - if(argc > 0) - { - for(int i=0; iroot->cipherPathWithoutRoot(argv[i]); - cout << name << "\n"; - } - } else - { - char buf[PATH_MAX+1]; - while(cin.getline(buf,PATH_MAX)) - { - cout << rootInfo->root->cipherPathWithoutRoot( buf ) << "\n"; - } - } - return EXIT_SUCCESS; -} - -static int cmd_ls( int argc, char **argv ) -{ - (void)argc; - - RootPtr rootInfo = initRootInfo(argv[1]); - - if(!rootInfo) - return EXIT_FAILURE; - - // show files in directory - { - DirTraverse dt = rootInfo->root->openDir("/"); - if(dt.valid()) - { - for(string name = dt.nextPlaintextName(); !name.empty(); - name = dt.nextPlaintextName()) - { - shared_ptr fnode = - rootInfo->root->lookupNode( name.c_str(), "encfsctl-ls" ); - struct stat stbuf; - fnode->getAttr( &stbuf ); - - struct tm stm; - localtime_r( &stbuf.st_mtime, &stm ); - stm.tm_year += 1900; - // TODO: when I add "%s" to the end and name.c_str(), I get a - // seg fault from within strlen. Why ??? - printf("%11i %4i-%02i-%02i %02i:%02i:%02i %s\n", - int(stbuf.st_size), - int(stm.tm_year), int(stm.tm_mon), int(stm.tm_mday), - int(stm.tm_hour), int(stm.tm_min), int(stm.tm_sec), - name.c_str()); - } - } - } - - return EXIT_SUCCESS; -} - -// apply an operation to every block in the file -template -int processContents( const shared_ptr &rootInfo, - const char *path, T &op ) -{ - int errCode = 0; - shared_ptr node = rootInfo->root->openNode( path, "encfsctl", - O_RDONLY, &errCode ); - - if(!node) - { - // try treating filename as an enciphered path - string plainName = rootInfo->root->plainPath( path ); - node = rootInfo->root->lookupNode( plainName.c_str(), "encfsctl" ); - if(node) - { - errCode = node->open( O_RDONLY ); - if(errCode < 0) - node.reset(); - } - } - - if(!node) - { - cerr << "unable to open " << path << "\n"; - return errCode; - } else - { - unsigned char buf[512]; - int blocks = (node->getSize() + sizeof(buf)-1) / sizeof(buf); - // read all the data in blocks - for(int i=0; iread(i*sizeof(buf), buf, sizeof(buf)); - int res = op(buf, bytes); - if(res < 0) - return res; - } - } - return 0; -} - -class WriteOutput -{ - int _fd; -public: - WriteOutput(int fd) { _fd = fd; } - ~WriteOutput() { close(_fd); } - - int operator()(const void *buf, int count) - { - return (int)write(_fd, buf, count); - } -}; - -static int cmd_cat( int argc, char **argv ) -{ - (void)argc; - RootPtr rootInfo = initRootInfo(argv[1]); - - if(!rootInfo) - return EXIT_FAILURE; - - const char *path = argv[2]; - WriteOutput output(STDOUT_FILENO); - int errCode = processContents( rootInfo, path, output ); - - return errCode; -} - -static int copyLink(const struct stat &stBuf, - const shared_ptr &rootInfo, - const string &cpath, const string &destName ) -{ - vector buf(stBuf.st_size+1, 0); - int res = ::readlink( cpath.c_str(), &buf[0], stBuf.st_size ); - if(res == -1) - { - cerr << "unable to readlink of " << cpath << "\n"; - return EXIT_FAILURE; - } - - buf[res] = '\0'; - string decodedLink = rootInfo->root->plainPath(&buf[0]); - - res = ::symlink( decodedLink.c_str(), destName.c_str() ); - if(res == -1) - { - cerr << "unable to create symlink for " << cpath - << " to " << decodedLink << "\n"; - } - - return EXIT_SUCCESS; -} - -static int copyContents(const shared_ptr &rootInfo, - const char* encfsName, const char* targetName) -{ - shared_ptr node = - rootInfo->root->lookupNode( encfsName, "encfsctl" ); - - if(!node) - { - cerr << "unable to open " << encfsName << "\n"; - return EXIT_FAILURE; - } else - { - struct stat st; - - if(node->getAttr(&st) != 0) - return EXIT_FAILURE; - - if((st.st_mode & S_IFLNK) == S_IFLNK) - { - string d = rootInfo->root->cipherPath(encfsName); - char linkContents[PATH_MAX+2]; - - if(readlink (d.c_str(), linkContents, PATH_MAX + 1) <= 0) - { - cerr << "unable to read link " << encfsName << "\n"; - return EXIT_FAILURE; - } - symlink(rootInfo->root->plainPath(linkContents).c_str(), - targetName); - } else - { - int outfd = creat(targetName, st.st_mode); - - WriteOutput output(outfd); - processContents( rootInfo, encfsName, output ); - } - } - return EXIT_SUCCESS; -} - -static bool endsWith(const string &str, char ch) -{ - if(str.empty()) - return false; - else - return str[str.length()-1] == ch; -} - -static int traverseDirs(const shared_ptr &rootInfo, - string volumeDir, string destDir) -{ - if(!endsWith(volumeDir, '/')) - volumeDir.append("/"); - if(!endsWith(destDir, '/')) - destDir.append("/"); - - // Lookup directory node so we can create a destination directory - // with the same permissions - { - struct stat st; - shared_ptr dirNode = - rootInfo->root->lookupNode( volumeDir.c_str(), "encfsctl" ); - if(dirNode->getAttr(&st)) - return EXIT_FAILURE; - - mkdir(destDir.c_str(), st.st_mode); - } - - // show files in directory - DirTraverse dt = rootInfo->root->openDir(volumeDir.c_str()); - if(dt.valid()) - { - for(string name = dt.nextPlaintextName(); !name.empty(); - name = dt.nextPlaintextName()) - { - // Recurse to subdirectories - if(name != "." && name != "..") - { - string plainPath = volumeDir + name; - string cpath = rootInfo->root->cipherPath(plainPath.c_str()); - string destName = destDir + name; - - int r = EXIT_SUCCESS; - struct stat stBuf; - if( !lstat( cpath.c_str(), &stBuf )) - { - if( S_ISDIR( stBuf.st_mode ) ) - { - traverseDirs(rootInfo, (plainPath + '/').c_str(), - destName + '/'); - } else if( S_ISLNK( stBuf.st_mode )) - { - r = copyLink( stBuf, rootInfo, cpath, destName ); - } else - { - r = copyContents(rootInfo, plainPath.c_str(), - destName.c_str()); - } - } else - { - r = EXIT_FAILURE; - } - - if(r != EXIT_SUCCESS) - return r; - } - } - } - return EXIT_SUCCESS; -} - -static int cmd_export( int argc, char **argv ) -{ - (void)argc; - - RootPtr rootInfo = initRootInfo(argv[1]); - - if(!rootInfo) - return EXIT_FAILURE; - - string destDir = argv[2]; - // if the dir doesn't exist, then create it (with user permission) - if(!checkDir(destDir) && !userAllowMkdir(destDir.c_str(), 0700)) - return EXIT_FAILURE; - - return traverseDirs(rootInfo, "/", destDir); -} - -int showcruft( const shared_ptr &rootInfo, const char *dirName ) -{ - int found = 0; - DirTraverse dt = rootInfo->root->openDir( dirName ); - if(dt.valid()) - { - bool showedDir = false; - for(string name = dt.nextInvalid(); !name.empty(); - name = dt.nextInvalid()) - { - string cpath = rootInfo->root->cipherPath( dirName ); - cpath += '/'; - cpath += name; - - if(!showedDir) - { - // just before showing a list of files in a directory - cout << autosprintf(_("In directory %s: \n"), dirName); - showedDir = true; - } - ++found; - cout << cpath << "\n"; - } - - // now go back and look for directories to recurse into.. - dt = rootInfo->root->openDir( dirName ); - if(dt.valid()) - { - for(string name = dt.nextPlaintextName(); !name.empty(); - name = dt.nextPlaintextName()) - { - if( name == "." || name == "..") - continue; - - string plainPath = dirName; - plainPath += '/'; - plainPath += name; - - string cpath = rootInfo->root->cipherPath( plainPath.c_str() ); - - if(isDirectory( cpath.c_str() )) - found += showcruft( rootInfo, plainPath.c_str() ); - } - } - } - - return found; -} - -/* - iterate recursively through the filesystem and print out names of files - which have filenames which cannot be decoded with the given key.. -*/ -static int cmd_showcruft( int argc, char **argv ) -{ - (void)argc; - - RootPtr rootInfo = initRootInfo(argv[1]); - - if(!rootInfo) - return EXIT_FAILURE; - - int filesFound = showcruft( rootInfo, "/" ); - - cerr << autosprintf("Found %i invalid file(s).", filesFound) << "\n"; - - return EXIT_SUCCESS; -} - -static int do_chpasswd( bool useStdin, bool annotate, int argc, char **argv ) -{ - (void)argc; - string rootDir = argv[1]; - if( !checkDir( rootDir )) - return EXIT_FAILURE; - - EncfsConfig config; - ConfigType cfgType = readConfig( rootDir, config ); - - if(cfgType == Config_None) - { - cout << _("Unable to load or parse config file\n"); - return EXIT_FAILURE; - } - - // instanciate proper cipher - shared_ptr cipher = getCipher(config); - if(!cipher) - { - cout << autosprintf(_("Unable to find specified cipher \"%s\"\n"), - config.cipher().name().c_str()); - return EXIT_FAILURE; - } - - // ask for existing password - cout << _("Enter current Encfs password\n"); - if (annotate) - cerr << "$PROMPT$ passwd" << endl; - CipherKey userKey = getUserKey( config, useStdin ); - if(!userKey) - return EXIT_FAILURE; - - // decode volume key using user key -- at this point we detect an incorrect - // password if the key checksum does not match (causing readKey to fail). - CipherKey volumeKey = cipher->readKey( - (const unsigned char *)config.key().ciphertext().data(), userKey ); - - if(!volumeKey) - { - cout << _("Invalid password\n"); - return EXIT_FAILURE; - } - - // Now, get New user key.. - userKey.reset(); - cout << _("Enter new Encfs password\n"); - - // create new key - if( useStdin ) - { - if (annotate) - cerr << "$PROMPT$ new_passwd" << endl; - } - - userKey = getNewUserKey( config, useStdin, string(), string() ); - - // re-encode the volume key using the new user key and write it out.. - int result = EXIT_FAILURE; - if(userKey) - { - int encodedKeySize = cipher->encodedKeySize(); - unsigned char *keyBuf = new unsigned char[ encodedKeySize ]; - - // encode volume key with new user key - cipher->writeKey( volumeKey, keyBuf, userKey ); - userKey.reset(); - - EncryptedKey *key = config.mutable_key(); - key->set_ciphertext( keyBuf, encodedKeySize ); - delete[] keyBuf; - - if(saveConfig( rootDir, config )) - { - // password modified -- changes volume key of filesystem.. - cout << _("Volume Key successfully updated.\n"); - result = EXIT_SUCCESS; - } else - { - cout << _("Error saving modified config file.\n"); - } - } else - { - cout << _("Error creating key\n"); - } - - volumeKey.reset(); - - return result; -} - -static int chpasswd( int argc, char **argv ) -{ - return do_chpasswd( false, false, argc, argv ); -} - -static int chpasswdAutomaticly( int argc, char **argv ) -{ - return do_chpasswd( true, false, argc, argv ); -} - - -int main(int argc, char **argv) -{ - RLogInit( argc, argv ); - -#ifdef LOCALEDIR - setlocale( LC_ALL, "" ); - bindtextdomain( PACKAGE, LOCALEDIR ); - textdomain( PACKAGE ); -#endif - -#ifdef HAVE_SSL - SSL_load_error_strings(); - SSL_library_init(); -#endif - - StdioNode *slog = new StdioNode( STDERR_FILENO ); - slog->subscribeTo( GetGlobalChannel("error") ); - slog->subscribeTo( GetGlobalChannel("warning") ); -#ifndef NO_DEBUG - slog->subscribeTo( GetGlobalChannel("debug") ); -#endif - - if(argc < 2) - { - usage( argv[0] ); - return EXIT_FAILURE; - } - - if(argc == 2 && !(*argv[1] == '-' && *(argv[1]+1) == '-')) - { - // default command when only 1 argument given -- treat the argument as - // a directory.. - return showInfo( argc, argv ); - } else - { - // find the specified command - int offset = 0; - while(commands[offset].name != 0) - { - if(!strcmp( argv[1], commands[offset].name )) - break; - ++offset; - } - - if(commands[offset].name == 0) - { - cerr << autosprintf(_("invalid command: \"%s\""), argv[1]) << "\n"; - } else - { - if((argc-2 < commands[offset].minOptions) || - (argc-2 > commands[offset].maxOptions)) - { - cerr << autosprintf( - _("Incorrect number of arguments for command \"%s\""), - argv[1]) << "\n"; - } else - return (*commands[offset].func)( argc-1, argv+1 ); - } - } - - return EXIT_FAILURE; -} diff --git a/encfs/main.cpp b/encfs/main.cpp index 66911ca..b7f2ecb 100644 --- a/encfs/main.cpp +++ b/encfs/main.cpp @@ -16,9 +16,7 @@ * */ -#include "encfs.h" -#include "config.h" -#include "autosprintf.h" +#include "fs/encfs.h" #include #include @@ -33,25 +31,24 @@ #include -#include -#include -#include -#include -#include +#include -#include "ConfigReader.h" -#include "Interface.h" -#include "MemoryPool.h" -#include "FileUtils.h" -#include "DirNode.h" -#include "Context.h" +#include "base/config.h" +#include "base/autosprintf.h" +#include "base/ConfigReader.h" +#include "base/Error.h" +#include "base/Interface.h" +#include "base/i18n.h" -#include "openssl.h" +#include "cipher/MemoryPool.h" +#include "cipher/openssl.h" + +#include "fs/FileUtils.h" +#include "fs/DirNode.h" +#include "fs/Context.h" #include -#include "i18n.h" - // Fuse version >= 26 requires another argument to fuse_unmount, which we // don't have. So use the backward compatible call instead.. extern "C" void fuse_unmount_compat22(const char *mountpoint); @@ -60,12 +57,11 @@ extern "C" void fuse_unmount_compat22(const char *mountpoint); #ifndef MAX inline static int MAX(int a, int b) { - return (a > b) ? a : b; + return (a > b) ? a : b; } #endif using namespace std; -using namespace rlog; using namespace gnu; // Maximum number of arguments that we're going to pass on to fuse. Doesn't @@ -73,44 +69,44 @@ using namespace gnu; const int MaxFuseArgs = 32; struct EncFS_Args { - string mountPoint; // where to make filesystem visible - bool isDaemon; // true == spawn in background, log to syslog - bool isThreaded; // true == threaded - bool isVerbose; // false == only enable warning/error messages - int idleTimeout; // 0 == idle time in minutes to trigger unmount - const char *fuseArgv[MaxFuseArgs]; - int fuseArgc; + string mountPoint; // where to make filesystem visible + bool isDaemon; // true == spawn in background, log to syslog + bool isThreaded; // true == threaded + bool isVerbose; // false == only enable warning/error messages + int idleTimeout; // 0 == idle time in minutes to trigger unmount + const char *fuseArgv[MaxFuseArgs]; + int fuseArgc; - shared_ptr opts; + shared_ptr opts; - // for debugging - // In case someone sends me a log dump, I want to know how what options are - // in effect. Not internationalized, since it is something that is mostly - // useful for me! - string toString() - { - ostringstream ss; - ss << (isDaemon ? "(daemon) " : "(fg) "); - ss << (isThreaded ? "(threaded) " : "(UP) "); - if(idleTimeout > 0) - ss << "(timeout " << idleTimeout << ") "; - if(opts->checkKey) ss << "(keyCheck) "; - if(opts->forceDecode) ss << "(forceDecode) "; - if(opts->ownerCreate) ss << "(ownerCreate) "; - if(opts->useStdin) ss << "(useStdin) "; - if(opts->annotate) ss << "(annotate) "; - if(opts->reverseEncryption) ss << "(reverseEncryption) "; - if(opts->mountOnDemand) ss << "(mountOnDemand) "; - for(int i=0; i 0) + ss << "(timeout " << idleTimeout << ") "; + if(opts->checkKey) ss << "(keyCheck) "; + if(opts->forceDecode) ss << "(forceDecode) "; + if(opts->ownerCreate) ss << "(ownerCreate) "; + if(opts->useStdin) ss << "(useStdin) "; + if(opts->annotate) ss << "(annotate) "; + if(opts->reverseEncryption) ss << "(reverseEncryption) "; + if(opts->mountOnDemand) ss << "(mountOnDemand) "; + for(int i=0; i(argv), (fuse_operations*)NULL, NULL); + int argc = 2; + const char *argv[] = {"...", "-h"}; + fuse_main( argc, const_cast(argv), (fuse_operations*)NULL, NULL); } #define PUSHARG(ARG) do { \ -rAssert(out->fuseArgc < MaxFuseArgs); \ -out->fuseArgv[out->fuseArgc++] = (ARG); } while(0) + rAssert(out->fuseArgc < MaxFuseArgs); \ + out->fuseArgv[out->fuseArgc++] = (ARG); } \ +while(0) static string slashTerminate( const string &src ) { - string result = src; - if( result[ result.length()-1 ] != '/' ) - result.append( "/" ); - return result; + string result = src; + if( result[ result.length()-1 ] != '/' ) + result.append( "/" ); + return result; } static bool processArgs(int argc, char *argv[], const shared_ptr &out) { - // set defaults - out->isDaemon = true; - out->isThreaded = true; - out->isVerbose = false; - out->idleTimeout = 0; - out->fuseArgc = 0; - out->opts->idleTracking = false; - out->opts->checkKey = true; - out->opts->forceDecode = false; - out->opts->ownerCreate = false; - out->opts->useStdin = false; - out->opts->annotate = false; - out->opts->reverseEncryption = false; - - bool useDefaultFlags = true; + // set defaults + out->isDaemon = true; + out->isThreaded = true; + out->isVerbose = false; + out->idleTimeout = 0; + out->fuseArgc = 0; + out->opts->idleTracking = false; + out->opts->checkKey = true; + out->opts->forceDecode = false; + out->opts->ownerCreate = false; + out->opts->useStdin = false; + out->opts->annotate = false; + out->opts->reverseEncryption = false; - // pass executable name through - out->fuseArgv[0] = lastPathElement(argv[0]); - ++out->fuseArgc; + bool useDefaultFlags = true; - // leave a space for mount point, as FUSE expects the mount point before - // any flags - out->fuseArgv[1] = NULL; - ++out->fuseArgc; - - // TODO: can flags be internationalized? - static struct option long_options[] = { - {"fuse-debug", 0, 0, 'd'}, // Fuse debug mode - {"forcedecode", 0, 0, 'D'}, // force decode - // {"foreground", 0, 0, 'f'}, // foreground mode (no daemon) - {"fuse-help", 0, 0, 'H'}, // fuse_mount usage - {"idle", 1, 0, 'i'}, // idle timeout - {"anykey", 0, 0, 'k'}, // skip key checks - {"no-default-flags", 0, 0, 'N'}, // don't use default fuse flags - {"ondemand", 0, 0, 'm'}, // mount on-demand - {"public", 0, 0, 'P'}, // public mode - {"extpass", 1, 0, 'p'}, // external password program - // {"single-thread", 0, 0, 's'}, // single-threaded mode - {"stdinpass", 0, 0, 'S'}, // read password from stdin - {"annotate", 0, 0, 513}, // Print annotation lines to stderr - {"verbose", 0, 0, 'v'}, // verbose mode - {"version", 0, 0, 'V'}, //version - {"reverse", 0, 0, 'r'}, // reverse encryption - {"standard", 0, 0, '1'}, // standard configuration - {"paranoia", 0, 0, '2'}, // standard configuration - {0,0,0,0} - }; + // pass executable name through + out->fuseArgv[0] = lastPathElement(argv[0]); + ++out->fuseArgc; - while (1) + // leave a space for mount point, as FUSE expects the mount point before + // any flags + out->fuseArgv[1] = NULL; + ++out->fuseArgc; + + // TODO: can flags be internationalized? + static struct option long_options[] = { + {"fuse-debug", 0, 0, 'd'}, // Fuse debug mode + {"forcedecode", 0, 0, 'D'}, // force decode + // {"foreground", 0, 0, 'f'}, // foreground mode (no daemon) + {"fuse-help", 0, 0, 'H'}, // fuse_mount usage + {"idle", 1, 0, 'i'}, // idle timeout + {"anykey", 0, 0, 'k'}, // skip key checks + {"no-default-flags", 0, 0, 'N'}, // don't use default fuse flags + {"ondemand", 0, 0, 'm'}, // mount on-demand + {"public", 0, 0, 'P'}, // public mode + {"extpass", 1, 0, 'p'}, // external password program + // {"single-thread", 0, 0, 's'}, // single-threaded mode + {"stdinpass", 0, 0, 'S'}, // read password from stdin + {"annotate", 0, 0, 513}, // Print annotation lines to stderr + {"verbose", 0, 0, 'v'}, // verbose mode + {"version", 0, 0, 'V'}, //version + {"reverse", 0, 0, 'r'}, // reverse encryption + {"standard", 0, 0, '1'}, // standard configuration + {"paranoia", 0, 0, '2'}, // standard configuration + {0,0,0,0} + }; + + while (1) + { + int option_index = 0; + + // 's' : single-threaded mode + // 'f' : foreground mode + // 'v' : verbose mode (same as --verbose) + // 'd' : fuse debug mode (same as --fusedebug) + // 'i' : idle-timeout, takes argument + // 'm' : mount-on-demand + // 'S' : password from stdin + // 'o' : arguments meant for fuse + int res = getopt_long( argc, argv, "HsSfvVdmi:o:", + long_options, &option_index); + + if(res == -1) + break; + + switch( res ) { - int option_index = 0; - - // 's' : single-threaded mode - // 'f' : foreground mode - // 'v' : verbose mode (same as --verbose) - // 'd' : fuse debug mode (same as --fusedebug) - // 'i' : idle-timeout, takes argument - // 'm' : mount-on-demand - // 'S' : password from stdin - // 'o' : arguments meant for fuse - int res = getopt_long( argc, argv, "HsSfvVdmi:o:", - long_options, &option_index); - - if(res == -1) - break; - - switch( res ) - { - case '1': - out->opts->configMode = Config_Standard; - break; - case '2': - out->opts->configMode = Config_Paranoia; - break; - case 's': - out->isThreaded = false; - break; - case 'S': - out->opts->useStdin = true; - break; - case 513: - out->opts->annotate = true; - break; - case 'f': - out->isDaemon = false; - // this option was added in fuse 2.x - PUSHARG("-f"); - break; - case 'v': - out->isVerbose = true; - break; - case 'd': - PUSHARG("-d"); - break; - case 'i': - out->idleTimeout = strtol( optarg, (char**)NULL, 10); - out->opts->idleTracking = true; - break; - case 'k': - out->opts->checkKey = false; - break; - case 'D': - out->opts->forceDecode = true; - break; - case 'r': - out->opts->reverseEncryption = true; - break; - case 'm': - out->opts->mountOnDemand = true; - break; - case 'N': - useDefaultFlags = false; - break; - case 'o': - PUSHARG("-o"); - PUSHARG( optarg ); - break; - case 'p': - out->opts->passwordProgram.assign( optarg ); - break; - case 'P': - if(geteuid() != 0) - rWarning(_("option '--public' ignored for non-root user")); - else - { - out->opts->ownerCreate = true; - // add 'allow_other' option - // add 'default_permissions' option (default) - PUSHARG("-o"); - PUSHARG("allow_other"); - } - break; - case 'V': - // xgroup(usage) - cerr << autosprintf(_("encfs version %s"), VERSION) << endl; - exit(EXIT_SUCCESS); - break; - case 'H': - FuseUsage(); - exit(EXIT_SUCCESS); - break; - case '?': - // invalid options.. - break; - case ':': - // missing parameter for option.. - break; - default: - rWarning(_("getopt error: %i"), res); - break; - } + case '1': + out->opts->configMode = Config_Standard; + break; + case '2': + out->opts->configMode = Config_Paranoia; + break; + case 's': + out->isThreaded = false; + break; + case 'S': + out->opts->useStdin = true; + break; + case 513: + out->opts->annotate = true; + break; + case 'f': + out->isDaemon = false; + // this option was added in fuse 2.x + PUSHARG("-f"); + break; + case 'v': + out->isVerbose = true; + break; + case 'd': + PUSHARG("-d"); + break; + case 'i': + out->idleTimeout = strtol( optarg, (char**)NULL, 10); + out->opts->idleTracking = true; + break; + case 'k': + out->opts->checkKey = false; + break; + case 'D': + out->opts->forceDecode = true; + break; + case 'r': + out->opts->reverseEncryption = true; + break; + case 'm': + out->opts->mountOnDemand = true; + break; + case 'N': + useDefaultFlags = false; + break; + case 'o': + PUSHARG("-o"); + PUSHARG( optarg ); + break; + case 'p': + out->opts->passwordProgram.assign( optarg ); + break; + case 'P': + if(geteuid() != 0) + LOG(WARNING) << "option '--public' ignored for non-root user"; + else + { + out->opts->ownerCreate = true; + // add 'allow_other' option + // add 'default_permissions' option (default) + PUSHARG("-o"); + PUSHARG("allow_other"); + } + break; + case 'V': + // xgroup(usage) + cerr << autosprintf(_("encfs version %s"), VERSION) << endl; + exit(EXIT_SUCCESS); + break; + case 'H': + FuseUsage(); + exit(EXIT_SUCCESS); + break; + case '?': + // invalid options.. + break; + case ':': + // missing parameter for option.. + break; + default: + LOG(WARNING) << "getopt error: " << res; + break; } + } - if(!out->isThreaded) - PUSHARG("-s"); + if(!out->isThreaded) + PUSHARG("-s"); - if(useDefaultFlags) + if(useDefaultFlags) + { + PUSHARG("-o"); + PUSHARG("use_ino"); + PUSHARG("-o"); + PUSHARG("default_permissions"); + } + + // we should have at least 2 arguments left over - the source directory and + // the mount point. + if(optind+2 <= argc) + { + out->opts->rootDir = slashTerminate( argv[optind++] ); + out->mountPoint = argv[optind++]; + } else + { + // no mount point specified + LOG(ERROR) << "Missing one or more arguments, aborting."; + return false; + } + + // If there are still extra unparsed arguments, pass them onto FUSE.. + if(optind < argc) + { + rAssert(out->fuseArgc < MaxFuseArgs); + + while(optind < argc) { - PUSHARG("-o"); - PUSHARG("use_ino"); - PUSHARG("-o"); - PUSHARG("default_permissions"); + rAssert(out->fuseArgc < MaxFuseArgs); + out->fuseArgv[out->fuseArgc++] = argv[optind]; + ++optind; } - - // we should have at least 2 arguments left over - the source directory and - // the mount point. - if(optind+2 <= argc) + } + + // sanity check + if(out->isDaemon && + (!isAbsolutePath( out->mountPoint.c_str() ) || + !isAbsolutePath( out->opts->rootDir.c_str() ) ) + ) + { + cerr << + // xgroup(usage) + _("When specifying daemon mode, you must use absolute paths " + "(beginning with '/')") + << endl; + return false; + } + + // the raw directory may not be a subdirectory of the mount point. + { + string testMountPoint = slashTerminate( out->mountPoint ); + string testRootDir = + out->opts->rootDir.substr(0, testMountPoint.length()); + + if( testMountPoint == testRootDir ) { - out->opts->rootDir = slashTerminate( argv[optind++] ); - out->mountPoint = argv[optind++]; - } else - { - // no mount point specified - rWarning(_("Missing one or more arguments, aborting.")); - return false; + cerr << + // xgroup(usage) + _("The raw directory may not be a subdirectory of the " + "mount point.") << endl; + return false; } + } - // If there are still extra unparsed arguments, pass them onto FUSE.. - if(optind < argc) - { - rAssert(out->fuseArgc < MaxFuseArgs); + if(out->opts->mountOnDemand && out->opts->passwordProgram.empty()) + { + cerr << + // xgroup(usage) + _("Must set password program when using mount-on-demand") + << endl; + return false; + } - while(optind < argc) - { - rAssert(out->fuseArgc < MaxFuseArgs); - out->fuseArgv[out->fuseArgc++] = argv[optind]; - ++optind; - } - } + // check that the directories exist, or that we can create them.. + if(!isDirectory( out->opts->rootDir.c_str() ) && + !userAllowMkdir( out->opts->annotate? 1:0, + out->opts->rootDir.c_str() ,0700)) + { + LOG(WARNING) << "Unable to locate root directory, aborting."; + return false; + } + if(!isDirectory( out->mountPoint.c_str() ) && + !userAllowMkdir( out->opts->annotate? 2:0, + out->mountPoint.c_str(),0700)) + { + LOG(WARNING) << "Unable to locate mount point, aborting."; + return false; + } - // sanity check - if(out->isDaemon && - (!isAbsolutePath( out->mountPoint.c_str() ) || - !isAbsolutePath( out->opts->rootDir.c_str() ) ) - ) - { - cerr << - // xgroup(usage) - _("When specifying daemon mode, you must use absolute paths " - "(beginning with '/')") - << endl; - return false; - } + // fill in mount path for fuse + out->fuseArgv[1] = out->mountPoint.c_str(); - // the raw directory may not be a subdirectory of the mount point. - { - string testMountPoint = slashTerminate( out->mountPoint ); - string testRootDir = - out->opts->rootDir.substr(0, testMountPoint.length()); - - if( testMountPoint == testRootDir ) - { - cerr << - // xgroup(usage) - _("The raw directory may not be a subdirectory of the " - "mount point.") << endl; - return false; - } - } - - if(out->opts->mountOnDemand && out->opts->passwordProgram.empty()) - { - cerr << - // xgroup(usage) - _("Must set password program when using mount-on-demand") - << endl; - return false; - } - - // check that the directories exist, or that we can create them.. - if(!isDirectory( out->opts->rootDir.c_str() ) && - !userAllowMkdir( out->opts->annotate? 1:0, - out->opts->rootDir.c_str() ,0700)) - { - rWarning(_("Unable to locate root directory, aborting.")); - return false; - } - if(!isDirectory( out->mountPoint.c_str() ) && - !userAllowMkdir( out->opts->annotate? 2:0, - out->mountPoint.c_str(),0700)) - { - rWarning(_("Unable to locate mount point, aborting.")); - return false; - } - - // fill in mount path for fuse - out->fuseArgv[1] = out->mountPoint.c_str(); - - return true; + return true; } static void * idleMonitor(void *); void *encfs_init(fuse_conn_info *conn) { - EncFS_Context *ctx = (EncFS_Context*)fuse_get_context()->private_data; + EncFS_Context *ctx = (EncFS_Context*)fuse_get_context()->private_data; - // set fuse connection options - conn->async_read = true; + // set fuse connection options + conn->async_read = true; - // if an idle timeout is specified, then setup a thread to monitor the - // filesystem. - if(ctx->args->idleTimeout > 0) + // if an idle timeout is specified, then setup a thread to monitor the + // filesystem. + if(ctx->args->idleTimeout > 0) + { + VLOG(1) << "starting idle monitoring thread"; + ctx->running = true; + + int res = pthread_create( &ctx->monitorThread, 0, idleMonitor, + (void*)ctx ); + if(res != 0) { - rDebug("starting idle monitoring thread"); - ctx->running = true; - - int res = pthread_create( &ctx->monitorThread, 0, idleMonitor, - (void*)ctx ); - if(res != 0) - { - rError("error starting idle monitor thread, " - "res = %i, errno = %i", res, errno); - } + LOG(ERROR) << "error starting idle monitor thread, " + "res = " << res << ", errno = " << errno; } + } - if(ctx->args->isDaemon && oldStderr >= 0) - { - rInfo("Closing stderr"); - close(oldStderr); - oldStderr = -1; - } + if(ctx->args->isDaemon && oldStderr >= 0) + { + VLOG(1) << "Closing stderr"; + close(oldStderr); + oldStderr = -1; + } - return (void*)ctx; + return (void*)ctx; } void encfs_destroy( void *_ctx ) { - EncFS_Context *ctx = (EncFS_Context*)_ctx; - if(ctx->args->idleTimeout > 0) - { - ctx->running = false; + EncFS_Context *ctx = (EncFS_Context*)_ctx; + if(ctx->args->idleTimeout > 0) + { + ctx->running = false; - // wake up the thread if it is waiting.. - rDebug("waking up monitoring thread"); - pthread_mutex_lock( &ctx->wakeupMutex ); - pthread_cond_signal( &ctx->wakeupCond ); - pthread_mutex_unlock( &ctx->wakeupMutex ); - rDebug("joining with idle monitoring thread"); - pthread_join( ctx->monitorThread , 0 ); - rDebug("join done"); - } + // wake up the thread if it is waiting.. + VLOG(1) << "waking up monitoring thread"; + pthread_mutex_lock( &ctx->wakeupMutex ); + pthread_cond_signal( &ctx->wakeupCond ); + pthread_mutex_unlock( &ctx->wakeupMutex ); + VLOG(1) << "joining with idle monitoring thread"; + pthread_join( ctx->monitorThread , 0 ); + VLOG(1) << "join done"; + } } int main(int argc, char *argv[]) { - // initialize the logging library - RLogInit( argc, argv ); + // log to stderr by default.. + FLAGS_logtostderr = 1; + FLAGS_minloglevel = 1; // WARNING and above. + + google::InitGoogleLogging(argv[0]); + google::InstallFailureSignalHandler(); #ifdef LOCALEDIR - setlocale( LC_ALL, "" ); - bindtextdomain( PACKAGE, LOCALEDIR ); - textdomain( PACKAGE ); + setlocale( LC_ALL, "" ); + bindtextdomain( PACKAGE, LOCALEDIR ); + textdomain( PACKAGE ); #endif - // log to stderr by default.. - shared_ptr slog( new StdioNode( STDERR_FILENO ) ); - shared_ptr logNode; + // anything that comes from the user should be considered tainted until + // we've processed it and only allowed through what we support. + shared_ptr encfsArgs( new EncFS_Args ); + for(int i=0; ifuseArgv[i] = NULL; // libfuse expects null args.. - // show error and warning output - slog->subscribeTo( GetGlobalChannel("error") ); - slog->subscribeTo( GetGlobalChannel("warning") ); + if(argc == 1 || !processArgs(argc, argv, encfsArgs)) + { + usage(argv[0]); + return EXIT_FAILURE; + } - // anything that comes from the user should be considered tainted until - // we've processed it and only allowed through what we support. - shared_ptr encfsArgs( new EncFS_Args ); - for(int i=0; ifuseArgv[i] = NULL; // libfuse expects null args.. + if(encfsArgs->isVerbose) + FLAGS_minloglevel = 0; - if(argc == 1 || !processArgs(argc, argv, encfsArgs)) - { - usage(argv[0]); - return EXIT_FAILURE; - } + LOG(INFO) << "Root directory: " << encfsArgs->opts->rootDir; + LOG(INFO) << "Fuse arguments: " << encfsArgs->toString(); - if(encfsArgs->isVerbose) - { - // subscribe to more logging channels.. - slog->subscribeTo( GetGlobalChannel("info") ); - slog->subscribeTo( GetGlobalChannel("debug") ); - } - - rDebug("Root directory: %s", encfsArgs->opts->rootDir.c_str()); - rDebug("Fuse arguments: %s", encfsArgs->toString().c_str()); - - fuse_operations encfs_oper; - // in case this code is compiled against a newer FUSE library and new - // members have been added to fuse_operations, make sure they get set to - // 0.. - memset(&encfs_oper, 0, sizeof(fuse_operations)); + fuse_operations encfs_oper; + // in case this code is compiled against a newer FUSE library and new + // members have been added to fuse_operations, make sure they get set to + // 0.. + memset(&encfs_oper, 0, sizeof(fuse_operations)); - encfs_oper.getattr = encfs_getattr; - encfs_oper.readlink = encfs_readlink; - encfs_oper.getdir = encfs_getdir; // deprecated for readdir - encfs_oper.mknod = encfs_mknod; - encfs_oper.mkdir = encfs_mkdir; - encfs_oper.unlink = encfs_unlink; - encfs_oper.rmdir = encfs_rmdir; - encfs_oper.symlink = encfs_symlink; - encfs_oper.rename = encfs_rename; - encfs_oper.link = encfs_link; - encfs_oper.chmod = encfs_chmod; - encfs_oper.chown = encfs_chown; - encfs_oper.truncate = encfs_truncate; - encfs_oper.utime = encfs_utime; // deprecated for utimens - encfs_oper.open = encfs_open; - encfs_oper.read = encfs_read; - encfs_oper.write = encfs_write; - encfs_oper.statfs = encfs_statfs; - encfs_oper.flush = encfs_flush; - encfs_oper.release = encfs_release; - encfs_oper.fsync = encfs_fsync; + encfs_oper.getattr = encfs_getattr; + encfs_oper.readlink = encfs_readlink; + encfs_oper.getdir = encfs_getdir; // deprecated for readdir + encfs_oper.mknod = encfs_mknod; + encfs_oper.mkdir = encfs_mkdir; + encfs_oper.unlink = encfs_unlink; + encfs_oper.rmdir = encfs_rmdir; + encfs_oper.symlink = encfs_symlink; + encfs_oper.rename = encfs_rename; + encfs_oper.link = encfs_link; + encfs_oper.chmod = encfs_chmod; + encfs_oper.chown = encfs_chown; + encfs_oper.truncate = encfs_truncate; + encfs_oper.utime = encfs_utime; // deprecated for utimens + encfs_oper.open = encfs_open; + encfs_oper.read = encfs_read; + encfs_oper.write = encfs_write; + encfs_oper.statfs = encfs_statfs; + encfs_oper.flush = encfs_flush; + encfs_oper.release = encfs_release; + encfs_oper.fsync = encfs_fsync; #ifdef HAVE_XATTR - encfs_oper.setxattr = encfs_setxattr; - encfs_oper.getxattr = encfs_getxattr; - encfs_oper.listxattr = encfs_listxattr; - encfs_oper.removexattr = encfs_removexattr; + encfs_oper.setxattr = encfs_setxattr; + encfs_oper.getxattr = encfs_getxattr; + encfs_oper.listxattr = encfs_listxattr; + encfs_oper.removexattr = encfs_removexattr; #endif // HAVE_XATTR - //encfs_oper.opendir = encfs_opendir; - //encfs_oper.readdir = encfs_readdir; - //encfs_oper.releasedir = encfs_releasedir; - //encfs_oper.fsyncdir = encfs_fsyncdir; - encfs_oper.init = encfs_init; - encfs_oper.destroy = encfs_destroy; - //encfs_oper.access = encfs_access; - //encfs_oper.create = encfs_create; - encfs_oper.ftruncate = encfs_ftruncate; - encfs_oper.fgetattr = encfs_fgetattr; - //encfs_oper.lock = encfs_lock; - encfs_oper.utimens = encfs_utimens; - //encfs_oper.bmap = encfs_bmap; + //encfs_oper.opendir = encfs_opendir; + //encfs_oper.readdir = encfs_readdir; + //encfs_oper.releasedir = encfs_releasedir; + //encfs_oper.fsyncdir = encfs_fsyncdir; + encfs_oper.init = encfs_init; + encfs_oper.destroy = encfs_destroy; + //encfs_oper.access = encfs_access; + //encfs_oper.create = encfs_create; + encfs_oper.ftruncate = encfs_ftruncate; + encfs_oper.fgetattr = encfs_fgetattr; + //encfs_oper.lock = encfs_lock; + encfs_oper.utimens = encfs_utimens; + //encfs_oper.bmap = encfs_bmap; #if (__FreeBSD__ >= 10) - // encfs_oper.setvolname - // encfs_oper.exchange - // encfs_oper.getxtimes - // encfs_oper.setbkuptime - // encfs_oper.setchgtime - // encfs_oper.setcrtime - // encfs_oper.chflags - // encfs_oper.setattr_x - // encfs_oper.fsetattr_x + // encfs_oper.setvolname + // encfs_oper.exchange + // encfs_oper.getxtimes + // encfs_oper.setbkuptime + // encfs_oper.setchgtime + // encfs_oper.setcrtime + // encfs_oper.chflags + // encfs_oper.setattr_x + // encfs_oper.fsetattr_x #endif - openssl_init( encfsArgs->isThreaded ); + openssl_init( encfsArgs->isThreaded ); - // context is not a smart pointer because it will live for the life of - // the filesystem. - EncFS_Context *ctx = new EncFS_Context; - ctx->publicFilesystem = encfsArgs->opts->ownerCreate; - RootPtr rootInfo = initFS( ctx, encfsArgs->opts ); - - int returnCode = EXIT_FAILURE; + // context is not a smart pointer because it will live for the life of + // the filesystem. + EncFS_Context *ctx = new EncFS_Context; + ctx->publicFilesystem = encfsArgs->opts->ownerCreate; + RootPtr rootInfo = initFS( ctx, encfsArgs->opts ); - if( rootInfo ) + int returnCode = EXIT_FAILURE; + + if( rootInfo ) + { + // set the globally visible root directory node + ctx->setRoot( rootInfo->root ); + ctx->args = encfsArgs; + ctx->opts = encfsArgs->opts; + + if(encfsArgs->isThreaded == false && encfsArgs->idleTimeout > 0) { - // set the globally visible root directory node - ctx->setRoot( rootInfo->root ); - ctx->args = encfsArgs; - ctx->opts = encfsArgs->opts; - - if(encfsArgs->isThreaded == false && encfsArgs->idleTimeout > 0) - { - // xgroup(usage) - cerr << _("Note: requested single-threaded mode, but an idle\n" - "timeout was specified. The filesystem will operate\n" - "single-threaded, but threads will still be used to\n" - "implement idle checking.") << endl; - } - - // reset umask now, since we don't want it to interfere with the - // pass-thru calls.. - umask( 0 ); - - if(encfsArgs->isDaemon) - { - // switch to logging just warning and error messages via syslog - logNode.reset( new SyslogNode( "encfs" ) ); - logNode->subscribeTo( GetGlobalChannel("warning") ); - logNode->subscribeTo( GetGlobalChannel("error") ); - - // disable stderr reporting.. - slog.reset(); - - // keep around a pointer just in case we end up needing it to - // report a fatal condition later (fuse_main exits unexpectedly)... - oldStderr = dup( STDERR_FILENO ); - } - - try - { - time_t startTime, endTime; - - if (encfsArgs->opts->annotate) - cerr << "$STATUS$ fuse_main_start" << endl; - - // FIXME: workaround for fuse_main returning an error on normal - // exit. Only print information if fuse_main returned - // immediately.. - time( &startTime ); - - // fuse_main returns an error code in newer versions of fuse.. - int res = fuse_main( encfsArgs->fuseArgc, - const_cast(encfsArgs->fuseArgv), - &encfs_oper, (void*)ctx); - - time( &endTime ); - - if (encfsArgs->opts->annotate) - cerr << "$STATUS$ fuse_main_end" << endl; - - if(res == 0) - returnCode = EXIT_SUCCESS; - - if(res != 0 && encfsArgs->isDaemon && (oldStderr >= 0) - && (endTime - startTime <= 1) ) - { - // the users will not have seen any message from fuse, so say a - // few words in libfuse's memory.. - FILE *out = fdopen( oldStderr, "a" ); - // xgroup(usage) - fprintf(out, _("fuse failed. Common problems:\n" - " - fuse kernel module not installed (modprobe fuse)\n" - " - invalid options -- see usage message\n")); - fclose(out); - } - } catch(std::exception &ex) - { - rError(_("Internal error: Caught exception from main loop: %s"), - ex.what()); - } catch(...) - { - rError(_("Internal error: Caught unexpected exception")); - } + // xgroup(usage) + cerr << _("Note: requested single-threaded mode, but an idle\n" + "timeout was specified. The filesystem will operate\n" + "single-threaded, but threads will still be used to\n" + "implement idle checking.") << endl; } - // cleanup so that we can check for leaked resources.. - rootInfo.reset(); - ctx->setRoot( shared_ptr() ); + // reset umask now, since we don't want it to interfere with the + // pass-thru calls.. + umask( 0 ); - MemoryPool::destroyAll(); - openssl_shutdown( encfsArgs->isThreaded ); + if(encfsArgs->isDaemon) + { + // switch to logging just warning and error messages via syslog + FLAGS_minloglevel = 1; + FLAGS_logtostderr = 0; - return returnCode; + // keep around a pointer just in case we end up needing it to + // report a fatal condition later (fuse_main exits unexpectedly)... + oldStderr = dup( STDERR_FILENO ); + } + + try + { + time_t startTime, endTime; + + if (encfsArgs->opts->annotate) + cerr << "$STATUS$ fuse_main_start" << endl; + + // FIXME: workaround for fuse_main returning an error on normal + // exit. Only print information if fuse_main returned + // immediately.. + time( &startTime ); + + // fuse_main returns an error code in newer versions of fuse.. + int res = fuse_main( encfsArgs->fuseArgc, + const_cast(encfsArgs->fuseArgv), + &encfs_oper, (void*)ctx); + + time( &endTime ); + + if (encfsArgs->opts->annotate) + cerr << "$STATUS$ fuse_main_end" << endl; + + if(res == 0) + returnCode = EXIT_SUCCESS; + + if(res != 0 && encfsArgs->isDaemon && (oldStderr >= 0) + && (endTime - startTime <= 1) ) + { + // the users will not have seen any message from fuse, so say a + // few words in libfuse's memory.. + FILE *out = fdopen( oldStderr, "a" ); + // xgroup(usage) + fprintf(out, _("fuse failed. Common problems:\n" + " - fuse kernel module not installed (modprobe fuse)\n" + " - invalid options -- see usage message\n")); + fclose(out); + } + } catch(std::exception &ex) + { + LOG(ERROR) << "Internal error: Caught exception from main loop: " + << ex.what(); + } catch(...) + { + LOG(ERROR) << "Internal error: Caught unexpected exception"; + } + } + + // cleanup so that we can check for leaked resources.. + rootInfo.reset(); + ctx->setRoot( shared_ptr() ); + + MemoryPool::destroyAll(); + openssl_shutdown( encfsArgs->isThreaded ); + + return returnCode; } /* @@ -699,73 +684,69 @@ static bool unmountFS(EncFS_Context *ctx); static void * idleMonitor(void *_arg) { - EncFS_Context *ctx = (EncFS_Context*)_arg; - shared_ptr arg = ctx->args; + EncFS_Context *ctx = (EncFS_Context*)_arg; + shared_ptr arg = ctx->args; - const int timeoutCycles = 60 * arg->idleTimeout / ActivityCheckInterval; - int idleCycles = 0; + const int timeoutCycles = 60 * arg->idleTimeout / ActivityCheckInterval; + int idleCycles = 0; - pthread_mutex_lock( &ctx->wakeupMutex ); - - while(ctx->running) + pthread_mutex_lock( &ctx->wakeupMutex ); + + while(ctx->running) + { + int usage = ctx->getAndResetUsageCounter(); + + if(usage == 0 && ctx->isMounted()) + ++idleCycles; + else + idleCycles = 0; + + if(idleCycles >= timeoutCycles) { - int usage = ctx->getAndResetUsageCounter(); + int openCount = ctx->openFileCount(); + if( openCount == 0 && unmountFS( ctx ) ) + { + // wait for main thread to wake us up + pthread_cond_wait( &ctx->wakeupCond, &ctx->wakeupMutex ); + break; + } - if(usage == 0 && ctx->isMounted()) - ++idleCycles; - else - idleCycles = 0; - - if(idleCycles >= timeoutCycles) - { - int openCount = ctx->openFileCount(); - if( openCount == 0 && unmountFS( ctx ) ) - { - // wait for main thread to wake us up - pthread_cond_wait( &ctx->wakeupCond, &ctx->wakeupMutex ); - break; - } - - rDebug("num open files: %i", openCount ); - } - - rDebug("idle cycle count: %i, timeout after %i", idleCycles, - timeoutCycles); - - struct timeval currentTime; - gettimeofday( ¤tTime, 0 ); - struct timespec wakeupTime; - wakeupTime.tv_sec = currentTime.tv_sec + ActivityCheckInterval; - wakeupTime.tv_nsec = currentTime.tv_usec * 1000; - pthread_cond_timedwait( &ctx->wakeupCond, - &ctx->wakeupMutex, &wakeupTime ); + VLOG(1) << "num open files: " << openCount; } - - pthread_mutex_unlock( &ctx->wakeupMutex ); - rDebug("Idle monitoring thread exiting"); + VLOG(1) << "idle cycle count: " << idleCycles + << ", timeout after " << timeoutCycles; - return 0; + struct timeval currentTime; + gettimeofday( ¤tTime, 0 ); + struct timespec wakeupTime; + wakeupTime.tv_sec = currentTime.tv_sec + ActivityCheckInterval; + wakeupTime.tv_nsec = currentTime.tv_usec * 1000; + pthread_cond_timedwait( &ctx->wakeupCond, + &ctx->wakeupMutex, &wakeupTime ); + } + + pthread_mutex_unlock( &ctx->wakeupMutex ); + + VLOG(1) << "Idle monitoring thread exiting"; + + return 0; } static bool unmountFS(EncFS_Context *ctx) { - shared_ptr arg = ctx->args; - if( arg->opts->mountOnDemand ) - { - rDebug("Detaching filesystem %s due to inactivity", - arg->mountPoint.c_str()); + shared_ptr arg = ctx->args; + LOG(INFO) << "Detaching filesystem " << arg->mountPoint + << " due to inactivity"; - ctx->setRoot( shared_ptr() ); - return false; - } else - { - // Time to unmount! - // xgroup(diag) - rWarning(_("Unmounting filesystem %s due to inactivity"), - arg->mountPoint.c_str()); - fuse_unmount( arg->mountPoint.c_str() ); - return true; - } + if( arg->opts->mountOnDemand ) + { + ctx->setRoot( shared_ptr() ); + return false; + } else + { + fuse_unmount( arg->mountPoint.c_str() ); + return true; + } } diff --git a/encfs/openssl.cpp b/encfs/openssl.cpp deleted file mode 100644 index dc388da..0000000 --- a/encfs/openssl.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/***************************************************************************** - * Author: Valient Gough - * - ***************************************************************************** - * Copyright (c) 2007, Valient Gough - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "openssl.h" - -#include - -#include - -#define NO_DES -#include -#include -#ifndef OPENSSL_NO_ENGINE -#include -#endif - -unsigned long pthreads_thread_id() -{ - return (unsigned long)pthread_self(); -} - -static pthread_mutex_t *crypto_locks = NULL; -void pthreads_locking_callback( int mode, int n, - const char *caller_file, int caller_line ) -{ - (void)caller_file; - (void)caller_line; - - if(!crypto_locks) - { - rDebug("Allocating %i locks for OpenSSL", CRYPTO_num_locks() ); - crypto_locks = new pthread_mutex_t[ CRYPTO_num_locks() ]; - for(int i=0; i - * - ***************************************************************************** - * Copyright (c) 2003, Valient Gough - * - * This library is free software; you can distribute it and/or modify it under - * the terms of the GNU General Public License (GPL), as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more - * details. - * - */ - -#include "encfs.h" - -#include "config.h" - -#include -#include -#include -#include - -#include "Cipher.h" -#include "DirNode.h" -#include "MemoryPool.h" -#include "Interface.h" -#include "FileUtils.h" -#include "StreamNameIO.h" -#include "BlockNameIO.h" -#include "NullNameIO.h" - -#include -#include -#include -#include - -#ifdef HAVE_SSL -#define NO_DES -#include -#ifndef OPENSSL_NO_ENGINE -#include -#endif -#endif - -#include - -#if HAVE_TR1_UNORDERED_SET -#include -using std::tr1::unordered_set; -#else -#include -using std::unordered_set; -#endif - -using namespace std; -using namespace rlog; - -const int FSBlockSize = 256; - -static -int checkErrorPropogation( const shared_ptr &cipher, - int size, int byteToChange, const CipherKey &key ) -{ - MemBlock orig = MemoryPool::allocate(size); - MemBlock data = MemoryPool::allocate(size); - - for(int i=0; istreamEncode( data.data, size, 0, key ); - else - cipher->blockEncode( data.data, size, 0, key ); - - // intoduce an error in the encoded data, so we can check error propogation - if(byteToChange >= 0 && byteToChange < size) - { - unsigned char previousValue = data.data[byteToChange]; - do - { - data.data[byteToChange] = rand(); - } while(data.data[byteToChange] == previousValue); - } - - if(size != FSBlockSize) - cipher->streamDecode( data.data, size, 0, key ); - else - cipher->blockDecode( data.data, size, 0, key ); - - int numByteErrors = 0; - for(int i=0; i \"" << encName.c_str() << "\""; - - // decrypt name - string decName = dirNode.plainPath( encName.c_str() ); - - if(decName == *orig) - { - if(verbose) - cerr << " OK\n"; - } else - { - if(verbose) - cerr << " FAILED (got " << decName << ")\n"; - return false; - } - - orig++; - } - - if (collisionTest) - { - if (verbose) - cerr << "Checking for name collections, this will take a while..\n"; - // check for collision rate - char buf[64]; - unordered_set encryptedNames; - for (long i=0; i < 10000000; i++) - { - snprintf(buf, sizeof(buf), "%li", i); - string encName = dirNode.relativeCipherPath( buf ); - // simulate a case-insisitive filesystem.. - std::transform(encName.begin(), encName.end(), encName.begin(), - ::toupper); - - if (encryptedNames.insert(encName).second == false) { - cerr << "collision detected after " << i << " iterations"; - break; - } - } - cerr << "NO collisions detected"; - } - - return true; -} - -bool runTests(const shared_ptr &cipher, bool verbose) -{ - // create a random key - if(verbose) - cerr << "Generating new key, output will be different on each run\n\n"; - CipherKey key = cipher->newRandomKey(); - - if(verbose) - cerr << "Testing key save / restore :"; - { - CipherKey encodingKey = cipher->newRandomKey(); - int encodedKeySize = cipher->encodedKeySize(); - unsigned char *keyBuf = new unsigned char [ encodedKeySize ]; - - cipher->writeKey( key, keyBuf, encodingKey ); - CipherKey key2 = cipher->readKey( keyBuf, encodingKey ); - if(!key2) - { - if(verbose) - cerr << " FAILED (decode error)\n"; - return false; - } - - if(cipher->compareKey( key, key2 )) - { - if(verbose) - cerr << " OK\n"; - } else - { - if(verbose) - cerr << " FAILED\n"; - return false; - } - } - - if(verbose) - cerr << "Testing Config interface load / store :"; - { - CipherKey encodingKey = cipher->newRandomKey(); - int encodedKeySize = cipher->encodedKeySize(); - unsigned char *keyBuf = new unsigned char [ encodedKeySize ]; - - cipher->writeKey( key, keyBuf, encodingKey ); - - // store in config struct.. - EncfsConfig cfg; - cfg.mutable_cipher()->MergeFrom(cipher->interface()); - EncryptedKey *encryptedKey = cfg.mutable_key(); - encryptedKey->set_size(8 * cipher->keySize()); - encryptedKey->set_ciphertext( keyBuf, encodedKeySize ); - cfg.set_block_size(FSBlockSize); - - // save config - string data; - google::protobuf::TextFormat::PrintToString(cfg, &data); - - // read back in and check everything.. - EncfsConfig cfg2; - google::protobuf::TextFormat::ParseFromString(data, &cfg2); - - // check.. - rAssert( implements(cfg.cipher(),cfg2.cipher()) ); - rAssert( cfg.key().size() == cfg2.key().size() ); - rAssert( cfg.block_size() == cfg2.block_size() ); - - // try decoding key.. - - CipherKey key2 = cipher->readKey( (unsigned char *)cfg2.key().ciphertext().data(), encodingKey ); - if(!key2) - { - if(verbose) - cerr << " FAILED (decode error)\n"; - return false; - } - - if(cipher->compareKey( key, key2 )) - { - if(verbose) - cerr << " OK\n"; - } else - { - if(verbose) - cerr << " FAILED\n"; - return false; - } - } - - FSConfigPtr fsCfg = FSConfigPtr(new FSConfig); - fsCfg->cipher = cipher; - fsCfg->key = key; - fsCfg->config.reset(new EncfsConfig); - fsCfg->config->set_block_size(FSBlockSize); - - if(verbose) - cerr << "Testing name encode/decode (stream coding w/ IV chaining)\n"; - { - fsCfg->opts.reset(new EncFS_Opts); - fsCfg->opts->idleTracking = false; - fsCfg->config->set_unique_iv(false); - - fsCfg->nameCoding.reset( new StreamNameIO( - StreamNameIO::CurrentInterface(), cipher, key ) ); - fsCfg->nameCoding->setChainedNameIV( true ); - - DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); - - if(!testNameCoding( dirNode, verbose )) - return false; - } - - if(verbose) - cerr << "Testing name encode/decode (block coding w/ IV chaining)\n"; - { - fsCfg->opts->idleTracking = false; - fsCfg->config->set_unique_iv(false); - fsCfg->nameCoding.reset( new BlockNameIO( - BlockNameIO::CurrentInterface(), cipher, key, - cipher->cipherBlockSize() ) ); - fsCfg->nameCoding->setChainedNameIV( true ); - - DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); - - if(!testNameCoding( dirNode, verbose )) - return false; - } - - if(verbose) - cerr << "Testing name encode/decode (block coding w/ IV chaining, base32)\n"; - { - fsCfg->opts->idleTracking = false; - fsCfg->config->set_unique_iv(false); - fsCfg->nameCoding.reset( new BlockNameIO( - BlockNameIO::CurrentInterface(), cipher, key, - cipher->cipherBlockSize(), true ) ); - fsCfg->nameCoding->setChainedNameIV( true ); - - DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); - - if(!testNameCoding( dirNode, verbose )) - return false; - } - - if(!verbose) - { - { - // test stream mode, this time without IV chaining - fsCfg->nameCoding = - shared_ptr( new StreamNameIO( - StreamNameIO::CurrentInterface(), cipher, key ) ); - fsCfg->nameCoding->setChainedNameIV( false ); - - DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); - - if(!testNameCoding( dirNode, verbose )) - return false; - } - - { - // test block mode, this time without IV chaining - fsCfg->nameCoding = shared_ptr( new BlockNameIO( - BlockNameIO::CurrentInterface(), cipher, key, - cipher->cipherBlockSize() ) ); - fsCfg->nameCoding->setChainedNameIV( false ); - - DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); - - if(!testNameCoding( dirNode, verbose )) - return false; - } - } - - if(verbose) - cerr << "Testing block encode/decode on full block - "; - { - int numErrors = checkErrorPropogation( cipher, - FSBlockSize, -1, key ); - if(numErrors) - { - if(verbose) - cerr << " FAILED!\n"; - return false; - } else - { - if(verbose) - cerr << " OK\n"; - } - } - if(verbose) - cerr << "Testing block encode/decode on partial block - "; - { - int numErrors = checkErrorPropogation( cipher, - FSBlockSize-1, -1, key ); - if(numErrors) - { - if(verbose) - cerr << " FAILED!\n"; - return false; - } else - { - if(verbose) - cerr << " OK\n"; - } - } - - if(verbose) - cerr << "Checking error propogation in partial block:\n"; - { - int minChanges = FSBlockSize-1; - int maxChanges = 0; - int minAt = 0; - int maxAt = 0; - for(int i=0; i maxChanges) - { - maxChanges = numErrors; - maxAt = i; - } - } - - if(verbose) - { - cerr << "modification of 1 byte affected between " << minChanges - << " and " << maxChanges << " decoded bytes\n"; - cerr << "minimum change at byte " << minAt - << " and maximum at byte " << maxAt << "\n"; - } - } - if(verbose) - cerr << "Checking error propogation on full block:\n"; - { - int minChanges = FSBlockSize; - int maxChanges = 0; - int minAt = 0; - int maxAt = 0; - for(int i=0; i maxChanges) - { - maxChanges = numErrors; - maxAt = i; - } - } - - if(verbose) - { - cerr << "modification of 1 byte affected between " << minChanges - << " and " << maxChanges << " decoded bytes\n"; - cerr << "minimum change at byte " << minAt - << " and maximum at byte " << maxAt << "\n"; - } - } - - return true; -} - - -int main(int argc, char *argv[]) -{ - RLogInit( argc, argv ); - - StdioNode stdLog( STDERR_FILENO ); - stdLog.subscribeTo( RLOG_CHANNEL("error") ); - stdLog.subscribeTo( RLOG_CHANNEL("warning") ); -#ifndef NO_DEBUG - stdLog.subscribeTo( RLOG_CHANNEL("debug") ); -#endif - -#ifdef HAVE_SSL - SSL_load_error_strings(); - SSL_library_init(); - -#ifndef OPENSSL_NO_ENGINE - ENGINE_load_builtin_engines(); - ENGINE_register_all_ciphers(); - ENGINE_register_all_digests(); - ENGINE_register_all_RAND(); -#endif -#endif - - srand( time(0) ); - - // get a list of the available algorithms - std::list algorithms = - Cipher::GetAlgorithmList(); - std::list::const_iterator it; - cerr << "Supported Crypto interfaces:\n"; - for(it = algorithms.begin(); it != algorithms.end(); ++it) - { - cerr << it->name - << " ( " << it->iface.name() << " " - << it->iface.major() << ":" - << it->iface.minor() << ":" - << it->iface.age() << " ) : " << it->description << "\n"; - cerr << " - key length " << it->keyLength.min() << " to " - << it->keyLength.max() << " , block size " << it->blockSize.min() - << " to " << it->blockSize.max() << "\n"; - } - cerr << "\n"; - - cerr << "Testing interfaces\n"; - for(it = algorithms.begin(); it != algorithms.end(); ++it) - { - int blockSize = it->blockSize.closest( 256 ); - for(int keySize = it->keyLength.min(); keySize <= it->keyLength.max(); - keySize += it->keyLength.inc()) - { - cerr << it->name << ", key length " << keySize - << ", block size " << blockSize << ": "; - - shared_ptr cipher = Cipher::New( it->name, keySize ); - if(!cipher) - { - cerr << "FAILED TO CREATE\n"; - } else - { - try - { - if(runTests( cipher, false )) - cerr << "OK\n"; - else - cerr << "FAILED\n"; - } catch( rlog::Error &er ) - { - cerr << "Error: " << er.what() << "\n"; - } - } - } - } - - // run one test with verbose output too.. - shared_ptr cipher = Cipher::New("AES", 192); - if(!cipher) - { - cerr << "\nNo AES cipher found, skipping verbose test.\n"; - } else - { - cerr << "\nVerbose output for " << cipher->interface().name() - << " test, key length " << cipher->keySize()*8 << ", block size " - << FSBlockSize << ":\n"; - - runTests( cipher, true ); - } - - MemoryPool::destroyAll(); - - return 0; -} - - - diff --git a/encfs/tests.t b/encfs/tests.t deleted file mode 100644 index 4c9e6c0..0000000 --- a/encfs/tests.t +++ /dev/null @@ -1,213 +0,0 @@ -#!/usr/bin/perl -w - -use Test::More qw( no_plan ); -use File::Path; -use IO::Handle; -use Digest::MD5; - -my $tempDir = $ENV{'TMPDIR'} || "/tmp"; - -my $raw = "$tempDir/crypt-raw-$$"; -my $crypt = "$tempDir/crypt-$$"; - - -# test filesystem in standard config mode -&runTests('standard'); - -# test in paranoia mode -&runTests('paranoia'); - - - -sub runTests -{ - my $mode = shift; - - my $hardlinks = 1; - if($mode eq 'standard') - { - &mount("--standard"); - } elsif($mode eq 'paranoia') - { - &mount("--paranoia"); - $hardlinks = 0; # no hardlinks in paranoia mode - } else - { - die "invalid test mode"; - } - - # tests.. - &fileCreation; - &links($hardlinks); - &truncate; - &renames; - - &cleanup; -} - -sub renames -{ - ok( open(F, ">$crypt/orig-name") && close F, "create file for rename test"); - ok( -f "$crypt/orig-name", "file exists"); - - ok( rename("$crypt/orig-name", "$crypt/2nd-name"), "rename"); - ok( ! -f "$crypt/orig-name", "file exists"); - ok( -f "$crypt/2nd-name", "file exists"); - - # rename directory with contents - ok( mkpath("$crypt/orig-dir/foo"), "mkdir for rename test"); - ok( open(F, ">$crypt/orig-dir/foo/bar") && close F, "make file"); - - ok( rename("$crypt/orig-dir", "$crypt/new-dir"), "rename dir"); - ok( -f "$crypt/new-dir/foo/bar", "dir rename contents"); - - # TODO: rename failure? (check undo works) - - # check time stamps of files on rename - my $mtime = (stat "$crypt/2nd-name")[9]; - # change time to 60 seconds earlier - my $olderTime = $mtime - 60; - ok( utime($olderTime, $olderTime, "$crypt/2nd-name"), "change time"); - - ok( rename("$crypt/2nd-name", "$crypt/3rd-name"), "rename"); - is( (stat "$crypt/3rd-name")[9], $olderTime, "time unchanged by rename"); -} - -sub truncate -{ - # write to file, then truncate it - ok( open(OUT, "+> $crypt/trunc"), "create truncate-test file"); - autoflush OUT 1; - print OUT "12345678901234567890"; - - is( -s "$crypt/trunc", 20, "initial file size" ); - - ok( truncate(OUT, 10), "truncate" ); - - is( -s "$crypt/trunc", 10, "truncated file size"); - is( qx(cat "$crypt/trunc"), "1234567890", "truncated file contents"); - - # try growing the file as well. - ok( truncate(OUT, 30), "truncate extend"); - is( -s "$crypt/trunc", 30, "truncated file size"); - - seek(OUT, 30, 0); - print OUT "12345"; - is( -s "$crypt/trunc", 35, "truncated file size"); - - seek(OUT, 0, 0); - is( Digest::MD5->new->addfile(*OUT)->hexdigest, - "5f170cc34b1944d75d86cc01496292df", "content digest"); - - # try crossing block boundaries - seek(OUT, 10000,0); - print OUT "abcde"; - seek(OUT, 0, 0); - is( Digest::MD5->new->addfile(*OUT)->hexdigest, - "117a51c980b64dcd21df097d02206f98", "content digest"); - - # then truncate back to 35 chars - truncate(OUT, 35); - seek(OUT, 0, 0); - is( Digest::MD5->new->addfile(*OUT)->hexdigest, - "5f170cc34b1944d75d86cc01496292df", "content digest"); - - close OUT; -} - -sub fileCreation -{ - # create a file - qx(df -ah > "$crypt/df.txt"); - ok( -f "$crypt/df.txt", "file created" ); - - # ensure there is an encrypted version. - my $c = qx(./encfsctl encode --extpass="echo test" $raw df.txt); - chomp($c); - cmp_ok( length($c), '>', 8, "encrypted name ok" ); - ok( -f "$raw/$c", "encrypted file created" ); - - # check contents - my $count = qx(grep -c crypt-$$ "$crypt/df.txt"); - isnt(scalar($count), 0, "encrypted file readable"); - - unlink "$crypt/df.txt"; - ok( ! -f "$crypt/df.txt", "file removal" ); - ok( ! -f "$raw/$c", "file removal" ); -} - -sub checkContents -{ - my ($file, $expected, $testName) = @_; - - open(IN, "< $file"); - my $line = ; - is( $line, $expected, $testName ); - - close IN; -} - -sub links -{ - my $hardlinkTests = shift; - - my $contents = "hello world\n"; - ok( open(OUT, "> $crypt/data"), "create file for link test" ); - print OUT $contents; - close OUT; - - # symlinks - ok( symlink("$crypt/data", "$crypt/data-fqn") , "fqn symlink"); - checkContents("$crypt/data-fqn", $contents, "fqn link traversal"); - is( readlink("$crypt/data-fqn"), "$crypt/data", "read fqn symlink"); - - ok( symlink("data", "$crypt/data-rel"), "local symlink"); - checkContents("$crypt/data-rel", $contents, "rel link traversal"); - is( readlink("$crypt/data-rel"), "data", "read rel symlink"); - - SKIP: { - skip "No hardlink support" unless $hardlinkTests; - - ok( link("$crypt/data", "$crypt/data.2"), "hard link"); - checkContents("$crypt/data.2", $contents, "hardlink read"); - }; -} - -sub mount -{ - my $args = shift; - - ok( ! -d $raw, "no existing dir"); - ok( ! -d $crypt, "no existing dir"); - - mkdir $raw; - ok( -d $raw, "created dir" ); - mkdir $crypt; - ok( -d $crypt, "created dir" ); - - qx(./encfs --extpass="echo test" $args $raw $crypt); - - ok( -f "$raw/.encfs6.xml", "created control file"); -} - -sub cleanup -{ - my $fusermount = qx(which fusermount); - if(-f $fusermount) - { - qx($fusermount -u "$crypt"); - } else - { - qx(umount "$crypt"); - } - - rmdir $crypt; - ok( ! -d $crypt, "unmount ok, mount point removed"); - - if(-d $raw) - { - rmtree($raw); - } - ok( ! -d $raw, "encrypted directory removed"); -} - diff --git a/fs/BlockFileIO.cpp b/fs/BlockFileIO.cpp new file mode 100644 index 0000000..2b0632e --- /dev/null +++ b/fs/BlockFileIO.cpp @@ -0,0 +1,426 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "fs/BlockFileIO.h" + +#include "base/config.pb.h" +#include "base/Error.h" +#include "base/i18n.h" +#include "cipher/MemoryPool.h" + +#include +#include + +template +inline Type min( Type A, Type B ) +{ + return (B < A) ? B : A; +} + +static void clearCache( IORequest &req, int blockSize ) +{ + memset( req.data, 0, blockSize ); + req.dataLen = 0; +} + +BlockFileIO::BlockFileIO( int blockSize, const FSConfigPtr &cfg ) + : _blockSize( blockSize ) + , _allowHoles( cfg->config->allow_holes() ) +{ + rAssert( _blockSize > 1 ); + _cache.data = new unsigned char [ _blockSize ]; +} + +BlockFileIO::~BlockFileIO() +{ + clearCache( _cache, _blockSize ); + delete[] _cache.data; +} + +ssize_t BlockFileIO::cacheReadOneBlock( const IORequest &req ) const +{ + // we can satisfy the request even if _cache.dataLen is too short, because + // we always request a full block during reads.. + if((req.offset == _cache.offset) && (_cache.dataLen != 0)) + { + // satisfy request from cache + int len = req.dataLen; + if(_cache.dataLen < len) + len = _cache.dataLen; + memcpy( req.data, _cache.data, len ); + return len; + } else + { + if(_cache.dataLen > 0) + clearCache( _cache, _blockSize ); + + // cache results of read -- issue reads for full blocks + IORequest tmp; + tmp.offset = req.offset; + tmp.data = _cache.data; + tmp.dataLen = _blockSize; + + ssize_t result = readOneBlock( tmp ); + if(result > 0) + { + _cache.offset = req.offset; + _cache.dataLen = result; // the amount we really have + if(result > req.dataLen) + result = req.dataLen; // only as much as requested + memcpy( req.data, _cache.data, result ); + } + return result; + } +} + +bool BlockFileIO::cacheWriteOneBlock( const IORequest &req ) +{ + // cache results of write (before pass-thru, because it may be modified + // in-place) + memcpy( _cache.data, req.data, req.dataLen ); + _cache.offset = req.offset; + _cache.dataLen = req.dataLen; + bool ok = writeOneBlock( req ); + if(!ok) + clearCache( _cache, _blockSize ); + return ok; +} + +ssize_t BlockFileIO::read( const IORequest &req ) const +{ + rAssert( _blockSize != 0 ); + + int partialOffset = req.offset % _blockSize; + off_t blockNum = req.offset / _blockSize; + ssize_t result = 0; + + if(partialOffset == 0 && req.dataLen <= _blockSize) + { + // read completely within a single block -- can be handled as-is by + // readOneBloc(). + return cacheReadOneBlock( req ); + } else + { + size_t size = req.dataLen; + + // if the request is larger then a block, then request each block + // individually + MemBlock mb; // in case we need to allocate a temporary block.. + IORequest blockReq; // for requests we may need to make + blockReq.dataLen = _blockSize; + blockReq.data = NULL; + + unsigned char *out = req.data; + while( size ) + { + blockReq.offset = blockNum * _blockSize; + + // if we're reading a full block, then read directly into the + // result buffer instead of using a temporary + if(partialOffset == 0 && size >= (size_t)_blockSize) + blockReq.data = out; + else + { + if(!mb.data) + mb.allocate( _blockSize ); + blockReq.data = mb.data; + } + + ssize_t readSize = cacheReadOneBlock( blockReq ); + if(readSize <= partialOffset) + break; // didn't get enough bytes + + int cpySize = min( (size_t)(readSize - partialOffset), size ); + rAssert(cpySize <= readSize); + + // if we read to a temporary buffer, then move the data + if(blockReq.data != out) + memcpy( out, blockReq.data + partialOffset, cpySize ); + + result += cpySize; + size -= cpySize; + out += cpySize; + ++blockNum; + partialOffset = 0; + + if(readSize < _blockSize) + break; + } + + return result; + } +} + +bool BlockFileIO::write( const IORequest &req ) +{ + rAssert( _blockSize != 0 ); + + off_t fileSize = getSize(); + + // where write request begins + off_t blockNum = req.offset / _blockSize; + int partialOffset = req.offset % _blockSize; + + // last block of file (for testing write overlaps with file boundary) + off_t lastFileBlock = fileSize / _blockSize; + ssize_t lastBlockSize = fileSize % _blockSize; + + off_t lastNonEmptyBlock = lastFileBlock; + if(lastBlockSize == 0) + --lastNonEmptyBlock; + + if( req.offset > fileSize ) + { + // extend file first to fill hole with 0's.. + const bool forceWrite = false; + padFile( fileSize, req.offset, forceWrite ); + } + + // check against edge cases where we can just let the base class handle the + // request as-is.. + if(partialOffset == 0 && req.dataLen <= _blockSize) + { + // if writing a full block.. pretty safe.. + if( req.dataLen == _blockSize ) + return cacheWriteOneBlock( req ); + + // if writing a partial block, but at least as much as what is + // already there.. + if(blockNum == lastFileBlock && req.dataLen >= lastBlockSize) + return cacheWriteOneBlock( req ); + } + + // have to merge data with existing block(s).. + MemBlock mb; + + IORequest blockReq; + blockReq.data = NULL; + blockReq.dataLen = _blockSize; + + bool ok = true; + size_t size = req.dataLen; + unsigned char *inPtr = req.data; + while( size ) + { + blockReq.offset = blockNum * _blockSize; + int toCopy = min((size_t)(_blockSize - partialOffset), size); + + // if writing an entire block, or writing a partial block that requires + // no merging with existing data.. + if( (toCopy == _blockSize) + ||(partialOffset == 0 && blockReq.offset + toCopy >= fileSize)) + { + // write directly from buffer + blockReq.data = inPtr; + blockReq.dataLen = toCopy; + } else + { + // need a temporary buffer, since we have to either merge or pad + // the data. + if(!mb.data) + mb.allocate( _blockSize ); + memset( mb.data, 0, _blockSize ); + blockReq.data = mb.data; + + if(blockNum > lastNonEmptyBlock) + { + // just pad.. + blockReq.dataLen = toCopy + partialOffset; + } else + { + // have to merge with existing block data.. + blockReq.dataLen = _blockSize; + blockReq.dataLen = cacheReadOneBlock( blockReq ); + + // extend data if necessary.. + if( partialOffset + toCopy > blockReq.dataLen ) + blockReq.dataLen = partialOffset + toCopy; + } + // merge in the data to be written.. + memcpy( blockReq.data + partialOffset, inPtr, toCopy ); + } + + // Finally, write the damn thing! + if(!cacheWriteOneBlock( blockReq )) + { + ok = false; + break; + } + + // prepare to start all over with the next block.. + size -= toCopy; + inPtr += toCopy; + ++blockNum; + partialOffset = 0; + } + + return ok; +} + +int BlockFileIO::blockSize() const +{ + return _blockSize; +} + +void BlockFileIO::padFile( off_t oldSize, off_t newSize, bool forceWrite ) +{ + off_t oldLastBlock = oldSize / _blockSize; + off_t newLastBlock = newSize / _blockSize; + int lastBlockSize = newSize % _blockSize; + + IORequest req; + MemBlock mb; + + if(oldLastBlock == newLastBlock) + { + // when the real write occurs, it will have to read in the existing + // data and pad it anyway, so we won't do it here (unless we're + // forced). + if( forceWrite ) + { + mb.allocate( _blockSize ); + req.data = mb.data; + + req.offset = oldLastBlock * _blockSize; + req.dataLen = oldSize % _blockSize; + int outSize = newSize % _blockSize; // outSize > req.dataLen + + if(outSize) + { + memset( mb.data, 0, outSize ); + cacheReadOneBlock( req ); + req.dataLen = outSize; + cacheWriteOneBlock( req ); + } + } else + VLOG(1) << "optimization: not padding last block"; + } else + { + mb.allocate( _blockSize ); + req.data = mb.data; + + // 1. extend the first block to full length + // 2. write the middle empty blocks + // 3. write the last block + + req.offset = oldLastBlock * _blockSize; + req.dataLen = oldSize % _blockSize; + + // 1. req.dataLen == 0, iff oldSize was already a multiple of blocksize + if(req.dataLen != 0) + { + VLOG(1) << "padding block " << oldLastBlock; + memset( mb.data, 0, _blockSize ); + cacheReadOneBlock( req ); + req.dataLen = _blockSize; // expand to full block size + cacheWriteOneBlock( req ); + ++oldLastBlock; + } + + // 2, pad zero blocks unless holes are allowed + if(!_allowHoles) + { + for(; oldLastBlock != newLastBlock; ++oldLastBlock) + { + VLOG(1) << "padding block " << oldLastBlock; + req.offset = oldLastBlock * _blockSize; + req.dataLen = _blockSize; + memset( mb.data, 0, req.dataLen ); + cacheWriteOneBlock( req ); + } + } + + // 3. only necessary if write is forced and block is non 0 length + if(forceWrite && lastBlockSize) + { + req.offset = newLastBlock * _blockSize; + req.dataLen = lastBlockSize; + memset( mb.data, 0, req.dataLen ); + cacheWriteOneBlock( req ); + } + } +} + +int BlockFileIO::blockTruncate( off_t size, FileIO *base ) +{ + rAssert(size >= 0); + + int partialBlock = size % _blockSize; + int res = 0; + + off_t oldSize = getSize(); + + if( size > oldSize ) + { + // truncate can be used to extend a file as well. truncate man page + // states that it will pad with 0's. + // do the truncate so that the underlying filesystem can allocate + // the space, and then we'll fill it in padFile.. + if(base) + base->truncate( size ); + + const bool forceWrite = true; + padFile( oldSize, size, forceWrite ); + } else + if( size == oldSize ) + { + // the easiest case, but least likely.... + } else + if( partialBlock ) + { + // partial block after truncate. Need to read in the block being + // truncated before the truncate. Then write it back out afterwards, + // since the encoding will change.. + off_t blockNum = size / _blockSize; + MemBlock mb; + mb.allocate( _blockSize ); + + IORequest req; + req.offset = blockNum * _blockSize; + req.dataLen = _blockSize; + req.data = mb.data; + + ssize_t rdSz = cacheReadOneBlock( req ); + + // do the truncate + if(base) + res = base->truncate( size ); + + // write back out partial block + req.dataLen = partialBlock; + bool wrRes = cacheWriteOneBlock( req ); + + if((rdSz < 0) || (!wrRes)) + { + LOG(ERROR) << "truncate failure: read size " << rdSz + << ", partial block of " << partialBlock; + } + + } else + { + // truncating on a block bounday. No need to re-encode the last + // block.. + if(base) + res = base->truncate( size ); + } + + return res; +} + diff --git a/encfs/BlockFileIO.h b/fs/BlockFileIO.h similarity index 97% rename from encfs/BlockFileIO.h rename to fs/BlockFileIO.h index 2a76d78..42acd54 100644 --- a/encfs/BlockFileIO.h +++ b/fs/BlockFileIO.h @@ -46,7 +46,7 @@ public: protected: - int truncate( off_t size, FileIO *base ); + int blockTruncate( off_t size, FileIO *base ); void padFile( off_t oldSize, off_t newSize, bool forceWrite ); // same as read(), except that the request.offset field is guarenteed to be diff --git a/fs/BlockNameIO.cpp b/fs/BlockNameIO.cpp new file mode 100644 index 0000000..69b4f91 --- /dev/null +++ b/fs/BlockNameIO.cpp @@ -0,0 +1,250 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004-2011, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "fs/BlockNameIO.h" + +#include "base/base64.h" +#include "base/Error.h" +#include "base/i18n.h" +#include "cipher/Cipher.h" + +#include +#include + +static shared_ptr NewBlockNameIO( const Interface &iface, + const shared_ptr &cipher, const CipherKey &key ) +{ + return shared_ptr( + new BlockNameIO( iface, cipher, key, false)); +} + +static shared_ptr NewBlockNameIO32( const Interface &iface, + const shared_ptr &cipher, const CipherKey &key ) +{ + return shared_ptr( + new BlockNameIO( iface, cipher, key, true)); +} + +static bool BlockIO_registered = NameIO::Register("Block", + // description of block name encoding algorithm.. + // xgroup(setup) + gettext_noop("Block encoding, hides file name size somewhat"), + BlockNameIO::CurrentInterface(false), + NewBlockNameIO, false); + +static bool BlockIO32_registered = NameIO::Register("Block32", + // description of block name encoding algorithm.. + // xgroup(setup) + gettext_noop("Block encoding with base32 output for case-sensitive systems"), + BlockNameIO::CurrentInterface(true), + NewBlockNameIO32, false); + +/* + - Version 1.0 computed MAC over the filename, but not the padding bytes. + This version was from pre-release 1.1, never publically released, so no + backward compatibility necessary. + + - Version 2.0 includes padding bytes in MAC computation. This way the MAC + computation uses the same number of bytes regardless of the number of + padding bytes. + + - Version 3.0 uses full 64 bit initialization vector during IV chaining. + Prior versions used only the output from the MAC_16 call, giving a 1 in + 2^16 chance of the same name being produced. Using the full 64 bit IV + changes that to a 1 in 2^64 chance.. + + - Version 4.0 adds support for base32, creating names more suitable for + case-insensitive filesystems (eg Mac). + */ +Interface BlockNameIO::CurrentInterface(bool caseSensitive) +{ + // implement major version 4 plus support for two prior versions + if (caseSensitive) + return makeInterface("nameio/block32", 4, 0, 2); + else + return makeInterface("nameio/block", 4, 0, 2); +} + +BlockNameIO::BlockNameIO( const Interface &iface, + const shared_ptr &cipher, + const CipherKey &key, bool caseSensitiveEncoding ) + : _interface( iface.major() ) + , _bs( cipher->cipherBlockSize() ) + , _cipher( cipher ) + , _key( key ) + , _caseSensitive( caseSensitiveEncoding ) +{ + rAssert( _bs < 128 ); +} + +BlockNameIO::~BlockNameIO() +{ +} + +Interface BlockNameIO::interface() const +{ + return CurrentInterface(_caseSensitive); +} + +int BlockNameIO::maxEncodedNameLen( int plaintextNameLen ) const +{ + // number of blocks, rounded up.. Only an estimate at this point, err on + // the size of too much space rather then too little. + int numBlocks = ( plaintextNameLen + _bs ) / _bs; + int encodedNameLen = numBlocks * _bs + 2; // 2 checksum bytes + if (_caseSensitive) + return B256ToB32Bytes( encodedNameLen ); + else + return B256ToB64Bytes( encodedNameLen ); +} + +int BlockNameIO::maxDecodedNameLen( int encodedNameLen ) const +{ + int decLen256 = _caseSensitive ? + B32ToB256Bytes( encodedNameLen ) : + B64ToB256Bytes( encodedNameLen ); + return decLen256 - 2; // 2 checksum bytes removed.. +} + +int BlockNameIO::encodeName( const char *plaintextName, int length, + uint64_t *iv, char *encodedName ) const +{ + // copy the data into the encoding buffer.. + memcpy( encodedName+2, plaintextName, length ); + + // Pad encryption buffer to block boundary.. + int padding = _bs - length % _bs; + if(padding == 0) + padding = _bs; // padding a full extra block! + + memset( encodedName+length+2, (unsigned char)padding, padding ); + + // store the IV before it is modified by the MAC call. + uint64_t tmpIV = 0; + if( iv && _interface >= 3 ) + tmpIV = *iv; + + // include padding in MAC computation + unsigned int mac = _cipher->MAC_16( (unsigned char *)encodedName+2, + length+padding, _key, iv ); + + // add checksum bytes + encodedName[0] = (mac >> 8) & 0xff; + encodedName[1] = (mac ) & 0xff; + + _cipher->blockEncode( (unsigned char *)encodedName+2, length+padding, + (uint64_t)mac ^ tmpIV, _key); + + // convert to base 64 ascii + int encodedStreamLen = length + 2 + padding; + int encLen; + + if (_caseSensitive) + { + encLen = B256ToB32Bytes( encodedStreamLen ); + + changeBase2Inline( (unsigned char *)encodedName, encodedStreamLen, + 8, 5, true ); + B32ToAscii( (unsigned char *)encodedName, encLen ); + } else + { + encLen = B256ToB64Bytes( encodedStreamLen ); + + changeBase2Inline( (unsigned char *)encodedName, encodedStreamLen, + 8, 6, true ); + B64ToAscii( (unsigned char *)encodedName, encLen ); + } + + return encLen; +} + +int BlockNameIO::decodeName( const char *encodedName, int length, + uint64_t *iv, char *plaintextName ) const +{ + int decLen256 = _caseSensitive ? + B32ToB256Bytes( length ) : + B64ToB256Bytes( length ); + int decodedStreamLen = decLen256 - 2; + + // don't bother trying to decode files which are too small + if(decodedStreamLen < _bs) + throw Error("Filename too small to decode"); + + BUFFER_INIT( tmpBuf, 32, (unsigned int)length ); + + // decode into tmpBuf, + if (_caseSensitive) + { + AsciiToB32((unsigned char *)tmpBuf, (unsigned char *)encodedName, length); + changeBase2Inline((unsigned char *)tmpBuf, length, 5, 8, false); + } else + { + AsciiToB64((unsigned char *)tmpBuf, (unsigned char *)encodedName, length); + changeBase2Inline((unsigned char *)tmpBuf, length, 6, 8, false); + } + + // pull out the header information + unsigned int mac = ((unsigned int)((unsigned char)tmpBuf[0])) << 8 + | ((unsigned int)((unsigned char)tmpBuf[1])); + + uint64_t tmpIV = 0; + if( iv && _interface >= 3 ) + tmpIV = *iv; + + _cipher->blockDecode( (unsigned char *)tmpBuf+2, decodedStreamLen, + (uint64_t)mac ^ tmpIV, _key); + + // find out true string length + int padding = (unsigned char)tmpBuf[2+decodedStreamLen-1]; + int finalSize = decodedStreamLen - padding; + + // might happen if there is an error decoding.. + if(padding > _bs || finalSize < 0) + { + VLOG(1) << "padding, _bx, finalSize = " << padding + << ", " << _bs << ", " << finalSize; + throw Error( "invalid padding size" ); + } + + // copy out the result.. + memcpy(plaintextName, tmpBuf+2, finalSize); + plaintextName[finalSize] = '\0'; + + // check the mac + unsigned int mac2 = _cipher->MAC_16((const unsigned char *)tmpBuf+2, + decodedStreamLen, _key, iv); + + BUFFER_RESET( tmpBuf ); + + if(mac2 != mac) + { + LOG(INFO) << "checksum mismatch: expected " << mac << ", got " + << mac2 << " on decode of " << finalSize << " bytes"; + throw Error( "checksum mismatch in filename decode" ); + } + + return finalSize; +} + +bool BlockNameIO::Enabled() +{ + return true; +} + diff --git a/encfs/BlockNameIO.h b/fs/BlockNameIO.h similarity index 95% rename from encfs/BlockNameIO.h rename to fs/BlockNameIO.h index b57f5b3..832bad3 100644 --- a/encfs/BlockNameIO.h +++ b/fs/BlockNameIO.h @@ -21,8 +21,8 @@ #ifndef _BlockNameIO_incl_ #define _BlockNameIO_incl_ -#include "NameIO.h" -#include "CipherKey.h" +#include "cipher/CipherKey.h" +#include "fs/NameIO.h" #include @@ -40,7 +40,7 @@ public: BlockNameIO( const Interface &iface, const shared_ptr &cipher, - const CipherKey &key, int blockSize, + const CipherKey &key, bool caseSensitiveEncoding = false ); virtual ~BlockNameIO(); diff --git a/fs/CMakeLists.txt b/fs/CMakeLists.txt new file mode 100644 index 0000000..22ff52c --- /dev/null +++ b/fs/CMakeLists.txt @@ -0,0 +1,56 @@ +find_package (FUSE REQUIRED) +include_directories (${FUSE_INCLUDE_DIR}) + +enable_testing () +find_package (GTest) + +add_library (encfs-fs + encfs.cpp + Context.cpp + FileIO.cpp + RawFileIO.cpp + BlockFileIO.cpp + CipherFileIO.cpp + MACFileIO.cpp + NameIO.cpp + StreamNameIO.cpp + BlockNameIO.cpp + NullNameIO.cpp + DirNode.cpp + FileNode.cpp + FileUtils.cpp + ${PROTO_SRCS} + ${PROTO_HDRS} +) + +target_link_libraries (encfs-fs + ${PROTOBUF_LIBRARY} +) + +# Unit tests are optional, depends on libgtest (Google's C++ test framework). +if (GTEST_FOUND) + link_directories (${Encfs_BINARY_DIR}/base) + link_directories (${Encfs_BINARY_DIR}/cipher) + + include_directories (${GTEST_INCLUDE_DIR}) + add_executable (unittests + MemBlockFileIO.cpp + MemFileIO.cpp + testing.cpp + test_IO.cpp + test_BlockIO.cpp + ) + + target_link_libraries (unittests + ${GTEST_BOTH_LIBRARIES} + encfs-fs + encfs-cipher + encfs-base + ${GLOG_LIBRARIES} + ) + + add_test (UnitTests unittests) + GTEST_ADD_TESTS (unittests "${UnitTestArgs}" test_IO.cpp test_BlockIO.cpp) + add_custom_target (test COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS unittests) + +endif (GTEST_FOUND) diff --git a/fs/CipherFileIO.cpp b/fs/CipherFileIO.cpp new file mode 100644 index 0000000..11dcddf --- /dev/null +++ b/fs/CipherFileIO.cpp @@ -0,0 +1,513 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004-2013, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "fs/CipherFileIO.h" + +#include "base/config.pb.h" +#include "base/Error.h" +#include "cipher/Cipher.h" +#include "cipher/MemoryPool.h" + +#include + +#include +#include + +/* + Version 3:0 adds support for block-only encryption by adding space for + a full block to the file header. + + Version 2:0 adds support for a per-file initialization vector with a + fixed 8 byte header. The headers are enabled globally within a + filesystem at the filesystem configuration level. + When headers are disabled, 2:0 is compatible with version 1:0. +*/ +static Interface CipherFileIO_iface = makeInterface("FileIO/Cipher", 3, 0, 2); + +CipherFileIO::CipherFileIO( const shared_ptr &_base, + const FSConfigPtr &cfg) + : BlockFileIO( cfg->config->block_size(), cfg ) + , base( _base ) + , headerLen( 0 ) + , blockOnlyMode( cfg->config->block_mode_only() ) + , perFileIV( cfg->config->unique_iv() ) + , externalIV( 0 ) + , fileIV( 0 ) + , lastFlags( 0 ) +{ + fsConfig = cfg; + cipher = cfg->cipher; + key = cfg->key; + + if ( blockOnlyMode ) + { + headerLen += blockSize(); + if ( perFileIV ) + headerLen += cipher->cipherBlockSize(); + } else + { + if ( perFileIV ) + headerLen += sizeof(uint64_t); // 64bit IV per file + } + + int blockBoundary = fsConfig->config->block_size() % + fsConfig->cipher->cipherBlockSize(); + if(blockBoundary != 0) + { + LOG_FIRST_N(ERROR, 1) + << "CipherFileIO: blocks should be multiple of cipher block size"; + } +} + +CipherFileIO::~CipherFileIO() +{ +} + +Interface CipherFileIO::interface() const +{ + return CipherFileIO_iface; +} + +int CipherFileIO::open( int flags ) +{ + int res = base->open( flags ); + + if( res >= 0 ) + lastFlags = flags; + + return res; +} + +void CipherFileIO::setFileName( const char *fileName ) +{ + base->setFileName( fileName ); +} + +const char *CipherFileIO::getFileName() const +{ + return base->getFileName(); +} + +bool CipherFileIO::setIV( uint64_t iv ) +{ + VLOG(1) << "in setIV, current IV = " << externalIV + << ", new IV = " << iv << ", fileIV = " << fileIV; + if(externalIV == 0) + { + // we're just being told about which IV to use. since we haven't + // initialized the fileIV, there is no need to just yet.. + externalIV = iv; + LOG_IF(WARNING, fileIV != 0) + << "fileIV initialized before externalIV! (" << fileIV + << ", " << externalIV << ")"; + } else if(perFileIV) + { + // we have an old IV, and now a new IV, so we need to update the fileIV + // on disk. + if(fileIV == 0) + { + // ensure the file is open for read/write.. + int newFlags = lastFlags | O_RDWR; + int res = base->open( newFlags ); + if(res < 0) + { + if(res == -EISDIR) + { + // duh -- there are no file headers for directories! + externalIV = iv; + return base->setIV( iv ); + } else + { + VLOG(1) << "writeHeader failed to re-open for write"; + return false; + } + } + initHeader(); + } + + uint64_t oldIV = externalIV; + externalIV = iv; + if(!writeHeader()) + { + externalIV = oldIV; + return false; + } + } + + return base->setIV( iv ); +} + +off_t CipherFileIO::adjustedSize(off_t rawSize) const +{ + off_t size = rawSize; + + if (rawSize >= headerLen) + size -= headerLen; + + return size; +} + +int CipherFileIO::getAttr( struct stat *stbuf ) const +{ + int res = base->getAttr( stbuf ); + + // adjust size if we have a file header + if((res == 0) && S_ISREG(stbuf->st_mode)) + stbuf->st_size = adjustedSize(stbuf->st_size); + + return res; +} + +off_t CipherFileIO::getSize() const +{ + // No check on S_ISREG here -- getSize only for normal files! + off_t size = base->getSize(); + return adjustedSize(size); +} + +void CipherFileIO::initHeader( ) +{ + int cbs = cipher->cipherBlockSize(); + + MemBlock mb; + mb.allocate(cbs); + + // check if the file has a header, and read it if it does.. Otherwise, + // create one. + off_t rawSize = base->getSize(); + if(rawSize >= headerLen) + { + VLOG(1) << "reading existing header, rawSize = " << rawSize; + + IORequest req; + req.offset = 0; + if (blockOnlyMode) + req.offset += blockSize(); + + req.data = mb.data; + req.dataLen = blockOnlyMode ? cbs : sizeof(uint64_t); + base->read( req ); + + if (perFileIV) + { + if (blockOnlyMode) + cipher->blockDecode( mb.data, cbs, externalIV, key ); + else + cipher->streamDecode( mb.data, sizeof(uint64_t), externalIV, key ); + + fileIV = 0; + for(unsigned int i=0; irandomize( mb.data, 8, false )) + throw Error("Unable to generate a random file IV"); + + fileIV = 0; + for(unsigned int i=0; iblockEncode( mb.data, cbs, externalIV, key ); + else + cipher->streamEncode( mb.data, sizeof(uint64_t), externalIV, key ); + + if( base->isWritable() ) + { + IORequest req; + req.offset = 0; + if (blockOnlyMode) + req.offset += blockSize(); + + req.data = mb.data; + req.dataLen = blockOnlyMode ? cbs : sizeof(uint64_t); + + base->write( req ); + } else + VLOG(1) << "base not writable, IV not written.."; + } + VLOG(1) << "initHeader finished, fileIV = " << fileIV; +} + +bool CipherFileIO::writeHeader( ) +{ + if( !base->isWritable() ) + { + // open for write.. + int newFlags = lastFlags | O_RDWR; + if( base->open( newFlags ) < 0 ) + { + VLOG(1) << "writeHeader failed to re-open for write"; + return false; + } + } + + LOG_IF(ERROR, fileIV == 0) + << "Internal error: fileIV == 0 in writeHeader!!!"; + VLOG(1) << "writing fileIV " << fileIV; + + MemBlock mb; + mb.allocate(headerLen); + + if (perFileIV) + { + int cbs = cipher->cipherBlockSize(); + unsigned char *buf = mb.data + (blockOnlyMode ? blockSize() : 0); + + for(int i=sizeof(buf)-1; i>=0; --i) + { + buf[i] = (unsigned char)(fileIV & 0xff); + fileIV >>= 8; + } + + if (blockOnlyMode) + cipher->blockEncode( buf, cbs, externalIV, key ); + else + cipher->streamEncode( buf, sizeof(uint64_t), externalIV, key); + } + + IORequest req; + req.offset = 0; + req.data = mb.data; + req.dataLen = headerLen; + + base->write( req ); + + return true; +} + +ssize_t CipherFileIO::readOneBlock( const IORequest &req ) const +{ + // read raw data, then decipher it.. + int bs = blockSize(); + rAssert(req.dataLen <= bs); + + off_t blockNum = req.offset / bs; + + ssize_t readSize = 0; + IORequest tmpReq = req; + + MemBlock mb; + if (headerLen != 0) + tmpReq.offset += headerLen; + + int maxReadSize = req.dataLen; + if (blockOnlyMode) + { + off_t size = getSize(); + if (req.offset + req.dataLen > size) + { + // Last block written as full block at front of the file header. + mb.allocate(bs); + + tmpReq.offset = 0; + tmpReq.dataLen = bs; + tmpReq.data = mb.data; + + // TODO: what is the expected behavior if req.offset >= size? + maxReadSize = size - req.offset; + if (maxReadSize <= 0) + return 0; + } + } + + readSize = base->read( tmpReq ); + + bool ok; + if(readSize > 0) + { + if(headerLen != 0 && fileIV == 0) + const_cast(this)->initHeader(); + + if(blockOnlyMode || readSize == bs) + { + ok = blockRead( tmpReq.data, bs, blockNum ^ fileIV); + } else + { + ok = streamRead( tmpReq.data, (int)readSize, blockNum ^ fileIV); + } + + if(!ok) + { + VLOG(1) << "decodeBlock failed for block " << blockNum + << ", size " << readSize; + readSize = -1; + } else if (tmpReq.data != req.data) + { + if (readSize > maxReadSize) + readSize = maxReadSize; + memcpy(req.data, tmpReq.data, readSize); + } + } else + VLOG(1) << "readSize zero for offset " << req.offset; + + return readSize; +} + + +bool CipherFileIO::writeOneBlock( const IORequest &req ) +{ + int bs = blockSize(); + int cbs = cipher->cipherBlockSize(); + off_t blockNum = req.offset / bs; + + if(headerLen != 0 && fileIV == 0) + initHeader(); + + MemBlock mb; + + bool ok; + if (req.dataLen == bs) + { + ok = blockWrite( req.data, bs, blockNum ^ fileIV ); + } else if (blockOnlyMode) + { + mb.allocate(bs); + cipher->randomize(mb.data + bs - cbs, cbs, false); + memcpy(mb.data, req.data, req.dataLen); + + ok = blockWrite( mb.data, bs, blockNum ^ fileIV ); + } else + { + ok = streamWrite( req.data, (int)req.dataLen, + blockNum ^ fileIV ); + } + + if( ok ) + { + if(headerLen != 0) + { + IORequest nreq = req; + + if (mb.data == NULL) + { + nreq.offset += headerLen; + } else + { + // Partial block is stored at front of file. + nreq.offset = 0; + nreq.data = mb.data; + nreq.dataLen = bs; + base->truncate(req.offset + req.dataLen + headerLen); + } + + ok = base->write( nreq ); + } else + ok = base->write( req ); + } else + { + VLOG(1) << "encodeBlock failed for block " << blockNum + << ", size " << req.dataLen; + ok = false; + } + return ok; +} + +bool CipherFileIO::blockWrite( unsigned char *buf, int size, + uint64_t _iv64 ) const +{ + if (!fsConfig->reverseEncryption) + return cipher->blockEncode( buf, size, _iv64, key ); + else + return cipher->blockDecode( buf, size, _iv64, key ); +} + +bool CipherFileIO::streamWrite( unsigned char *buf, int size, + uint64_t _iv64 ) const +{ + if (!fsConfig->reverseEncryption) + return cipher->streamEncode( buf, size, _iv64, key ); + else + return cipher->streamDecode( buf, size, _iv64, key ); +} + + +bool CipherFileIO::blockRead( unsigned char *buf, int size, + uint64_t _iv64 ) const +{ + if (fsConfig->reverseEncryption) + return cipher->blockEncode( buf, size, _iv64, key ); + else if(_allowHoles) + { + // special case - leave all 0's alone + for(int i=0; iblockDecode( buf, size, _iv64, key ); + + return true; + } else + return cipher->blockDecode( buf, size, _iv64, key ); +} + +bool CipherFileIO::streamRead( unsigned char *buf, int size, + uint64_t _iv64 ) const +{ + if (fsConfig->reverseEncryption) + return cipher->streamEncode( buf, size, _iv64, key ); + else + return cipher->streamDecode( buf, size, _iv64, key ); +} + +int CipherFileIO::truncate( off_t size ) +{ + rAssert(size >= 0); + + if(headerLen == 0) + { + return blockTruncate( size, base.get() ); + } else if(0 == fileIV) + { + // empty file.. create the header.. + if( !base->isWritable() ) + { + // open for write.. + int newFlags = lastFlags | O_RDWR; + if( base->open( newFlags ) < 0 ) + VLOG(1) << "writeHeader failed to re-open for write"; + } + initHeader(); + } + + // can't let BlockFileIO call base->truncate(), since it would be using + // the wrong size.. + int res = blockTruncate( size, 0 ); + + if(res == 0) + base->truncate( size + headerLen ); + + return res; +} + +bool CipherFileIO::isWritable() const +{ + return base->isWritable(); +} + diff --git a/encfs/CipherFileIO.h b/fs/CipherFileIO.h similarity index 85% rename from encfs/CipherFileIO.h rename to fs/CipherFileIO.h index 90dfc11..efc1dcd 100644 --- a/encfs/CipherFileIO.h +++ b/fs/CipherFileIO.h @@ -21,9 +21,9 @@ #ifndef _CipherFileIO_incl_ #define _CipherFileIO_incl_ -#include "BlockFileIO.h" -#include "CipherKey.h" -#include "FileUtils.h" +#include "cipher/CipherKey.h" +#include "fs/BlockFileIO.h" +#include "fs/FileUtils.h" #include @@ -52,6 +52,9 @@ public: virtual int getAttr( struct stat *stbuf ) const; virtual off_t getSize() const; + // NOTE: if truncate is used to extend the file, the extended plaintext is + // not 0. The extended ciphertext may be 0, resulting in non-zero + // plaintext. virtual int truncate( off_t size ); virtual bool isWritable() const; @@ -71,13 +74,18 @@ private: bool streamWrite( unsigned char *buf, int size, uint64_t iv64 ) const; + off_t adjustedSize(off_t size) const; + shared_ptr base; FSConfigPtr fsConfig; // if haveHeader is true, then we have a transparent file header which - // contains a 64 bit initialization vector. - bool haveHeader; + int headerLen; + // Use block only encryption, no stream encryption. + bool blockOnlyMode; + + bool perFileIV; bool externalIVChaining; uint64_t externalIV; uint64_t fileIV; diff --git a/fs/Context.cpp b/fs/Context.cpp new file mode 100644 index 0000000..b24d06a --- /dev/null +++ b/fs/Context.cpp @@ -0,0 +1,175 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2007, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "base/Mutex.h" +#include "base/Error.h" +#include "fs/FileNode.h" +#include "fs/Context.h" +#include "fs/FileUtils.h" +#include "fs/DirNode.h" + +using namespace rel; + +EncFS_Context::EncFS_Context() +{ + pthread_cond_init( &wakeupCond, 0 ); + pthread_mutex_init( &wakeupMutex, 0 ); + pthread_mutex_init( &contextMutex, 0 ); + + usageCount = 0; +} + +EncFS_Context::~EncFS_Context() +{ + pthread_mutex_destroy( &contextMutex ); + pthread_mutex_destroy( &wakeupMutex ); + pthread_cond_destroy( &wakeupCond ); + + // release all entries from map + openFiles.clear(); +} + +shared_ptr EncFS_Context::getRoot(int *errCode) +{ + shared_ptr ret; + do + { + { + Lock lock( contextMutex ); + ret = root; + ++usageCount; + } + + if(!ret) + { + int res = remountFS( this ); + if(res != 0) + { + *errCode = res; + break; + } + } + } while(!ret); + + return ret; +} + +void EncFS_Context::setRoot(const shared_ptr &r) +{ + Lock lock( contextMutex ); + + root = r; + if(r) + rootCipherDir = r->rootDirectory(); +} + +bool EncFS_Context::isMounted() +{ + return root; +} + +int EncFS_Context::getAndResetUsageCounter() +{ + Lock lock( contextMutex ); + + int count = usageCount; + usageCount = 0; + + return count; +} + +int EncFS_Context::openFileCount() const +{ + Lock lock( contextMutex ); + + return openFiles.size(); +} + +shared_ptr EncFS_Context::lookupNode(const char *path) +{ + Lock lock( contextMutex ); + + FileMap::iterator it = openFiles.find( std::string(path) ); + if(it != openFiles.end()) + { + // all the items in the set point to the same node.. so just use the + // first + return (*it->second.begin())->node; + } else + { + return shared_ptr(); + } +} + +void EncFS_Context::renameNode(const char *from, const char *to) +{ + Lock lock( contextMutex ); + + FileMap::iterator it = openFiles.find( std::string(from) ); + if(it != openFiles.end()) + { + std::set val = it->second; + openFiles.erase(it); + openFiles[ std::string(to) ] = val; + } +} + +shared_ptr EncFS_Context::getNode(void *pl) +{ + Placeholder *ph = (Placeholder*)pl; + return ph->node; +} + +void *EncFS_Context::putNode(const char *path, + const shared_ptr &node) +{ + Lock lock( contextMutex ); + Placeholder *pl = new Placeholder( node ); + openFiles[ std::string(path) ].insert(pl); + + return (void *)pl; +} + +void EncFS_Context::eraseNode(const char *path, void *pl) +{ + Lock lock( contextMutex ); + + Placeholder *ph = (Placeholder *)pl; + + FileMap::iterator it = openFiles.find( std::string(path) ); + rAssert(it != openFiles.end()); + + int rmCount = it->second.erase( ph ); + + rAssert(rmCount == 1); + + // if no more references to this file, remove the record all together + if(it->second.empty()) + { + // attempts to make use of shallow copy to clear memory used to hold + // unencrypted filenames.. not sure this does any good.. + std::string storedName = it->first; + openFiles.erase( it ); + storedName.assign( storedName.length(), '\0' ); + } + + delete ph; +} + diff --git a/encfs/Context.h b/fs/Context.h similarity index 97% rename from encfs/Context.h rename to fs/Context.h index 282dad5..0f470a4 100644 --- a/encfs/Context.h +++ b/fs/Context.h @@ -21,11 +21,11 @@ #ifndef _Context_incl_ #define _Context_incl_ -#include "encfs.h" -#include "shared_ptr.h" +#include "base/shared_ptr.h" +#include "fs/encfs.h" #include -#if HAVE_TR1_UNORDERED_MAP +#ifdef HAVE_TR1_UNORDERED_MAP #include using std::tr1::unordered_map; #else diff --git a/fs/DirNode.cpp b/fs/DirNode.cpp new file mode 100644 index 0000000..8a967e0 --- /dev/null +++ b/fs/DirNode.cpp @@ -0,0 +1,816 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2003-2004, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "fs/encfs.h" + +#include +#include +#include +#include +#include +#include +#include +#ifdef linux +#include +#endif + +#include + +#include "cipher/Cipher.h" +#include "base/Error.h" +#include "base/Mutex.h" +#include "fs/Context.h" +#include "fs/DirNode.h" +#include "fs/FileUtils.h" + + +#include + +#include + +using namespace std; +using namespace rel; + +class DirDeleter +{ +public: + void operator () ( DIR *d ) + { + ::closedir( d ); + } +}; + + +DirTraverse::DirTraverse(const shared_ptr &_dirPtr, + uint64_t _iv, const shared_ptr &_naming) + : dir( _dirPtr ) + , iv( _iv ) + , naming( _naming ) +{ +} + +DirTraverse::DirTraverse(const DirTraverse &src) + : dir( src.dir ) + , iv( src.iv ) + , naming( src.naming ) +{ +} + +DirTraverse &DirTraverse::operator = (const DirTraverse &src) +{ + dir = src.dir; + iv = src.iv; + naming = src.naming; + + return *this; +} + +DirTraverse::~DirTraverse() +{ + dir.reset(); + iv = 0; + naming.reset(); +} + +static +bool _nextName(struct dirent *&de, const shared_ptr &dir, + int *fileType, ino_t *inode) +{ + de = ::readdir( dir.get() ); + + if(de) + { + if(fileType) + { +#if defined(_DIRENT_HAVE_D_TYPE) || defined(__FreeBSD__) + *fileType = de->d_type; +#else +#warning "struct dirent.d_type not supported" + *fileType = 0; +#endif + } + if(inode) + *inode = de->d_ino; + return true; + } else + { + if(fileType) + *fileType = 0; + return false; + } +} + + +std::string DirTraverse::nextPlaintextName(int *fileType, ino_t *inode) +{ + struct dirent *de=0; + while(_nextName(de, dir, fileType, inode)) + { + try + { + uint64_t localIv = iv; + return naming->decodePath( de->d_name, &localIv ); + } catch ( Error &ex ) + { + // .. .problem decoding, ignore it and continue on to next name.. + VLOG(1) << "error decoding filename " << de->d_name + << " : " << ex.what(); + } + } + + return string(); +} + +std::string DirTraverse::nextInvalid() +{ + struct dirent *de=0; + // find the first name which produces a decoding error... + while(_nextName(de, dir, (int*)0, (ino_t*)0)) + { + try + { + uint64_t localIv = iv; + naming->decodePath( de->d_name, &localIv ); + continue; + } catch( Error &ex ) + { + return string( de->d_name ); + } + } + + return string(); +} + +struct RenameEl +{ + // ciphertext names + string oldCName; + string newCName; // intermediate name (not final cname) + + // plaintext names + string oldPName; + string newPName; + + bool isDirectory; +}; + +class RenameOp +{ +private: + DirNode *dn; + shared_ptr< list > renameList; + list::const_iterator last; + +public: + RenameOp( DirNode *_dn, const shared_ptr< list > &_renameList ) + : dn(_dn), renameList(_renameList) + { + last = renameList->begin(); + } + + RenameOp(const RenameOp &src) + : dn(src.dn) + , renameList(src.renameList) + , last(src.last) + { + } + + ~RenameOp(); + + operator bool () const + { + return renameList; + } + + bool apply(); + void undo(); +}; + +RenameOp::~RenameOp() +{ + if(renameList) + { + // got a bunch of decoded filenames sitting in memory.. do a little + // cleanup before leaving.. + list::iterator it; + for(it = renameList->begin(); it != renameList->end(); ++it) + { + it->oldPName.assign( it->oldPName.size(), ' ' ); + it->newPName.assign( it->newPName.size(), ' ' ); + } + } +} + +bool RenameOp::apply() +{ + try + { + while(last != renameList->end()) + { + // backing store rename. + VLOG(2) << "renaming " << last->oldCName << "-> " << last->newCName; + + struct stat st; + bool preserve_mtime = ::stat(last->oldCName.c_str(), &st) == 0; + + // internal node rename.. + dn->renameNode( last->oldPName.c_str(), + last->newPName.c_str() ); + + // rename on disk.. + if(::rename( last->oldCName.c_str(), + last->newCName.c_str() ) == -1) + { + LOG(WARNING) << "Error renaming " << last->oldCName << ": " << + strerror(errno); + dn->renameNode( last->newPName.c_str(), + last->oldPName.c_str(), false ); + return false; + } + + if(preserve_mtime) + { + struct utimbuf ut; + ut.actime = st.st_atime; + ut.modtime = st.st_mtime; + ::utime(last->newCName.c_str(), &ut); + } + + ++last; + } + + return true; + } catch( Error &err ) + { + LOG(WARNING) << "caught error in rename application: " << err.what(); + return false; + } +} + +void RenameOp::undo() +{ + VLOG(1) << "in undoRename"; + + if(last == renameList->begin()) + { + VLOG(1) << "nothing to undo"; + return; // nothing to undo + } + + // list has to be processed backwards, otherwise we may rename + // directories and directory contents in the wrong order! + int undoCount = 0; + int errorCount = 0; + list::const_iterator it = last; + + while( it != renameList->begin() ) + { + --it; + + VLOG(1) << "undo: renaming " << it->newCName << " -> " << it->oldCName; + + ::rename( it->newCName.c_str(), it->oldCName.c_str() ); + try + { + dn->renameNode( it->newPName.c_str(), + it->oldPName.c_str(), false ); + } catch( Error &err ) + { + if (++errorCount == 1) + LOG(WARNING) << "error in rename und: " << err.what(); + // continue on anyway... + } + ++undoCount; + }; + + LOG(WARNING) << "Undo rename count: " << undoCount; +} + +DirNode::DirNode(EncFS_Context *_ctx, + const string &sourceDir, + const FSConfigPtr &_config) +{ + pthread_mutex_init( &mutex, 0 ); + + Lock _lock( mutex ); + + ctx = _ctx; + rootDir = sourceDir; + fsConfig = _config; + + // make sure rootDir ends in '/', so that we can form a path by appending + // the rest.. + if( rootDir[ rootDir.length()-1 ] != '/' ) + rootDir.append( 1, '/'); + + naming = fsConfig->nameCoding; +} + +DirNode::~DirNode() +{ +} + +bool DirNode::hasDirectoryNameDependency() const +{ + return naming ? naming->getChainedNameIV() : false; +} + +string DirNode::rootDirectory() +{ + // don't update last access here, otherwise 'du' would cause lastAccess to + // be reset. + // chop off '/' terminator from root dir. + return string( rootDir, 0, rootDir.length()-1 ); +} + +string DirNode::cipherPath( const char *plaintextPath ) +{ + return rootDir + naming->encodePath( plaintextPath ); +} + +string DirNode::cipherPathWithoutRoot( const char *plaintextPath ) +{ + return naming->encodePath( plaintextPath ); +} + +string DirNode::plainPath( const char *cipherPath_ ) +{ + try + { + if( !strncmp( cipherPath_, rootDir.c_str(), + rootDir.length() ) ) + { + return naming->decodePath( cipherPath_ + rootDir.length() ); + } else + { + if ( cipherPath_[0] == '+' ) + { + // decode as fully qualified path + return string("/") + naming->decodeName( cipherPath_+1, + strlen(cipherPath_+1) ); + } else + { + return naming->decodePath( cipherPath_ ); + } + } + + } catch( Error &err ) + { + LOG(ERROR) << "decode err: " << err.what(); + return string(); + } +} + +string DirNode::relativeCipherPath( const char *plaintextPath ) +{ + try + { + if(plaintextPath[0] == '/') + { + // mark with '+' to indicate special decoding.. + return string("+") + naming->encodeName(plaintextPath+1, + strlen(plaintextPath+1)); + } else + { + return naming->encodePath( plaintextPath ); + } + } catch( Error &err ) + { + LOG(ERROR) << "encode err: " << err.what(); + return string(); + } +} + +DirTraverse DirNode::openDir(const char *plaintextPath) +{ + string cyName = rootDir + naming->encodePath( plaintextPath ); + //rDebug("openDir on %s", cyName.c_str() ); + + DIR *dir = ::opendir( cyName.c_str() ); + if(dir == NULL) + { + VLOG(1) << "opendir error " << strerror(errno); + return DirTraverse( shared_ptr(), 0, shared_ptr() ); + } else + { + shared_ptr dp( dir, DirDeleter() ); + + uint64_t iv = 0; + // if we're using chained IV mode, then compute the IV at this + // directory level.. + try + { + if( naming->getChainedNameIV() ) + naming->encodePath( plaintextPath, &iv ); + } catch( Error &err ) + { + LOG(ERROR) << "encode err: " << err.what(); + } + return DirTraverse( dp, iv, naming ); + } +} + +bool DirNode::genRenameList( list &renameList, + const char *fromP, const char *toP ) +{ + uint64_t fromIV = 0, toIV = 0; + + // compute the IV for both paths + string fromCPart = naming->encodePath( fromP, &fromIV ); + string toCPart = naming->encodePath( toP, &toIV ); + + // where the files live before the rename.. + string sourcePath = rootDir + fromCPart; + + // ok..... we wish it was so simple.. should almost never happen + if(fromIV == toIV) + return true; + + // generate the real destination path, where we expect to find the files.. + VLOG(1) << "opendir " << sourcePath; + shared_ptr dir = shared_ptr( + opendir( sourcePath.c_str() ), DirDeleter() ); + if(!dir) + return false; + + struct dirent *de = NULL; + while((de = ::readdir( dir.get() )) != NULL) + { + // decode the name using the oldIV + uint64_t localIV = fromIV; + string plainName; + + if((de->d_name[0] == '.') && + ((de->d_name[1] == '\0') + || ((de->d_name[1] == '.') && (de->d_name[2] == '\0')))) + { + // skip "." and ".." + continue; + } + + try + { + plainName = naming->decodePath( de->d_name, &localIV ); + } catch( Error &ex ) + { + // if filename can't be decoded, then ignore it.. + continue; + } + + // any error in the following will trigger a rename failure. + try + { + // re-encode using the new IV.. + localIV = toIV; + string newName = naming->encodePath( plainName.c_str(), &localIV ); + + // store rename information.. + string oldFull = sourcePath + '/' + de->d_name; + string newFull = sourcePath + '/' + newName; + + RenameEl ren; + ren.oldCName = oldFull; + ren.newCName = newFull; + ren.oldPName = string(fromP) + '/' + plainName; + ren.newPName = string(toP) + '/' + plainName; + + bool isDir; +#if defined(_DIRENT_HAVE_D_TYPE) + if(de->d_type != DT_UNKNOWN) + { + isDir = (de->d_type == DT_DIR); + } else +#endif + { + isDir = isDirectory( oldFull.c_str() ); + } + + ren.isDirectory = isDir; + + if(isDir) + { + // recurse.. We want to add subdirectory elements before the + // parent, as that is the logical rename order.. + if(!genRenameList( renameList, + ren.oldPName.c_str(), + ren.newPName.c_str())) + { + return false; + } + } + + VLOG(1) << "adding file " << oldFull << " to rename list"; + renameList.push_back( ren ); + + } catch( Error &err ) + { + // We can't convert this name, because we don't have a valid IV for + // it (or perhaps a valid key).. It will be inaccessible.. + LOG(WARNING) << "Aborting rename: error on file " << + fromCPart.append(1, '/').append(de->d_name) << ":" << err.what(); + + // abort.. Err on the side of safety and disallow rename, rather + // then loosing files.. + return false; + } + } + + return true; +} + + +/* + A bit of a pain.. If a directory is renamed in a filesystem with + directory initialization vector chaining, then we have to recursively + rename every descendent of this directory, as all initialization vectors + will have changed.. + + Returns a list of renamed items on success, a null list on failure. +*/ +shared_ptr +DirNode::newRenameOp( const char *fromP, const char *toP ) +{ + // Do the rename in two stages to avoid chasing our tail + // Undo everything if we encounter an error! + shared_ptr< list > renameList(new list); + if(!genRenameList( *renameList.get(), fromP, toP )) + { + LOG(WARNING) << "Error during generation of recursive rename list"; + return shared_ptr(); + } else + return shared_ptr( new RenameOp(this, renameList) ); +} + + +int DirNode::mkdir(const char *plaintextPath, mode_t mode, + uid_t uid, gid_t gid) +{ + string cyName = rootDir + naming->encodePath( plaintextPath ); + rAssert( !cyName.empty() ); + + VLOG(1) << "mkdir on " << cyName; + + // if uid or gid are set, then that should be the directory owner + int olduid = -1; + int oldgid = -1; + if(uid != 0) + olduid = setfsuid( uid ); + if(gid != 0) + oldgid = setfsgid( gid ); + + int res = ::mkdir( cyName.c_str(), mode ); + + if(olduid >= 0) + setfsuid( olduid ); + if(oldgid >= 0) + setfsgid( oldgid ); + + if(res == -1) + { + int eno = errno; + LOG(WARNING) << "mkdir error on " << cyName + << " mode " << mode << ": " << strerror(eno); + res = -eno; + } else + res = 0; + + return res; +} + +int +DirNode::rename( const char *fromPlaintext, const char *toPlaintext ) +{ + Lock _lock( mutex ); + + string fromCName = rootDir + naming->encodePath( fromPlaintext ); + string toCName = rootDir + naming->encodePath( toPlaintext ); + rAssert( !fromCName.empty() ); + rAssert( !toCName.empty() ); + + VLOG(1) << "rename " << fromCName << " -> " << toCName; + + shared_ptr toNode = findOrCreate( toPlaintext ); + + shared_ptr renameOp; + if( hasDirectoryNameDependency() && isDirectory( fromCName.c_str() )) + { + VLOG(1) << "recursive rename begin"; + renameOp = newRenameOp( fromPlaintext, toPlaintext ); + + if(!renameOp || !renameOp->apply()) + { + if(renameOp) + renameOp->undo(); + + LOG(WARNING) << "rename aborted"; + return -EACCES; + } + VLOG(1) << "recursive rename end"; + } + + int res = 0; + try + { + struct stat st; + bool preserve_mtime = ::stat(fromCName.c_str(), &st) == 0; + + renameNode( fromPlaintext, toPlaintext ); + res = ::rename( fromCName.c_str(), toCName.c_str() ); + + if(res == -1) + { + // undo + res = -errno; + renameNode( toPlaintext, fromPlaintext, false ); + + if(renameOp) + renameOp->undo(); + } else if(preserve_mtime) + { + struct utimbuf ut; + ut.actime = st.st_atime; + ut.modtime = st.st_mtime; + ::utime(toCName.c_str(), &ut); + } + } catch( Error &err ) + { + // exception from renameNode, just show the error and continue.. + LOG(ERROR) << "rename err: " << err.what(); + res = -EIO; + } + + if(res != 0) + { + VLOG(1) << "rename failed: " << strerror( errno ); + res = -errno; + } + + return res; +} + +int DirNode::link( const char *from, const char *to ) +{ + Lock _lock( mutex ); + + string fromCName = rootDir + naming->encodePath( from ); + string toCName = rootDir + naming->encodePath( to ); + + rAssert( !fromCName.empty() ); + rAssert( !toCName.empty() ); + + VLOG(1) << "link " << fromCName << " -> " << toCName; + + int res = -EPERM; + if( fsConfig->config->external_iv() ) + { + VLOG(1) << "hard links not supported with external IV chaining!"; + } else + { + res = ::link( fromCName.c_str(), toCName.c_str() ); + if(res == -1) + res = -errno; + else + res = 0; + } + + return res; +} + +/* + The node is keyed by filename, so a rename means the internal node names + must be changed. +*/ +shared_ptr DirNode::renameNode( const char *from, const char *to ) +{ + return renameNode( from, to, true ); +} + +shared_ptr DirNode::renameNode( const char *from, const char *to, + bool forwardMode ) +{ + shared_ptr node = findOrCreate( from ); + + if(node) + { + uint64_t newIV = 0; + string cname = rootDir + naming->encodePath( to, &newIV ); + + VLOG(1) << "renaming internal node " << node->cipherName() + << " -> " << cname.c_str(); + + if(node->setName( to, cname.c_str(), newIV, forwardMode )) + { + if(ctx) + ctx->renameNode( from, to ); + } else + { + // rename error! - put it back + LOG(ERROR) << "renameNode failed"; + throw Error("Internal node name change failed!"); + } + } + + return node; +} + +shared_ptr DirNode::findOrCreate( const char *plainName) +{ + shared_ptr node; + if(ctx) + node = ctx->lookupNode( plainName ); + + if(!node) + { + uint64_t iv = 0; + string cipherName = naming->encodePath( plainName, &iv ); + node.reset( new FileNode( this, fsConfig, + plainName, + (rootDir + cipherName).c_str())); + + if(fsConfig->config->external_iv()) + node->setName(0, 0, iv); + + VLOG(1) << "created FileNode for " << node->cipherName(); + } + + return node; +} + +shared_ptr +DirNode::lookupNode( const char *plainName, const char * requestor ) +{ + (void)requestor; + Lock _lock( mutex ); + + shared_ptr node = findOrCreate( plainName ); + + return node; +} + +/* + Similar to lookupNode, except that we also call open() and only return a + node on sucess.. This is done in one step to avoid any race conditions + with the stored state of the file. +*/ +shared_ptr +DirNode::openNode( const char *plainName, const char * requestor, int flags, + int *result ) +{ + (void)requestor; + rAssert( result != NULL ); + Lock _lock( mutex ); + + shared_ptr node = findOrCreate( plainName ); + + if( node && (*result = node->open( flags )) >= 0 ) + return node; + else + return shared_ptr(); +} + +int DirNode::unlink( const char *plaintextName ) +{ + string cyName = naming->encodePath( plaintextName ); + VLOG(1) << "unlink " << cyName; + + Lock _lock( mutex ); + + int res = 0; + if(ctx && ctx->lookupNode( plaintextName )) + { + // If FUSE is running with "hard_remove" option where it doesn't + // hide open files for us, then we can't allow an unlink of an open + // file.. + LOG(WARNING) << "Refusing to unlink open file: " + << cyName << ", hard_remove option is probably in effect"; + res = -EBUSY; + } else + { + string fullName = rootDir + cyName; + res = ::unlink( fullName.c_str() ); + if(res == -1) + { + res = -errno; + VLOG(1) << "unlink error: " << strerror(errno); + } + } + + return res; +} diff --git a/encfs/DirNode.h b/fs/DirNode.h similarity index 97% rename from encfs/DirNode.h rename to fs/DirNode.h index a1e3fdf..0c3a344 100644 --- a/encfs/DirNode.h +++ b/fs/DirNode.h @@ -30,11 +30,11 @@ #include #include -#include "FileNode.h" -#include "NameIO.h" -#include "CipherKey.h" -#include "FSConfig.h" -#include "shared_ptr.h" +#include "base/shared_ptr.h" +#include "cipher/CipherKey.h" +#include "fs/FileNode.h" +#include "fs/NameIO.h" +#include "fs/FSConfig.h" class Cipher; class RenameOp; diff --git a/encfs/FSConfig.h b/fs/FSConfig.h similarity index 94% rename from encfs/FSConfig.h rename to fs/FSConfig.h index c777679..75cc003 100644 --- a/encfs/FSConfig.h +++ b/fs/FSConfig.h @@ -21,10 +21,10 @@ #ifndef _FSConfig_incl_ #define _FSConfig_incl_ -#include "encfs.h" -#include "Interface.h" -#include "CipherKey.h" -#include "shared_ptr.h" +#include "base/Interface.h" +#include "base/shared_ptr.h" +#include "cipher/CipherKey.h" +#include "fs/encfs.h" #include @@ -39,7 +39,7 @@ enum ConfigType Config_V7 = 7 }; -class EncFS_Opts; +struct EncFS_Opts; class Cipher; class NameIO; class EncfsConfig; diff --git a/encfs/FileIO.cpp b/fs/FileIO.cpp similarity index 100% rename from encfs/FileIO.cpp rename to fs/FileIO.cpp diff --git a/encfs/FileIO.h b/fs/FileIO.h similarity index 97% rename from encfs/FileIO.h rename to fs/FileIO.h index c97ff2a..0c71213 100644 --- a/encfs/FileIO.h +++ b/fs/FileIO.h @@ -21,12 +21,11 @@ #ifndef _FileIO_incl_ #define _FileIO_incl_ -#include "encfs.h" +#include "base/Interface.h" +#include "fs/encfs.h" #include -#include "Interface.h" - struct IORequest { off_t offset; diff --git a/fs/FileNode.cpp b/fs/FileNode.cpp new file mode 100644 index 0000000..fc5cdc8 --- /dev/null +++ b/fs/FileNode.cpp @@ -0,0 +1,303 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2003-2004, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +// Include encfs first, because we need to include fuse.h before any inclusion +// of sys/stat.h or other system headers (to be safe) +#include "fs/encfs.h" + +#include +#include +#include +#include +#include +#ifdef linux +#include +#endif + +#include + +#include "base/config.h" +#include "base/Error.h" +#include "base/Mutex.h" +#include "cipher/Cipher.h" +#include "cipher/MemoryPool.h" + +#include "fs/FileNode.h" +#include "fs/FileUtils.h" +#include "fs/CipherFileIO.h" +#include "fs/RawFileIO.h" +#include "fs/MACFileIO.h" +#include "fs/DirNode.h" + +#include "fs/FileIO.h" + +#include + +using namespace std; +using namespace rel; + +/* + TODO: locking at the FileNode level is inefficient, since this precludes + multiple concurrent IO operations within the same file. + + There is no reason why simultainous reads cannot be satisfied, or why one + read has to wait for the decoding of the previous read before it can be + sent to the IO subsystem! +*/ + +FileNode::FileNode(DirNode *parent_, const FSConfigPtr &cfg, + const char *plaintextName_, const char *cipherName_) +{ + pthread_mutex_init( &mutex, 0 ); + + Lock _lock( mutex ); + + this->_pname = plaintextName_; + this->_cname = cipherName_; + this->parent = parent_; + + this->fsConfig = cfg; + + // chain RawFileIO & CipherFileIO + shared_ptr rawIO( new RawFileIO( _cname ) ); + io = shared_ptr( new CipherFileIO( rawIO, fsConfig )); + + if(cfg->config->block_mac_bytes() || cfg->config->block_mac_rand_bytes()) + io = shared_ptr(new MACFileIO(io, fsConfig)); +} + +FileNode::~FileNode() +{ + // FileNode mutex should be locked before the destructor is called + //pthread_mutex_lock( &mutex ); + + _pname.assign( _pname.length(), '\0' ); + _cname.assign( _cname.length(), '\0' ); + io.reset(); + + pthread_mutex_destroy( &mutex ); +} + +const char *FileNode::cipherName() const +{ + return _cname.c_str(); +} + +const char *FileNode::plaintextName() const +{ + return _pname.c_str(); +} + +string FileNode::plaintextParent() const +{ + return parentDirectory( _pname ); +} + +static bool setIV(const shared_ptr &io, uint64_t iv) +{ + struct stat stbuf; + if((io->getAttr(&stbuf) < 0) || S_ISREG(stbuf.st_mode)) + return io->setIV( iv ); + else + return true; +} + +bool FileNode::setName( const char *plaintextName_, const char *cipherName_, + uint64_t iv, bool setIVFirst ) +{ + //Lock _lock( mutex ); + VLOG(1) << "calling setIV on " << cipherName_; + if(setIVFirst) + { + if(fsConfig->config->external_iv() && !setIV(io, iv)) + return false; + + // now change the name.. + if(plaintextName_) + this->_pname = plaintextName_; + if(cipherName_) + { + this->_cname = cipherName_; + io->setFileName( cipherName_ ); + } + } else + { + std::string oldPName = _pname; + std::string oldCName = _cname; + + if(plaintextName_) + this->_pname = plaintextName_; + if(cipherName_) + { + this->_cname = cipherName_; + io->setFileName( cipherName_ ); + } + + if(fsConfig->config->external_iv() && !setIV(io, iv)) + { + _pname = oldPName; + _cname = oldCName; + return false; + } + } + + return true; +} + +int FileNode::mknod(mode_t mode, dev_t rdev, uid_t uid, gid_t gid) +{ + Lock _lock( mutex ); + + int res; + int olduid = -1; + int oldgid = -1; + if(uid != 0) + { + olduid = setfsuid( uid ); + if(olduid == -1) + { + LOG(INFO) << "setfsuid error: " << strerror(errno); + return -EPERM; + } + } + if(gid != 0) + { + oldgid = setfsgid( gid ); + if(oldgid == -1) + { + LOG(INFO) << "setfsgid error: " << strerror(errno); + return -EPERM; + } + } + + /* + * cf. xmp_mknod() in fusexmp.c + * The regular file stuff could be stripped off if there + * were a create method (advised to have) + */ + if (S_ISREG( mode )) { + res = ::open( _cname.c_str(), O_CREAT | O_EXCL | O_WRONLY, mode ); + if (res >= 0) + res = ::close( res ); + } else if (S_ISFIFO( mode )) + res = ::mkfifo( _cname.c_str(), mode ); + else + res = ::mknod( _cname.c_str(), mode, rdev ); + + if(olduid >= 0) + setfsuid( olduid ); + if(oldgid >= 0) + setfsgid( oldgid ); + + if(res == -1) + { + int eno = errno; + VLOG(1) << "mknod error: " << strerror(eno); + res = -eno; + } + + return res; +} + +int FileNode::open(int flags) const +{ + Lock _lock( mutex ); + + int res = io->open( flags ); + return res; +} + +int FileNode::getAttr(struct stat *stbuf) const +{ + Lock _lock( mutex ); + + int res = io->getAttr( stbuf ); + return res; +} + +off_t FileNode::getSize() const +{ + Lock _lock( mutex ); + + int res = io->getSize(); + return res; +} + +ssize_t FileNode::read( off_t offset, unsigned char *data, ssize_t size ) const +{ + IORequest req; + req.offset = offset; + req.dataLen = size; + req.data = data; + + Lock _lock( mutex ); + + return io->read( req ); +} + +bool FileNode::write(off_t offset, unsigned char *data, ssize_t size) +{ + VLOG(1) << "FileNode::write offset " << offset + << ", data size " << size; + + IORequest req; + req.offset = offset; + req.dataLen = size; + req.data = data; + + Lock _lock( mutex ); + + return io->write( req ); +} + +int FileNode::truncate( off_t size ) +{ + Lock _lock( mutex ); + + return io->truncate( size ); +} + +int FileNode::sync(bool datasync) +{ + Lock _lock( mutex ); + + int fh = io->open( O_RDONLY ); + if(fh >= 0) + { + int res = -EIO; +#ifdef linux + if(datasync) + res = fdatasync( fh ); + else + res = fsync( fh ); +#else + // no fdatasync support + // TODO: use autoconfig to check for it.. + res = fsync(fh); +#endif + + if(res == -1) + res = -errno; + + return res; + } else + return fh; +} + diff --git a/encfs/FileNode.h b/fs/FileNode.h similarity index 97% rename from encfs/FileNode.h rename to fs/FileNode.h index 0037643..c827a89 100644 --- a/encfs/FileNode.h +++ b/fs/FileNode.h @@ -21,9 +21,9 @@ #ifndef _FileNode_incl_ #define _FileNode_incl_ -#include "encfs.h" -#include "CipherKey.h" -#include "FileUtils.h" +#include "cipher/CipherKey.h" +#include "fs/encfs.h" +#include "fs/FileUtils.h" #include #include diff --git a/encfs/FileUtils.cpp b/fs/FileUtils.cpp similarity index 90% rename from encfs/FileUtils.cpp rename to fs/FileUtils.cpp index b045d54..28ab025 100644 --- a/encfs/FileUtils.cpp +++ b/fs/FileUtils.cpp @@ -24,28 +24,29 @@ #endif #define _BSD_SOURCE // pick up setenv on RH7.3 -#include "encfs.h" -#include "config.h" -#include "config.pb.h" +#include "fs/encfs.h" -#include "readpassphrase.h" -#include "autosprintf.h" +#include "base/autosprintf.h" +#include "base/config.h" +#include "base/config.pb.h" +#include "base/ConfigReader.h" +#include "base/Error.h" +#include "base/i18n.h" +#include "base/XmlReader.h" -#include "FileUtils.h" -#include "ConfigReader.h" -#include "XmlReader.h" -#include "FSConfig.h" +#include "cipher/Cipher.h" +#include "cipher/MemoryPool.h" +#include "cipher/readpassphrase.h" -#include "DirNode.h" -#include "Cipher.h" -#include "StreamNameIO.h" -#include "BlockNameIO.h" -#include "NullNameIO.h" -#include "Context.h" -#include "MemoryPool.h" +#include "fs/BlockNameIO.h" +#include "fs/Context.h" +#include "fs/DirNode.h" +#include "fs/FileUtils.h" +#include "fs/FSConfig.h" +#include "fs/NullNameIO.h" +#include "fs/StreamNameIO.h" -#include -#include +#include #include #include @@ -62,23 +63,16 @@ #include #include -#include "i18n.h" - #include #include -// disable rlog section grouping for this file.. seems to cause problems -#undef RLOG_SECTION -#define RLOG_SECTION - -using namespace rlog; using namespace std; using namespace gnu; -static const int DefaultBlockSize = 1024; +static const int DefaultBlockSize = 2048; // The maximum length of text passwords. If longer are needed, // use the extpass option, as extpass can return arbitrary length binary data. -static const int MaxPassBuf = 512; +static const int MaxPassBuf = 2048; static const int NormalKDFDuration = 500; // 1/2 a second static const int ParanoiaKDFDuration = 3000; // 3 seconds @@ -90,7 +84,7 @@ static const char ENCFS_ENV_STDOUT[] = "encfs_stdout"; static const char ENCFS_ENV_STDERR[] = "encfs_stderr"; const int V5Latest = 20040813; // fix MACFileIO block size issues -const int ProtoSubVersion = 20120819; +const int ProtoSubVersion = 20120902; const char ConfigFileName[] = ".encfs.txt"; @@ -223,12 +217,12 @@ ConfigType readConfig_load( ConfigInfo *nm, const char *path, { if( (*nm->loadFunc)( path, config, nm )) return nm->type; - } catch( rlog::Error & err ) + } catch( Error &err ) { - err.log( _RLWarningChannel ); + LOG(WARNING) << "readConfig failed: " << err.what(); } - rError( _("Found config file %s, but failed to load"), path); + LOG(ERROR) << "Found config file " << path << ", but failed to load"; return Config_None; } else { @@ -269,7 +263,7 @@ bool readV6Config( const char *configFile, XmlReader rdr; if (!rdr.load(configFile)) { - rError("Failed to load config file %s", configFile); + LOG(ERROR) << "Failed to load config file " << configFile; return false; } @@ -279,39 +273,40 @@ bool readV6Config( const char *configFile, config = (*serialization)["config"]; } if (!config) { - rError("Unable to find XML configuration in file %s", configFile); + LOG(ERROR) << "Unable to find XML configuration in file " << configFile; return false; } int version; if (!config->read("version", &version) && !config->read("@version", &version)) { - rError("Unable to find version in config file"); + LOG(ERROR) << "Unable to find version in config file"; return false; } // version numbering was complicated by boost::archive if (version == 20 || version >= 20100713) { - rInfo("found new serialization format"); + VLOG(1) << "found new serialization format"; cfg.set_revision(version); } else if (version == 26800) { - rInfo("found 20080816 version"); + VLOG(1) << "found 20080816 version"; cfg.set_revision(20080816); } else if (version == 26797) { - rInfo("found 20080813"); + VLOG(1) << "found 20080813"; cfg.set_revision(20080813); } else if (version < V5Latest) { - rError("Invalid version %i - please fix config file", version); + LOG(ERROR) << "Invalid version " << version + << " - please fix config file"; } else { - rInfo("Boost <= 1.41 compatibility mode"); + LOG(INFO) << "Boost <= 1.41 compatibility mode"; cfg.set_revision(version); } - rInfo("subVersion = %i", cfg.revision()); + VLOG(1) << "subVersion = " << cfg.revision(); config->read("creator", cfg.mutable_creator()); config->read("cipherAlg", cfg.mutable_cipher()); @@ -390,15 +385,15 @@ bool readV5Config( const char *configFile, { /* config file specifies a version outside our supported range.. */ - rWarning(_("Config subversion %i found, but this version of" - " encfs only supports up to version %i."), - config.revision(), V5Latest); + LOG(ERROR) << "Config subversion " << config.revision() + << " found, but this version of encfs only supports up to version " + << V5Latest; return false; } if( config.revision() < V5Latest ) { - rError(_("This version of EncFS doesn't support " - "filesystems created before 2004-08-13")); + LOG(ERROR) << "This version of EncFS doesn't support " + << "filesystems created with EncFS releases before 2004-08-13"; return false; } @@ -423,10 +418,10 @@ bool readV5Config( const char *configFile, config.set_block_mac_rand_bytes( cfgRdr["blockMACRandBytes"].readInt(0) ); ok = true; - } catch( rlog::Error &err) + } catch( Error &err) { - err.log( _RLWarningChannel ); - rDebug("Error parsing data in config file %s", configFile); + LOG(WARNING) << "Error parsing data in config file " << configFile + << "; " << err.what(); ok = false; } } @@ -458,10 +453,10 @@ bool readV4Config( const char *configFile, config.set_creator( "EncFS 1.0.x" ); ok = true; - } catch( rlog::Error &err) + } catch( Error &err) { - err.log( _RLWarningChannel ); - rDebug("Error parsing config file %s", configFile); + LOG(WARNING) << "Error parsing config file " << configFile + << ": " << err.what(); ok = false; } } @@ -474,7 +469,7 @@ bool writeTextConfig( const char *fileName, const EncfsConfig &cfg ) int fd = ::open( fileName, O_RDWR | O_CREAT, 0640 ); if (fd < 0) { - rError("Unable to open or create file %s", fileName); + LOG(ERROR) << "Unable to open or create file " << fileName; return false; } @@ -503,10 +498,11 @@ bool saveConfig( const string &rootDir, const EncfsConfig &config ) try { + const_cast(config).set_writer("EncFS " VERSION ); ok = writeTextConfig( path.c_str(), config ); - } catch( rlog::Error &err ) + } catch( Error &err ) { - err.log( _RLWarningChannel ); + LOG(WARNING) << "saveConfig failed: " << err.what(); ok = false; } @@ -519,7 +515,7 @@ bool readProtoConfig( const char *fileName, EncfsConfig &config, int fd = ::open( fileName, O_RDONLY, 0640 ); if (fd < 0) { - rError("Unable to open file %s", fileName); + LOG(ERROR) << "Unable to open file " << fileName; return false; } @@ -623,7 +619,7 @@ Cipher::CipherAlgorithm selectCipherAlgorithm() } static -Interface selectNameCoding() +Interface selectNameCoding(const Cipher::CipherAlgorithm &alg) { for(;;) { @@ -634,10 +630,15 @@ Interface selectNameCoding() NameIO::AlgorithmList algorithms = NameIO::GetAlgorithmList(); NameIO::AlgorithmList::const_iterator it; int optNum = 1; - for(it = algorithms.begin(); it != algorithms.end(); ++it, ++optNum) + map algMap; + for(it = algorithms.begin(); it != algorithms.end(); ++it) { + if (it->needsStreamMode && !alg.hasStreamMode) + continue; + cout << optNum << ". " << it->name << " : " << gettext(it->description.c_str()) << "\n"; + algMap[optNum++] = it; } // xgroup(setup) @@ -647,15 +648,13 @@ Interface selectNameCoding() int algNum = (res == 0 ? 0 : atoi( answer )); cout << "\n"; - if( algNum < 1 || algNum > (int)algorithms.size() ) + if( algNum < 1 || algNum >= optNum ) { cerr << _("Invalid selection.") << "\n"; continue; } - it = algorithms.begin(); - while(--algNum) // numbering starts at 1 - ++it; + it = algMap[algNum]; // xgroup(setup) cout << autosprintf(_("Selected algorithm \"%s\""), it->name.c_str()) @@ -940,7 +939,7 @@ RootPtr createConfig( EncFS_Context *ctx, { if (reverseEncryption) { - rError(_("Paranoia configuration not supported for --reverse")); + LOG(ERROR) << "Paranoia configuration not supported for --reverse"; return rootInfo; } @@ -953,7 +952,7 @@ RootPtr createConfig( EncFS_Context *ctx, // Enable filename initialization vector chaning keySize = 256; blockSize = DefaultBlockSize; - alg = findCipherAlgorithm("AES", keySize); + alg = findCipherAlgorithm("AES_XTS", keySize); nameIOIface = BlockNameIO::CurrentInterface(); blockMACBytes = 8; blockMACRandBytes = 0; // using uniqueIV, so this isn't necessary @@ -1004,7 +1003,7 @@ RootPtr createConfig( EncFS_Context *ctx, alg = selectCipherAlgorithm(); keySize = selectKeySize( alg ); blockSize = selectBlockSize( alg ); - nameIOIface = selectNameCoding(); + nameIOIface = selectNameCoding( alg ); if (reverseEncryption) { cout << _("--reverse specified, not using unique/chained IV") << "\n"; @@ -1030,18 +1029,22 @@ RootPtr createConfig( EncFS_Context *ctx, shared_ptr cipher = Cipher::New( alg.name, keySize ); if(!cipher) { - rError(_("Unable to instanciate cipher %s, key size %i, block size %i"), - alg.name.c_str(), keySize, blockSize); + LOG(ERROR) << "Unable to instanciate cipher " << alg.name + << ", key size " << keySize << ", block size " << blockSize; return rootInfo; } else { - rDebug("Using cipher %s, key size %i, block size %i", - alg.name.c_str(), keySize, blockSize); + VLOG(1) << "Using cipher " << alg.name + << ", key size " << keySize << ", block size " << blockSize; } EncfsConfig config; config.mutable_cipher()->MergeFrom( cipher->interface() ); + // TODO: allow user config + if (!cipher->hasStreamMode()) + config.set_block_mode_only(true); + config.set_block_size( blockSize ); config.mutable_naming()->MergeFrom( nameIOIface ); config.set_creator( "EncFS " VERSION ); @@ -1093,7 +1096,7 @@ RootPtr createConfig( EncFS_Context *ctx, // get user key and use it to encode volume key CipherKey userKey; - rDebug( "useStdin: %i", useStdin ); + VLOG(1) << "useStdin: " << useStdin; if(useStdin) { if (annotate) @@ -1109,8 +1112,8 @@ RootPtr createConfig( EncFS_Context *ctx, if(!volumeKey) { - rWarning(_("Failure generating new volume key! " - "Please report this error.")); + LOG(ERROR) << "Failure generating new volume key! " + << "Please report this error."; return rootInfo; } @@ -1122,7 +1125,7 @@ RootPtr createConfig( EncFS_Context *ctx, cipher, volumeKey ); if(!nameCoder) { - rWarning(_("Name coding interface not supported")); + LOG(WARNING) << "Name coding interface not supported"; cout << _("The filename encoding interface requested is not available") << endl; return rootInfo; @@ -1175,12 +1178,17 @@ void showFSInfo( const EncfsConfig &config ) cout << "\n"; } } - { - // xgroup(diag) - cout << autosprintf(_("Filename encoding: \"%s\", version %i:%i:%i"), - config.naming().name().c_str(), config.naming().major(), - config.naming().minor(), config.naming().age()); + // xgroup(diag) + cout << autosprintf(_("Filename encoding: \"%s\", version %i:%i:%i"), + config.naming().name().c_str(), config.naming().major(), + config.naming().minor(), config.naming().age()); + + if (!cipher) + { + cout << "\n"; + } else + { // check if we support the filename encoding interface.. shared_ptr nameCoder = NameIO::New( config.naming(), cipher, CipherKey() ); @@ -1312,8 +1320,9 @@ CipherKey decryptKey(const EncfsConfig &config, const char *password, int passwd iterations, key.kdf_duration(), (const unsigned char *)key.salt().data(), key.salt().size()); - if (iterations != key.kdf_iterations()) { - rError("Error in KDF, iteration mismatch"); + if (iterations != key.kdf_iterations()) + { + LOG(ERROR) << "Error in KDF, iteration mismatch"; return userKey; } } else @@ -1366,7 +1375,7 @@ SecureMem *passwordFromProgram(const std::string &passProg, perror(_("Internal error: socketpair() failed")); return NULL; } - rDebug("getUserKey: fds = %i, %i", fds[0], fds[1]); + VLOG(1) << "getUserKey: fds = " << fds[0] << ", " << fds[1]; pid = fork(); if(pid == -1) @@ -1570,11 +1579,10 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr &opts ) shared_ptr cipher = getCipher(config); if(!cipher) { - rError(_("Unable to find cipher %s, version %i:%i:%i"), - config.cipher().name().c_str(), - config.cipher().major(), - config.cipher().minor(), - config.cipher().age()); + Interface iface = config.cipher(); + LOG(ERROR) << "Unable to find cipher " << iface.name() + << ", version " << iface.major() + << ":" << iface.minor() << ":" << iface.age(); // xgroup(diag) cout << _("The requested cipher interface is not available\n"); return rootInfo; @@ -1594,7 +1602,7 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr &opts ) if(!userKey) return rootInfo; - rDebug("cipher encoded key size = %i", cipher->encodedKeySize()); + VLOG(1) << "cipher encoded key size = " << cipher->encodedKeySize(); // decode volume key.. CipherKey volumeKey = cipher->readKey( (const unsigned char *)config.key().ciphertext().data(), userKey, opts->checkKey); @@ -1611,11 +1619,10 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr &opts ) cipher, volumeKey ); if(!nameCoder) { - rError(_("Unable to find nameio interface %s, version %i:%i:%i"), - config.naming().name().c_str(), - config.naming().major(), - config.naming().minor(), - config.naming().age()); + Interface iface = config.naming(); + LOG(ERROR) << "Unable to find nameio interface " << iface.name() + << ", version " << iface.major() + << ":" << iface.minor() << ":" << iface.age(); // xgroup(diag) cout << _("The requested filename coding interface is " "not available\n"); @@ -1653,7 +1660,7 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr &opts ) int remountFS(EncFS_Context *ctx) { - rDebug("Attempting to reinitialize filesystem"); + VLOG(1) << "Attempting to reinitialize filesystem"; RootPtr rootInfo = initFS( ctx, ctx->opts ); if(rootInfo) @@ -1662,7 +1669,7 @@ int remountFS(EncFS_Context *ctx) return 0; } else { - rInfo(_("Remount failed")); + LOG(WARNING) << "Remount failed"; return -EACCES; } } diff --git a/encfs/FileUtils.h b/fs/FileUtils.h similarity index 97% rename from encfs/FileUtils.h rename to fs/FileUtils.h index 9cb058e..83b4f56 100644 --- a/encfs/FileUtils.h +++ b/fs/FileUtils.h @@ -21,10 +21,10 @@ #ifndef _FileUtils_incl_ #define _FileUtils_incl_ -#include "encfs.h" -#include "Interface.h" -#include "CipherKey.h" -#include "FSConfig.h" +#include "base/Interface.h" +#include "cipher/CipherKey.h" +#include "fs/encfs.h" +#include "fs/FSConfig.h" // true if the path points to an existing node (of any type) bool fileExists( const char *fileName ); diff --git a/fs/MACFileIO.cpp b/fs/MACFileIO.cpp new file mode 100644 index 0000000..430464b --- /dev/null +++ b/fs/MACFileIO.cpp @@ -0,0 +1,295 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "fs/MACFileIO.h" + +#include "base/config.pb.h" +#include "base/Error.h" +#include "base/i18n.h" +#include "cipher/MemoryPool.h" +#include "fs/FileUtils.h" + +#include + +#include + +using namespace std; + +// +// Version 1.0 worked on blocks of size (blockSize + headerSize). +// That is, it took [blockSize] worth of user data and added headers. +// Version 2.0 takes [blockSize - headerSize] worth of user data and writes +// [blockSize] bytes. That way the size going into the crypto engine is +// valid from what was selected based on the crypto module allowed ranges! +// Version 2.1 allows per-block rand bytes to be used without enabling MAC. +// +// The information about MACFileIO currently does not make its way into the +// configuration file, so there is no easy way to make this backward +// compatible, except at a high level by checking a revision number for the +// filesystem... +// +static Interface MACFileIO_iface = makeInterface("FileIO/MAC", 2, 1, 0); + +int dataBlockSize(const FSConfigPtr &cfg) +{ + return cfg->config->block_size() + - cfg->config->block_mac_bytes() + - cfg->config->block_mac_rand_bytes(); +} + +MACFileIO::MACFileIO( const shared_ptr &_base, + const FSConfigPtr &cfg ) + : BlockFileIO( dataBlockSize( cfg ), cfg ) + , base( _base ) + , cipher( cfg->cipher ) + , key( cfg->key ) + , macBytes( cfg->config->block_mac_bytes() ) + , randBytes( cfg->config->block_mac_rand_bytes() ) + , warnOnly( cfg->opts->forceDecode ) +{ + rAssert( macBytes >= 0 && macBytes <= 8 ); + rAssert( randBytes >= 0 ); + VLOG(1) << "fs block size = " << cfg->config->block_size() + << ", macBytes = " << cfg->config->block_mac_bytes() + << ", randBytes = " << cfg->config->block_mac_rand_bytes(); +} + +MACFileIO::~MACFileIO() +{ +} + +Interface MACFileIO::interface() const +{ + return MACFileIO_iface; +} + +int MACFileIO::open( int flags ) +{ + return base->open( flags ); +} + +void MACFileIO::setFileName( const char *fileName ) +{ + base->setFileName( fileName ); +} + +const char *MACFileIO::getFileName() const +{ + return base->getFileName(); +} + +bool MACFileIO::setIV( uint64_t iv ) +{ + return base->setIV( iv ); +} + +inline static off_t roundUpDivide( off_t numerator, int denominator ) +{ + // integer arithmetic always rounds down, so we can round up by adding + // enough so that any value other then a multiple of denominator gets + // rouned to the next highest value. + return ( numerator + denominator - 1 ) / denominator; +} + +// Convert from a location in the raw file to a location when MAC headers are +// interleved with the data. +// So, if the filesystem stores/encrypts [blockSize] bytes per block, then +// [blockSize - headerSize] of those bytes will contain user-supplied data, +// and the rest ([headerSize]) will contain the MAC header for this block. +// Example, offset points to second block (of user-data) +// offset = blockSize - headerSize +// ... blockNum = 1 +// ... partialBlock = 0 +// ... adjLoc = 1 * blockSize +static off_t locWithHeader( off_t offset, int blockSize, int headerSize ) +{ + off_t blockNum = roundUpDivide( offset , blockSize - headerSize ); + return offset + blockNum * headerSize; +} + +// convert from a given location in the stream containing headers, and return a +// location in the user-data stream (which doesn't contain MAC headers).. +// The output value will always be less then the input value, because the +// headers are stored at the beginning of the block, so even the first data is +// offset by the size of the header. +static off_t locWithoutHeader( off_t offset, int blockSize, int headerSize ) +{ + off_t blockNum = roundUpDivide( offset , blockSize ); + return offset - blockNum * headerSize; +} + +int MACFileIO::getAttr( struct stat *stbuf ) const +{ + int res = base->getAttr( stbuf ); + + if(res == 0 && S_ISREG(stbuf->st_mode)) + { + // have to adjust size field.. + int headerSize = macBytes + randBytes; + int bs = blockSize() + headerSize; + stbuf->st_size = locWithoutHeader( stbuf->st_size, bs, headerSize ); + } + + return res; +} + +off_t MACFileIO::getSize() const +{ + // adjust the size to hide the header overhead we tack on.. + int headerSize = macBytes + randBytes; + int bs = blockSize() + headerSize; + + off_t size = base->getSize(); + if(size > 0) + size = locWithoutHeader( size, bs, headerSize ); + + return size; +} + +ssize_t MACFileIO::readOneBlock( const IORequest &req ) const +{ + int headerSize = macBytes + randBytes; + + int bs = blockSize() + headerSize; + + MemBlock mb; + mb.allocate( bs ); + + IORequest tmp; + tmp.offset = locWithHeader( req.offset, bs, headerSize ); + tmp.data = mb.data; + tmp.dataLen = headerSize + req.dataLen; + + // get the data from the base FileIO layer + ssize_t readSize = base->read( tmp ); + + // don't store zeros if configured for zero-block pass-through + bool skipBlock = true; + if( _allowHoles ) + { + for(int i=0; i 0) + skipBlock = false; + + if(readSize > headerSize) + { + if(!skipBlock) + { + // At this point the data has been decoded. So, compute the MAC of + // the block and check against the checksum stored in the header.. + uint64_t mac = cipher->MAC_64( tmp.data + macBytes, + readSize - macBytes, key ); + + for(int i=0; i>= 8) + { + int test = mac & 0xff; + int stored = tmp.data[i]; + if(test != stored) + { + // uh oh.. + long blockNum = req.offset / bs; + LOG(WARNING) << "MAC comparison failure in block " << blockNum; + if( !warnOnly ) + { + throw Error( + _("MAC comparison failure, refusing to read")); + } + break; + } + } + } + + // now copy the data to the output buffer + readSize -= headerSize; + memcpy( req.data, tmp.data + headerSize, readSize ); + } else + { + VLOG(1) << "readSize " << readSize << " at offset " << req.offset; + if(readSize > 0) + readSize = 0; + } + + return readSize; +} + +bool MACFileIO::writeOneBlock( const IORequest &req ) +{ + int headerSize = macBytes + randBytes; + + int bs = blockSize() + headerSize; + + // we have the unencrypted data, so we need to attach a header to it. + MemBlock mb; + mb.allocate( bs ); + + IORequest newReq; + newReq.offset = locWithHeader( req.offset, bs, headerSize ); + newReq.data = mb.data; + newReq.dataLen = headerSize + req.dataLen; + + memset( newReq.data, 0, headerSize ); + memcpy( newReq.data + headerSize, req.data, req.dataLen ); + if(randBytes > 0) + { + if(!cipher->randomize( newReq.data+macBytes, randBytes, false )) + return false; + } + + if(macBytes > 0) + { + // compute the mac (which includes the random data) and fill it in + uint64_t mac = cipher->MAC_64( newReq.data+macBytes, + req.dataLen + randBytes, key ); + + for(int i=0; i>= 8; + } + } + + // now, we can let the next level have it.. + bool ok = base->write( newReq ); + + return ok; +} + +int MACFileIO::truncate( off_t size ) +{ + int headerSize = macBytes + randBytes; + int bs = blockSize() + headerSize; + + int res = blockTruncate( size, 0 ); + + if(res == 0) + base->truncate( locWithHeader( size, bs, headerSize ) ); + + return res; +} + +bool MACFileIO::isWritable() const +{ + return base->isWritable(); +} diff --git a/encfs/MACFileIO.h b/fs/MACFileIO.h similarity index 97% rename from encfs/MACFileIO.h rename to fs/MACFileIO.h index eba1b4b..0a27709 100644 --- a/encfs/MACFileIO.h +++ b/fs/MACFileIO.h @@ -21,8 +21,8 @@ #ifndef _MACFileIO_incl_ #define _MACFileIO_incl_ -#include "BlockFileIO.h" -#include "Cipher.h" +#include "cipher/Cipher.h" +#include "fs/BlockFileIO.h" class MACFileIO : public BlockFileIO { diff --git a/fs/MemBlockFileIO.cpp b/fs/MemBlockFileIO.cpp new file mode 100644 index 0000000..5192f79 --- /dev/null +++ b/fs/MemBlockFileIO.cpp @@ -0,0 +1,76 @@ + +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2012 Valient Gough + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "fs/MemBlockFileIO.h" +#include "fs/MemFileIO.h" + +#include + +static Interface MemBlockFileIO_iface = makeInterface("FileIO/MemBlock", + 1, 0, 0); + +MemBlockFileIO::MemBlockFileIO(int blockSize, const FSConfigPtr &cfg) + : BlockFileIO(blockSize, cfg), impl(new MemFileIO(0)) { +} + +MemBlockFileIO::~MemBlockFileIO() { +} + +Interface MemBlockFileIO::interface() const { + return MemBlockFileIO_iface; +} + +void MemBlockFileIO::setFileName(const char *name) { + return impl->setFileName(name); +} + +const char *MemBlockFileIO::getFileName() const { + return impl->getFileName(); +} + +int MemBlockFileIO::open(int flags) { + return impl->open(flags); +} + +int MemBlockFileIO::getAttr(struct stat* stbuf) const { + return impl->getAttr(stbuf); +} + +off_t MemBlockFileIO::getSize() const { + return impl->getSize(); +} + +ssize_t MemBlockFileIO::readOneBlock(const IORequest& req) const { + return impl->read(req); +} + +bool MemBlockFileIO::writeOneBlock(const IORequest& req) { + return impl->write(req); +} + +int MemBlockFileIO::truncate(off_t size) { + return impl->truncate(size); +} + +bool MemBlockFileIO::isWritable() const { + return impl->isWritable(); +} + diff --git a/fs/MemBlockFileIO.h b/fs/MemBlockFileIO.h new file mode 100644 index 0000000..82f8484 --- /dev/null +++ b/fs/MemBlockFileIO.h @@ -0,0 +1,59 @@ + +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2012 Valient Gough + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef _MEMBLOCKFILEIO_incl_ +#define _MEMBLOCKFILEIO_incl_ + +#include "fs/BlockFileIO.h" + +#include +#include + +class MemFileIO; + +class MemBlockFileIO : public BlockFileIO { + public: + MemBlockFileIO(int blockSize, const FSConfigPtr &cfg); + virtual ~MemBlockFileIO(); + + virtual Interface interface() const; + + virtual void setFileName(const char *name); + virtual const char *getFileName() const; + + virtual int open(int flags); + + virtual int getAttr(struct stat *stbuf) const; + virtual off_t getSize() const; + + virtual bool isWritable() const; + + virtual int truncate(off_t size); + protected: + virtual ssize_t readOneBlock(const IORequest &req) const; + virtual bool writeOneBlock(const IORequest &req); + + private: + MemFileIO *impl; +}; + +#endif + diff --git a/fs/MemFileIO.cpp b/fs/MemFileIO.cpp new file mode 100644 index 0000000..d24e583 --- /dev/null +++ b/fs/MemFileIO.cpp @@ -0,0 +1,106 @@ + +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2012 Valient Gough + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "fs/MemFileIO.h" + +#include "base/Error.h" + +#include + +static Interface MemFileIO_iface = makeInterface("FileIO/Mem", 1, 0, 0); + +MemFileIO* NewMemFileIO(const Interface& iface) { + (void)iface; + return new MemFileIO(0); +} + +MemFileIO::MemFileIO(int size) + : writable(false) { + buf.resize(size); +} + +MemFileIO::~MemFileIO() { +} + +Interface MemFileIO::interface() const { + return MemFileIO_iface; +} + +void MemFileIO::setFileName(const char *name) { + this->name = name; +} + +const char *MemFileIO::getFileName() const { + return name.c_str(); +} + +int MemFileIO::open(int flags) { + bool requestWrite = ((flags & O_RDWR) || (flags & O_WRONLY)); + + writable = writable || requestWrite; + LOG(ERROR) << "returning fake file descriptor"; + return 0; +} + +int MemFileIO::getAttr(struct stat* stbuf) const { + stbuf->st_size = buf.size(); + return 0; +} + +off_t MemFileIO::getSize() const { + return buf.size(); +} + +ssize_t MemFileIO::read(const IORequest& req) const { + rAssert(req.offset >= 0); + + int len = req.dataLen; + if (req.offset + req.dataLen > getSize()) { + len = getSize() - req.offset; + } + if (len < 0) { + len = 0; + } + + memcpy(req.data, &buf[req.offset], len); + return len; +} + +bool MemFileIO::write(const IORequest& req) { + rAssert(req.offset >= 0); + if (req.offset + req.dataLen > getSize()) { + truncate(req.offset + req.dataLen); + } + rAssert(req.offset + req.dataLen <= getSize()); + + memcpy(&buf[req.offset], req.data, req.dataLen); + return true; +} + +int MemFileIO::truncate(off_t size) { + buf.resize(size); + return 0; +} + +bool MemFileIO::isWritable() const { + return writable; +} + diff --git a/fs/MemFileIO.h b/fs/MemFileIO.h new file mode 100644 index 0000000..894f229 --- /dev/null +++ b/fs/MemFileIO.h @@ -0,0 +1,58 @@ + +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2012 Valient Gough + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef _MEMFILEIO_incl_ +#define _MEMFILEIO_incl_ + +#include "FileIO.h" + +#include +#include + +class MemFileIO : public FileIO { + public: + MemFileIO(int size); + virtual ~MemFileIO(); + + virtual Interface interface() const; + + virtual void setFileName(const char *name); + virtual const char *getFileName() const; + + virtual int open(int flags); + + virtual int getAttr(struct stat *stbuf) const; + virtual off_t getSize() const; + + virtual ssize_t read(const IORequest& req) const; + virtual bool write(const IORequest& req); + + virtual int truncate(off_t size); + virtual bool isWritable() const; + + private: + std::vector buf; + std::string name; + bool writable; +}; + +#endif + diff --git a/fs/NameIO.cpp b/fs/NameIO.cpp new file mode 100644 index 0000000..a3795ff --- /dev/null +++ b/fs/NameIO.cpp @@ -0,0 +1,338 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "base/config.h" +#include "base/Error.h" +#include "fs/NameIO.h" + +#include + +#include +#include + +// for static build. Need to reference the modules which are registered at +// run-time, to ensure that the linker doesn't optimize them away. +#include +#include "fs/BlockNameIO.h" +#include "fs/StreamNameIO.h" +#include "fs/NullNameIO.h" + +using namespace std; + +#define REF_MODULE(TYPE) \ + do { \ + if(!TYPE::Enabled() ) \ + cerr << "referenceModule: should never happen\n"; \ + } while(0) + +static +void AddSymbolReferences() +{ + REF_MODULE(BlockNameIO); + REF_MODULE(StreamNameIO); + REF_MODULE(NullNameIO); +} + + +struct NameIOAlg +{ + bool hidden; + NameIO::Constructor constructor; + string description; + Interface iface; + bool needsStreamMode; +}; + +typedef multimap< string, NameIOAlg > NameIOMap_t; +static NameIOMap_t *gNameIOMap = 0; + + +list< NameIO::Algorithm > NameIO::GetAlgorithmList( bool includeHidden ) +{ + AddSymbolReferences(); + + list< Algorithm > result; + if(gNameIOMap) + { + NameIOMap_t::const_iterator it; + NameIOMap_t::const_iterator end = gNameIOMap->end(); + for(it = gNameIOMap->begin(); it != end; ++it) + { + if(includeHidden || !it->second.hidden) + { + Algorithm tmp; + tmp.name = it->first; + tmp.description = it->second.description; + tmp.iface = it->second.iface; + tmp.needsStreamMode = it->second.needsStreamMode; + + result.push_back( tmp ); + } + } + } + + return result; +} + +bool NameIO::Register( const char *name, const char *description, + const Interface &iface, Constructor constructor, + bool needsStreamMode, + bool hidden ) +{ + if( !gNameIOMap ) + gNameIOMap = new NameIOMap_t; + + NameIOAlg alg; + alg.hidden = hidden; + alg.constructor = constructor; + alg.description = description; + alg.iface = iface; + alg.needsStreamMode = needsStreamMode; + + gNameIOMap->insert( make_pair( string(name), alg )); + return true; +} + +shared_ptr NameIO::New( const string &name, + const shared_ptr &cipher, const CipherKey &key) +{ + shared_ptr result; + if(gNameIOMap) + { + NameIOMap_t::const_iterator it = gNameIOMap->find( name ); + if(it != gNameIOMap->end()) + { + Constructor fn = it->second.constructor; + result = (*fn)( it->second.iface, cipher, key ); + } + } + return result; +} + +shared_ptr NameIO::New( const Interface &iface, + const shared_ptr &cipher, const CipherKey &key ) +{ + shared_ptr result; + if(gNameIOMap) + { + NameIOMap_t::const_iterator it; + NameIOMap_t::const_iterator end = gNameIOMap->end(); + for(it = gNameIOMap->begin(); it != end; ++it) + { + if( implements(it->second.iface, iface )) + { + Constructor fn = it->second.constructor; + result = (*fn)( iface, cipher, key ); + break; + } + } + } + return result; +} + + + +NameIO::NameIO() + : chainedNameIV( false ), reverseEncryption( false ) +{ +} + +NameIO::~NameIO() +{ +} + +void NameIO::setChainedNameIV( bool enable ) +{ + chainedNameIV = enable; +} + +bool NameIO::getChainedNameIV() const +{ + return chainedNameIV; +} + +void NameIO::setReverseEncryption( bool enable ) +{ + reverseEncryption = enable; +} + +bool NameIO::getReverseEncryption() const +{ + return reverseEncryption; +} + + +std::string NameIO::recodePath( const char *path, + int (NameIO::*_length)(int) const, + int (NameIO::*_code)(const char*, int, uint64_t *, char*) const, + uint64_t *iv ) const +{ + string output; + + while( *path ) + { + if( *path == '/' ) + { + if( !output.empty() ) // don't start the string with '/' + output += '/'; + ++path; + } else + { + bool isDotFile = (*path == '.'); + const char *next = strchr( path, '/' ); + int len = next ? next - path : strlen( path ); + + // at this point we know that len > 0 + if( isDotFile && (path[len-1] == '.') && (len <= 2) ) + { + output.append(len, '.'); // append [len] copies of '.' + path += len; + continue; + } + + // figure out buffer sizes + int approxLen = (this->*_length)( len ); + if(approxLen <= 0) + throw Error("Filename too small to decode"); + + BUFFER_INIT( codeBuf, 32, (unsigned int)approxLen+1 ); + + // code the name + int codedLen = (this->*_code)( path, len, iv, codeBuf ); + rAssert( codedLen <= approxLen ); + rAssert( codeBuf[codedLen] == '\0' ); + path += len; + + // append result to string + output += (char*)codeBuf; + + BUFFER_RESET( codeBuf ); + } + } + + return output; +} + +std::string NameIO::encodePath( const char *plaintextPath ) const +{ + uint64_t iv = 0; + return encodePath( plaintextPath, &iv); +} + +std::string NameIO::decodePath( const char *cipherPath ) const +{ + uint64_t iv = 0; + return decodePath( cipherPath, &iv ); +} + +std::string NameIO::_encodePath( const char *plaintextPath, uint64_t *iv ) const +{ + // if chaining is not enabled, then the iv pointer is not used.. + if(!chainedNameIV) + iv = 0; + return recodePath( plaintextPath, + &NameIO::maxEncodedNameLen, &NameIO::encodeName, iv); +} + +std::string NameIO::_decodePath( const char *cipherPath, uint64_t *iv ) const +{ + // if chaining is not enabled, then the iv pointer is not used.. + if(!chainedNameIV) + iv = 0; + return recodePath( cipherPath, + &NameIO::maxDecodedNameLen, &NameIO::decodeName, iv); +} + +std::string NameIO::encodePath( const char *path, uint64_t *iv ) const +{ + return getReverseEncryption() ? + _decodePath( path, iv ) : + _encodePath( path, iv ); +} + +std::string NameIO::decodePath( const char *path, uint64_t *iv ) const +{ + return getReverseEncryption() ? + _encodePath( path, iv ) : + _decodePath( path, iv ); +} + + +int NameIO::encodeName( const char *input, int length, char *output ) const +{ + return encodeName( input, length, (uint64_t*)0, output ); +} + +int NameIO::decodeName( const char *input, int length, char *output ) const +{ + return decodeName( input, length, (uint64_t*)0, output ); +} + +std::string NameIO::_encodeName( const char *plaintextName, int length ) const +{ + int approxLen = maxEncodedNameLen( length ); + + BUFFER_INIT( codeBuf, 32, (unsigned int)approxLen+1 ); + + // code the name + int codedLen = encodeName( plaintextName, length, 0, codeBuf ); + rAssert( codedLen <= approxLen ); + rAssert( codeBuf[codedLen] == '\0' ); + + // append result to string + std::string result = (char*)codeBuf; + + BUFFER_RESET( codeBuf ); + + return result; +} + +std::string NameIO::_decodeName( const char *encodedName, int length ) const +{ + int approxLen = maxDecodedNameLen( length ); + + BUFFER_INIT( codeBuf, 32, (unsigned int)approxLen+1 ); + + // code the name + int codedLen = decodeName( encodedName, length, 0, codeBuf ); + rAssert( codedLen <= approxLen ); + rAssert( codeBuf[codedLen] == '\0' ); + + // append result to string + std::string result = (char*)codeBuf; + + BUFFER_RESET( codeBuf ); + + return result; +} + +std::string NameIO::encodeName( const char *path, int length ) const +{ + return getReverseEncryption() ? + _decodeName( path, length ) : + _encodeName( path, length ); +} + +std::string NameIO::decodeName( const char *path, int length ) const +{ + return getReverseEncryption() ? + _encodeName( path, length ) : + _decodeName( path, length ); +} diff --git a/encfs/NameIO.h b/fs/NameIO.h similarity index 93% rename from encfs/NameIO.h rename to fs/NameIO.h index 73e0800..7761a06 100644 --- a/encfs/NameIO.h +++ b/fs/NameIO.h @@ -26,8 +26,8 @@ #include -#include "Interface.h" -#include "CipherKey.h" +#include "base/Interface.h" +#include "cipher/CipherKey.h" class Cipher; @@ -42,6 +42,7 @@ public: std::string name; std::string description; Interface iface; + bool needsStreamMode; }; typedef std::list AlgorithmList; @@ -54,6 +55,7 @@ public: static bool Register( const char *name, const char *description, const Interface &iface, Constructor constructor, + bool needsStreamMode, bool hidden = false); @@ -121,17 +123,19 @@ private: char Name ## _Raw [ OptimizedSize ]; \ char *Name = Name ## _Raw; \ if( sizeof(Name ## _Raw) < Size ) \ - Name = new char[ Size ];\ -memset( Name, 0, Size ); +{ \ + Name = new char[ Size ];\ +} \ +memset( Name, 0, Size ) #define BUFFER_RESET( Name ) \ do { \ - if( Name != Name ## _Raw ) \ - { \ - delete[] Name; \ - Name = Name ## _Raw; \ - } \ -} while(0); + if( Name != Name ## _Raw ) \ + { \ + delete[] Name; \ + Name = Name ## _Raw; \ + } \ +} while(0) #endif diff --git a/encfs/NullNameIO.cpp b/fs/NullNameIO.cpp similarity index 73% rename from encfs/NullNameIO.cpp rename to fs/NullNameIO.cpp index 11200dd..d3295c1 100644 --- a/encfs/NullNameIO.cpp +++ b/fs/NullNameIO.cpp @@ -18,26 +18,24 @@ * along with this program. If not, see . */ -#include "NullNameIO.h" - -#include "Cipher.h" -#include "base64.h" +#include "base/base64.h" +#include "cipher/Cipher.h" +#include "fs/NullNameIO.h" #include static shared_ptr NewNNIO( const Interface &, - const shared_ptr &, const CipherKey & ) + const shared_ptr &, const CipherKey & ) { - return shared_ptr( new NullNameIO() ); + return shared_ptr( new NullNameIO() ); } static Interface NNIOIface = makeInterface("nameio/null", 1, 0, 0); static bool NullNameIO_registered = NameIO::Register("Null", - "No encryption of filenames", NNIOIface, NewNNIO); + "No encryption of filenames", NNIOIface, NewNNIO, false); NullNameIO::NullNameIO( ) { - } NullNameIO::~NullNameIO() @@ -46,43 +44,41 @@ NullNameIO::~NullNameIO() Interface NullNameIO::interface() const { - return NNIOIface; + return NNIOIface; } Interface NullNameIO::CurrentInterface() { - return NNIOIface; + return NNIOIface; } int NullNameIO::maxEncodedNameLen( int plaintextNameLen ) const { - return plaintextNameLen; + return plaintextNameLen; } int NullNameIO::maxDecodedNameLen( int encodedNameLen ) const { - return encodedNameLen; + return encodedNameLen; } int NullNameIO::encodeName( const char *plaintextName, int length, - uint64_t *iv, char *encodedName ) const + uint64_t *iv, char *encodedName ) const { - memcpy( encodedName, plaintextName, length ); - - return length; + memcpy( encodedName, plaintextName, length ); + return length; } int NullNameIO::decodeName( const char *encodedName, int length, - uint64_t *iv, char *plaintextName ) const + uint64_t *iv, char *plaintextName ) const { - memcpy( plaintextName, encodedName, length ); - - return length; + memcpy( plaintextName, encodedName, length ); + return length; } bool NullNameIO::Enabled() { - return true; + return true; } diff --git a/encfs/NullNameIO.h b/fs/NullNameIO.h similarity index 100% rename from encfs/NullNameIO.h rename to fs/NullNameIO.h diff --git a/fs/RawFileIO.cpp b/fs/RawFileIO.cpp new file mode 100644 index 0000000..a46e916 --- /dev/null +++ b/fs/RawFileIO.cpp @@ -0,0 +1,330 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifdef linux +#define _XOPEN_SOURCE 500 // pick up pread , pwrite +#endif +#include + +#include "base/Error.h" +#include "fs/RawFileIO.h" + +#include + +#include +#include +#include +#include + +#include + +using namespace std; + +static Interface RawFileIO_iface = makeInterface("FileIO/Raw", 1, 0, 0); + +FileIO *NewRawFileIO( const Interface &iface ) +{ + (void)iface; + return new RawFileIO(); +} + +inline void swap( int &x, int &y ) +{ + int tmp = x; + x = y; + y = tmp; +} + +RawFileIO::RawFileIO( ) + : knownSize( false ) + , fileSize(0) + , fd( -1 ) + , oldfd( -1 ) + , canWrite( false ) +{ +} + +RawFileIO::RawFileIO( const std::string &fileName ) + : name( fileName ) + , knownSize( false ) + , fileSize( 0 ) + , fd( -1 ) + , oldfd( -1 ) + , canWrite( false ) +{ +} + +RawFileIO::~RawFileIO() +{ + int _fd = -1; + int _oldfd = -1; + + swap( _fd, fd ); + swap( _oldfd, oldfd ); + + if( _oldfd != -1 ) + close( _oldfd ); + + if( _fd != -1 ) + close( _fd ); +} + +Interface RawFileIO::interface() const +{ + return RawFileIO_iface; +} + +/* + Workaround for opening a file for write when permissions don't allow. + Since the kernel has already checked permissions, we can assume it is ok to + provide access. So force it by changing permissions temporarily. Should + be called with a lock around it so that there won't be a race condition + with calls to lstat picking up the wrong permissions. +*/ +static int open_readonly_workaround(const char *path, int flags) +{ + int fd = -1; + struct stat stbuf; + memset(&stbuf, 0, sizeof(struct stat)); + if(lstat( path, &stbuf ) != -1) + { + // make sure user has read/write permission.. + chmod( path , stbuf.st_mode | 0600 ); + fd = ::open( path , flags ); + chmod( path , stbuf.st_mode ); + } else + { + LOG(INFO) << "can't stat file " << path; + } + + return fd; +} + +/* + We shouldn't have to support all possible open flags, so untaint the flags + argument by only taking ones we understand and accept. + - Since the kernel has already done permission tests before calling us, we + shouldn't have to worry about access control. + - Basically we just need to distinguish between read and write flags + - Also keep the O_LARGEFILE flag, in case the underlying filesystem needs + it.. +*/ +int RawFileIO::open(int flags) +{ + bool requestWrite = ((flags & O_RDWR) || (flags & O_WRONLY)); + + VLOG(1) << "open call for " + << (requestWrite ? "writable" : "read only") + << " file"; + + int result = 0; + + // if we have a descriptor and it is writable, or we don't need writable.. + if((fd >= 0) && (canWrite || !requestWrite)) + { + VLOG(1) << "using existing file descriptor"; + result = fd; // success + } else + { + int finalFlags = requestWrite ? O_RDWR : O_RDONLY; + +#if defined(O_LARGEFILE) + if( flags & O_LARGEFILE ) + finalFlags |= O_LARGEFILE; +#else +#warning O_LARGEFILE not supported +#endif + + int newFd = ::open( name.c_str(), finalFlags ); + + VLOG(1) << "open file with flags " << finalFlags << ", result = " << newFd; + + if((newFd == -1) && (errno == EACCES)) + { + VLOG(1) << "using readonly workaround for open"; + newFd = open_readonly_workaround( name.c_str(), finalFlags ); + } + + if(newFd >= 0) + { + if(oldfd >= 0) + { + LOG(ERROR) << "leaking FD?: oldfd = " + << oldfd << ", fd = " << fd << ", newfd = " << newFd; + } + + // the old fd might still be in use, so just keep it around for + // now. + canWrite = requestWrite; + oldfd = fd; + result = fd = newFd; + } else + { + result = -errno; + LOG(INFO) << "::open error: " << strerror(errno); + } + } + + LOG_IF(INFO, result < 0) << "file " << name << " open failure: " << -result; + + return result; +} + +int RawFileIO::getAttr( struct stat *stbuf ) const +{ + int res = lstat( name.c_str(), stbuf ); + int eno = errno; + + LOG_IF(INFO, res < 0) << "getAttr error on " << name + << ": " << strerror( eno ); + + return ( res < 0 ) ? -eno : 0; +} + +void RawFileIO::setFileName( const char *fileName ) +{ + name = fileName; +} + +const char *RawFileIO::getFileName() const +{ + return name.c_str(); +} + +off_t RawFileIO::getSize() const +{ + if(!knownSize) + { + struct stat stbuf; + memset( &stbuf, 0, sizeof( struct stat )); + int res = lstat( name.c_str(), &stbuf ); + + if(res == 0) + { + fileSize = stbuf.st_size; + knownSize = true; + return fileSize; + } else + return -1; + } else + { + return fileSize; + } +} + +ssize_t RawFileIO::read( const IORequest &req ) const +{ + rAssert( fd >= 0 ); + + VLOG(2) << "Read " << req.dataLen << " bytes from offset " << req.offset; + ssize_t readSize = pread( fd, req.data, req.dataLen, req.offset ); + + if(readSize < 0) + { + LOG(INFO) << "read failed at offset " << req.offset + << " for " << req.dataLen << " bytes: " << strerror(errno); + } + + return readSize; +} + +bool RawFileIO::write( const IORequest &req ) +{ + rAssert( fd >= 0 ); + rAssert( true == canWrite ); + + VLOG(2) << "Write " << req.dataLen << " bytes to offset " << req.offset; + + int retrys = 10; + void *buf = req.data; + ssize_t bytes = req.dataLen; + off_t offset = req.offset; + + while( bytes && retrys > 0 ) + { + ssize_t writeSize = ::pwrite( fd, buf, bytes, offset ); + + if( writeSize < 0 ) + { + knownSize = false; + LOG(INFO) << "write failed at offset " << offset << " for " + << bytes << " bytes: " << strerror(errno); + return false; + } + + bytes -= writeSize; + offset += writeSize; + buf = (void*)((char*)buf + writeSize); + --retrys; + } + + if(bytes != 0) + { + LOG(ERROR) << "Write error: wrote " << (req.dataLen-bytes) + << " bytes of " << req.dataLen << ", max retries reached"; + knownSize = false; + return false; + } else + { + if(knownSize) + { + off_t last = req.offset + req.dataLen; + if(last > fileSize) + fileSize = last; + } + + return true; + } +} + +int RawFileIO::truncate( off_t size ) +{ + int res; + + if(fd >= 0 && canWrite) + { + res = ::ftruncate( fd, size ); +#ifndef __FreeBSD__ + ::fdatasync( fd ); +#endif + } else + res = ::truncate( name.c_str(), size ); + + if(res < 0) + { + int eno = errno; + LOG(INFO) << "truncate failed for " << name + << " (" << fd << ") size " << size << ", error " << strerror(eno); + res = -eno; + knownSize = false; + } else + { + res = 0; + fileSize = size; + knownSize = true; + } + + return res; +} + +bool RawFileIO::isWritable() const +{ + return canWrite; +} diff --git a/encfs/RawFileIO.h b/fs/RawFileIO.h similarity index 96% rename from encfs/RawFileIO.h rename to fs/RawFileIO.h index d172b09..b2c58a9 100644 --- a/encfs/RawFileIO.h +++ b/fs/RawFileIO.h @@ -52,8 +52,8 @@ protected: std::string name; - bool knownSize; - off_t fileSize; + mutable bool knownSize; + mutable off_t fileSize; int fd; int oldfd; diff --git a/fs/StreamNameIO.cpp b/fs/StreamNameIO.cpp new file mode 100644 index 0000000..d03df64 --- /dev/null +++ b/fs/StreamNameIO.cpp @@ -0,0 +1,206 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "base/base64.h" +#include "base/Error.h" +#include "base/i18n.h" +#include "cipher/Cipher.h" +#include "fs/StreamNameIO.h" + +#include + +#include + +using namespace std; + +static shared_ptr NewStreamNameIO( const Interface &iface, + const shared_ptr &cipher, const CipherKey &key) +{ + return shared_ptr( new StreamNameIO( iface, cipher, key ) ); +} + +static bool StreamIO_registered = NameIO::Register("Stream", + gettext_noop("Stream encoding, keeps filenames as short as possible"), + StreamNameIO::CurrentInterface(), + NewStreamNameIO, true); + + +/* + - Version 0.1 is for EncFS 0.x support. The difference to 1.0 is that 0.x + stores the file checksums at the end of the encoded name, where 1.0 + stores them at the beginning. + + - Version 1.0 is the basic stream encoding mode used since the beginning of + EncFS. There is a slight difference in filename encodings from EncFS 0.x + to 1.0.x. This implements just the 1.0.x method. + + - Version 1.1 adds support for IV chaining. This is transparently + backward compatible, since older filesystems do not use IV chaining. + + - Version 2.0 uses full 64 bit IV during IV chaining mode. Prior versions + used only the 16 bit output from MAC_16. This reduces the theoretical + possibility (unlikely to make any difference in practice) of two files + with the same name in different directories ending up with the same + encrypted name. Added because there is no good reason to chop to 16 + bits. + + - Version 2.1 adds support for version 0 for EncFS 0.x compatibility. +*/ +Interface StreamNameIO::CurrentInterface() +{ + // implement major version 2, 1, and 0 + return makeInterface("nameio/stream", 2, 1, 2); +} + +StreamNameIO::StreamNameIO( const Interface &iface, + const shared_ptr &cipher, + const CipherKey &key ) + : _interface( iface.major() ) + , _cipher( cipher ) + , _key( key ) +{ + +} + +StreamNameIO::~StreamNameIO() +{ +} + +Interface StreamNameIO::interface() const +{ + return CurrentInterface(); +} + +int StreamNameIO::maxEncodedNameLen( int plaintextStreamLen ) const +{ + int encodedStreamLen = 2 + plaintextStreamLen; + return B256ToB64Bytes( encodedStreamLen ); +} + +int StreamNameIO::maxDecodedNameLen( int encodedStreamLen ) const +{ + int decLen256 = B64ToB256Bytes( encodedStreamLen ); + return decLen256 - 2; +} + +int StreamNameIO::encodeName( const char *plaintextName, int length, + uint64_t *iv, char *encodedName ) const +{ + uint64_t tmpIV = 0; + if( iv && _interface >= 2 ) + tmpIV = *iv; + + unsigned int mac = _cipher->MAC_16( (const unsigned char *)plaintextName, + length, _key, iv ); + + // add on checksum bytes + unsigned char *encodeBegin; + if(_interface >= 1) + { + // current versions store the checksum at the beginning + encodedName[0] = (mac >> 8) & 0xff; + encodedName[1] = (mac ) & 0xff; + encodeBegin = (unsigned char *)encodedName+2; + } else + { + // encfs 0.x stored checksums at the end. + encodedName[length] = (mac >> 8) & 0xff; + encodedName[length+1] = (mac ) & 0xff; + encodeBegin = (unsigned char *)encodedName; + } + + // stream encode the plaintext bytes + memcpy( encodeBegin, plaintextName, length ); + _cipher->streamEncode( encodeBegin, length, (uint64_t)mac ^ tmpIV, _key); + + // convert the entire thing to base 64 ascii.. + int encodedStreamLen = length + 2; + int encLen64 = B256ToB64Bytes( encodedStreamLen ); + + changeBase2Inline( (unsigned char *)encodedName, encodedStreamLen, + 8, 6, true ); + B64ToAscii( (unsigned char *)encodedName, encLen64 ); + + return encLen64; +} + +int StreamNameIO::decodeName( const char *encodedName, int length, + uint64_t *iv, char *plaintextName ) const +{ + rAssert(length > 2); + int decLen256 = B64ToB256Bytes( length ); + int decodedStreamLen = decLen256 - 2; + + if(decodedStreamLen <= 0) + throw Error("Filename too small to decode"); + + BUFFER_INIT( tmpBuf, 32, (unsigned int)length ); + + // decode into tmpBuf, because this step produces more data then we can fit + // into the result buffer.. + AsciiToB64( (unsigned char *)tmpBuf, (unsigned char *)encodedName, length ); + changeBase2Inline((unsigned char *)tmpBuf, length, 6, 8, false); + + // pull out the checksum value which is used as an initialization vector + uint64_t tmpIV = 0; + unsigned int mac; + if(_interface >= 1) + { + // current versions store the checksum at the beginning + mac = ((unsigned int)((unsigned char)tmpBuf[0])) << 8 + | ((unsigned int)((unsigned char)tmpBuf[1])); + + // version 2 adds support for IV chaining.. + if( iv && _interface >= 2 ) + tmpIV = *iv; + + memcpy( plaintextName, tmpBuf+2, decodedStreamLen ); + } else + { + // encfs 0.x stored checksums at the end. + mac = ((unsigned int)((unsigned char)tmpBuf[decodedStreamLen])) << 8 + | ((unsigned int)((unsigned char)tmpBuf[decodedStreamLen+1])); + + memcpy( plaintextName, tmpBuf, decodedStreamLen ); + } + + _cipher->streamDecode( (unsigned char *)plaintextName, decodedStreamLen, + (uint64_t)mac ^ tmpIV, _key); + + // compute MAC to check with stored value + unsigned int mac2 = _cipher->MAC_16((const unsigned char *)plaintextName, + decodedStreamLen, _key, iv); + + BUFFER_RESET( tmpBuf ); + if(mac2 != mac) + { + VLOG(1) << "checksum mismatch: expected " << mac << ", got " << mac2 + << "on decode of " << decodedStreamLen << " bytes"; + throw Error( "checksum mismatch in filename decode" ); + } + + return decodedStreamLen; +} + +bool StreamNameIO::Enabled() +{ + return true; +} + diff --git a/encfs/StreamNameIO.h b/fs/StreamNameIO.h similarity index 97% rename from encfs/StreamNameIO.h rename to fs/StreamNameIO.h index a18e23c..9ff40a3 100644 --- a/encfs/StreamNameIO.h +++ b/fs/StreamNameIO.h @@ -21,8 +21,8 @@ #ifndef _StreamNameIO_incl_ #define _StreamNameIO_incl_ -#include "NameIO.h" -#include "CipherKey.h" +#include "cipher/CipherKey.h" +#include "fs/NameIO.h" class Cipher; diff --git a/fs/encfs.cpp b/fs/encfs.cpp new file mode 100644 index 0000000..e41620d --- /dev/null +++ b/fs/encfs.cpp @@ -0,0 +1,793 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2003-2007, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include "fs/encfs.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef linux +#include +#endif + +#ifdef HAVE_ATTR_XATTR_H +#include +#elif defined(HAVE_SYS_XATTR_H) +#include +#endif + + +#include +#include + +#ifdef HAVE_TR1_TUPLE +#include +using namespace std; +using namespace std::tr1; +#else +#include +using namespace std; +#endif + +#include "base/shared_ptr.h" +#include "base/Mutex.h" +#include "base/Error.h" +#include "cipher/MemoryPool.h" +#include "fs/DirNode.h" +#include "fs/FileUtils.h" +#include "fs/Context.h" + +#include + +#ifndef MIN +#define MIN(a,b) (((a)<(b)) ? (a): (b)) +#endif + +#define ESUCCESS 0 + +using rel::Lock; + +#define GET_FN(ctx, finfo) ctx->getNode((void*)(uintptr_t)finfo->fh) + +static EncFS_Context * context() +{ + return (EncFS_Context*)fuse_get_context()->private_data; +} + +/* + The log messages below always print out encrypted filenames, not + plaintext. The reason is so that it isn't possible to leak information + about the encrypted data through logging interfaces. + + + The purpose of this layer of code is to take the FUSE request and dispatch + to the internal interfaces. Any marshaling of arguments and return types + can be done here. +*/ + +// helper function -- apply a functor to a cipher path, given the plain path +template +static int withCipherPath( const char *opName, const char *path, + int (*op)(EncFS_Context *, const string &name, T data ), T data, + bool passReturnCode = false ) +{ + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + string cyName = FSRoot->cipherPath( path ); + VLOG(1) << opName << " " << cyName.c_str(); + + res = op( ctx, cyName, data ); + + if(res == -1) + { + res = -errno; + LOG(INFO) << opName << " error: " << strerror(-res); + } else if(!passReturnCode) + res = ESUCCESS; + } catch( Error &err ) + { + LOG(ERROR) << "error caught in " << opName << + ":" << err.what(); + } + return res; +} + +// helper function -- apply a functor to a node +template +static int withFileNode( const char *opName, + const char *path, struct fuse_file_info *fi, + int (*op)(FileNode *, T data ), T data ) +{ + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + shared_ptr fnode; + + if(fi != NULL) + fnode = GET_FN(ctx, fi); + else + fnode = FSRoot->lookupNode( path, opName ); + + rAssert(fnode != NULL); + VLOG(1) << opName << " " << fnode->cipherName(); + res = op( fnode.get(), data ); + + LOG_IF(INFO, res < 0) << opName << " error: " << strerror(-res); + } catch( Error &err ) + { + LOG(ERROR) << "error caught in " << opName << + ":" << err.what(); + } + return res; +} + +int _do_getattr(FileNode *fnode, struct stat *stbuf) +{ + int res = fnode->getAttr(stbuf); + if(res == ESUCCESS && S_ISLNK(stbuf->st_mode)) + { + EncFS_Context *ctx = context(); + shared_ptr FSRoot = ctx->getRoot(&res); + if(FSRoot) + { + // determine plaintext link size.. Easiest to read and decrypt.. + vector buf(stbuf->st_size+1, 0); + + res = ::readlink( fnode->cipherName(), &buf[0], stbuf->st_size ); + if(res >= 0) + { + // other functions expect c-strings to be null-terminated, which + // readlink doesn't provide + buf[res] = '\0'; + + stbuf->st_size = FSRoot->plainPath( &buf[0] ).length(); + + res = ESUCCESS; + } else + res = -errno; + } + } + + return res; +} + +int encfs_getattr(const char *path, struct stat *stbuf) +{ + return withFileNode( "getattr", path, NULL, _do_getattr, stbuf ); +} + +int encfs_fgetattr(const char *path, struct stat *stbuf, + struct fuse_file_info *fi) +{ + return withFileNode( "fgetattr", path, fi, _do_getattr, stbuf ); +} + +int encfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler) +{ + EncFS_Context *ctx = context(); + + int res = ESUCCESS; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + + DirTraverse dt = FSRoot->openDir( path ); + + VLOG(1) << "getdir on " << FSRoot->cipherPath(path); + + if(dt.valid()) + { + int fileType = 0; + ino_t inode = 0; + + std::string name = dt.nextPlaintextName( &fileType, &inode ); + while( !name.empty() ) + { + res = filler( h, name.c_str(), fileType, inode ); + + if(res != ESUCCESS) + break; + + name = dt.nextPlaintextName( &fileType, &inode ); + } + } else + { + LOG(INFO) << "getdir request invalid, path: '" << path << "'"; + } + + return res; + } catch( Error &err ) + { + LOG(ERROR) << "error caught in getdir: " << err.what(); + return -EIO; + } +} + +int encfs_mknod(const char *path, mode_t mode, dev_t rdev) +{ + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + shared_ptr fnode = FSRoot->lookupNode( path, "mknod" ); + + VLOG(1) << "mknod on " << fnode->cipherName() + << ", mode " << mode << ", dev " << rdev; + + uid_t uid = 0; + gid_t gid = 0; + if(ctx->publicFilesystem) + { + fuse_context *context = fuse_get_context(); + uid = context->uid; + gid = context->gid; + } + res = fnode->mknod( mode, rdev, uid, gid ); + // Is this error due to access problems? + if(ctx->publicFilesystem && -res == EACCES) + { + // try again using the parent dir's group + string parent = fnode->plaintextParent(); + LOG(INFO) << "attempting public filesystem workaround for " + << parent.c_str(); + shared_ptr dnode = + FSRoot->lookupNode( parent.c_str(), "mknod" ); + + struct stat st; + if(dnode->getAttr( &st ) == 0) + res = fnode->mknod( mode, rdev, uid, st.st_gid ); + } + } catch( Error &err ) + { + LOG(ERROR) << "error caught in mknod: " << err.what(); + } + return res; +} + +int encfs_mkdir(const char *path, mode_t mode) +{ + fuse_context *fctx = fuse_get_context(); + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + uid_t uid = 0; + gid_t gid = 0; + if(ctx->publicFilesystem) + { + uid = fctx->uid; + gid = fctx->gid; + } + res = FSRoot->mkdir( path, mode, uid, gid ); + // Is this error due to access problems? + if(ctx->publicFilesystem && -res == EACCES) + { + // try again using the parent dir's group + string parent = parentDirectory( path ); + shared_ptr dnode = + FSRoot->lookupNode( parent.c_str(), "mkdir" ); + + struct stat st; + if(dnode->getAttr( &st ) == 0) + res = FSRoot->mkdir( path, mode, uid, st.st_gid ); + } + } catch( Error &err ) + { + LOG(ERROR) << "error caught in mkdir: " << err.what(); + } + return res; +} + +int encfs_unlink(const char *path) +{ + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + // let DirNode handle it atomically so that it can handle race + // conditions + res = FSRoot->unlink( path ); + } catch( Error &err ) + { + LOG(ERROR) << "error caught in unlink: " << err.what(); + } + return res; +} + + +int _do_rmdir(EncFS_Context *, const string &cipherPath, int ) +{ + return rmdir( cipherPath.c_str() ); +} + +int encfs_rmdir(const char *path) +{ + return withCipherPath( "rmdir", path, _do_rmdir, 0 ); +} + +int _do_readlink(EncFS_Context *ctx, const string &cyName, + tuple data ) +{ + char *buf = get<0>(data); + size_t size = get<1>(data); + + int res = ESUCCESS; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + res = ::readlink( cyName.c_str(), buf, size-1 ); + + if(res == -1) + return -errno; + + buf[res] = '\0'; // ensure null termination + string decodedName; + try + { + decodedName = FSRoot->plainPath( buf ); + } catch(...) { } + + if(!decodedName.empty()) + { + strncpy(buf, decodedName.c_str(), size-1); + buf[size-1] = '\0'; + + return ESUCCESS; + } else + { + LOG(WARNING) << "Error decoding link"; + return -1; + } +} + +int encfs_readlink(const char *path, char *buf, size_t size) +{ + return withCipherPath( "readlink", path, _do_readlink, + make_tuple(buf, size) ); +} + +int encfs_symlink(const char *from, const char *to) +{ + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + // allow fully qualified names in symbolic links. + string fromCName = FSRoot->relativeCipherPath( from ); + string toCName = FSRoot->cipherPath( to ); + + VLOG(1) << "symlink " << fromCName << " -> " << toCName; + + // use setfsuid / setfsgid so that the new link will be owned by the + // uid/gid provided by the fuse_context. + int olduid = -1; + int oldgid = -1; + if(ctx->publicFilesystem) + { + fuse_context *context = fuse_get_context(); + olduid = setfsuid( context->uid ); + oldgid = setfsgid( context->gid ); + } + res = ::symlink( fromCName.c_str(), toCName.c_str() ); + if(olduid >= 0) + setfsuid( olduid ); + if(oldgid >= 0) + setfsgid( oldgid ); + + if(res == -1) + res = -errno; + else + res = ESUCCESS; + } catch( Error &err ) + { + LOG(ERROR) << "error caught in symlink: " << err.what(); + } + return res; +} + +int encfs_link(const char *from, const char *to) +{ + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + res = FSRoot->link( from, to ); + } catch( Error &err ) + { + LOG(ERROR) << "error caught in link: " << err.what(); + } + return res; +} + +int encfs_rename(const char *from, const char *to) +{ + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + res = FSRoot->rename( from, to ); + } catch( Error &err ) + { + LOG(ERROR) << "error caught in rename: " << err.what(); + } + return res; +} + +int _do_chmod(EncFS_Context *, const string &cipherPath, mode_t mode) +{ + return chmod( cipherPath.c_str(), mode ); +} + +int encfs_chmod(const char *path, mode_t mode) +{ + return withCipherPath( "chmod", path, _do_chmod, mode ); +} + +int _do_chown(EncFS_Context *, const string &cyName, + tuple data) +{ + int res = lchown( cyName.c_str(), get<0>(data), get<1>(data) ); + return (res == -1) ? -errno : ESUCCESS; +} + +int encfs_chown(const char *path, uid_t uid, gid_t gid) +{ + return withCipherPath( "chown", path, _do_chown, make_tuple(uid, gid)); +} + +int _do_truncate( FileNode *fnode, off_t size ) +{ + return fnode->truncate( size ); +} + +int encfs_truncate(const char *path, off_t size) +{ + return withFileNode( "truncate", path, NULL, _do_truncate, size ); +} + +int encfs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi) +{ + return withFileNode( "ftruncate", path, fi, _do_truncate, size ); +} + +int _do_utime(EncFS_Context *, const string &cyName, struct utimbuf *buf) +{ + int res = utime( cyName.c_str(), buf); + return (res == -1) ? -errno : ESUCCESS; +} + +int encfs_utime(const char *path, struct utimbuf *buf) +{ + return withCipherPath( "utime", path, _do_utime, buf ); +} + +int _do_utimens(EncFS_Context *, const string &cyName, + const struct timespec ts[2]) +{ + struct timeval tv[2]; + tv[0].tv_sec = ts[0].tv_sec; + tv[0].tv_usec = ts[0].tv_nsec / 1000; + tv[1].tv_sec = ts[1].tv_sec; + tv[1].tv_usec = ts[1].tv_nsec / 1000; + + int res = lutimes( cyName.c_str(), tv); + return (res == -1) ? -errno : ESUCCESS; +} + +int encfs_utimens(const char *path, const struct timespec ts[2] ) +{ + return withCipherPath( "utimens", path, _do_utimens, ts ); +} + +int encfs_open(const char *path, struct fuse_file_info *file) +{ + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if(!FSRoot) + return res; + + try + { + shared_ptr fnode = + FSRoot->openNode( path, "open", file->flags, &res ); + + if(fnode) + { + VLOG(1) << "encfs_open for " << fnode->cipherName() + << ", flags " << file->flags; + + if( res >= 0 ) + { + file->fh = (uintptr_t)ctx->putNode(path, fnode); + res = ESUCCESS; + } + } + } catch( Error &err ) + { + LOG(ERROR) << "error caught in open: " << err.what(); + } + + return res; +} + +int _do_flush(FileNode *fnode, int ) +{ + /* Flush can be called multiple times for an open file, so it doesn't + close the file. However it is important to call close() for some + underlying filesystems (like NFS). + */ + int res = fnode->open( O_RDONLY ); + if(res >= 0) + { + int fh = res; + res = close(dup(fh)); + if(res == -1) + res = -errno; + } + + return res; +} + +int encfs_flush(const char *path, struct fuse_file_info *fi) +{ + return withFileNode( "flush", path, fi, _do_flush, 0 ); +} + +/* +Note: This is advisory -- it might benefit us to keep file nodes around for a +bit after they are released just in case they are reopened soon. But that +requires a cache layer. + */ +int encfs_release(const char *path, struct fuse_file_info *finfo) +{ + EncFS_Context *ctx = context(); + + try + { + ctx->eraseNode( path, (void*)(uintptr_t)finfo->fh ); + return ESUCCESS; + } catch( Error &err ) + { + LOG(ERROR) << "error caught in release: " << err.what(); + return -EIO; + } +} + +int _do_read(FileNode *fnode, tuple data) +{ + return fnode->read( get<2>(data), get<0>(data), get<1>(data)); +} + +int encfs_read(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *file) +{ + return withFileNode( "read", path, file, _do_read, + make_tuple((unsigned char *)buf, size, offset)); +} + +int _do_fsync(FileNode *fnode, int dataSync) +{ + return fnode->sync( dataSync != 0 ); +} + +int encfs_fsync(const char *path, int dataSync, + struct fuse_file_info *file) +{ + return withFileNode( "fsync", path, file, _do_fsync, dataSync ); +} + +int _do_write(FileNode *fnode, tuple data) +{ + size_t size = get<1>(data); + if(fnode->write( get<2>(data), (unsigned char *)get<0>(data), size )) + return size; + else + return -EIO; +} + +int encfs_write(const char *path, const char *buf, size_t size, + off_t offset, struct fuse_file_info *file) +{ + return withFileNode("write", path, file, _do_write, + make_tuple(buf, size, offset)); +} + +// statfs works even if encfs is detached.. +int encfs_statfs(const char *path, struct statvfs *st) +{ + EncFS_Context *ctx = context(); + + int res = -EIO; + try + { + (void)path; // path should always be '/' for now.. + rAssert( st != NULL ); + string cyName = ctx->rootCipherDir; + + VLOG(1) << "doing statfs of " << cyName; + res = statvfs( cyName.c_str(), st ); + if(!res) + { + // adjust maximum name length.. + st->f_namemax = 6 * (st->f_namemax - 2) / 8; // approx.. + } + if(res == -1) + res = -errno; + } catch( Error &err ) + { + LOG(ERROR) << "error caught in statfs: " << err.what(); + } + return res; +} + +#ifdef HAVE_XATTR + + +#ifdef XATTR_ADD_OPT +int _do_setxattr(EncFS_Context *, const string &cyName, + tuple data) +{ + int options = 0; + return ::setxattr( cyName.c_str(), get<0>(data), get<1>(data), + get<2>(data), get<3>(data), options ); +} +int encfs_setxattr( const char *path, const char *name, + const char *value, size_t size, int flags, uint32_t position ) +{ + (void)flags; + return withCipherPath( "setxattr", path, _do_setxattr, + make_tuple(name, value, size, position) ); +} +#else +int _do_setxattr(EncFS_Context *, const string &cyName, + tuple data) +{ + return ::setxattr( cyName.c_str(), get<0>(data), get<1>(data), + get<2>(data), get<3>(data) ); +} +int encfs_setxattr( const char *path, const char *name, + const char *value, size_t size, int flags ) +{ + return withCipherPath( "setxattr", path, _do_setxattr, + make_tuple(name, value, size, flags) ); +} +#endif + + +#ifdef XATTR_ADD_OPT +int _do_getxattr(EncFS_Context *, const string &cyName, + tuple data) +{ + int options = 0; + return ::getxattr( cyName.c_str(), get<0>(data), + get<1>(data), get<2>(data), get<3>(data), options ); +} +int encfs_getxattr( const char *path, const char *name, + char *value, size_t size, uint32_t position ) +{ + return withCipherPath( "getxattr", path, _do_getxattr, + make_tuple(name, (void *)value, size, position), true ); +} +#else +int _do_getxattr(EncFS_Context *, const string &cyName, + tuple data) +{ + return ::getxattr( cyName.c_str(), get<0>(data), + get<1>(data), get<2>(data)); +} +int encfs_getxattr( const char *path, const char *name, + char *value, size_t size ) +{ + return withCipherPath( "getxattr", path, _do_getxattr, + make_tuple(name, (void *)value, size), true ); +} +#endif + + +int _do_listxattr(EncFS_Context *, const string &cyName, + tuple data) +{ +#ifdef XATTR_ADD_OPT + int options = 0; + int res = ::listxattr( cyName.c_str(), get<0>(data), get<1>(data), + options ); +#else + int res = ::listxattr( cyName.c_str(), get<0>(data), get<1>(data) ); +#endif + return (res == -1) ? -errno : res; +} + +int encfs_listxattr( const char *path, char *list, size_t size ) +{ + return withCipherPath( "listxattr", path, _do_listxattr, + make_tuple(list, size), true ); +} + +int _do_removexattr(EncFS_Context *, const string &cyName, const char *name) +{ +#ifdef XATTR_ADD_OPT + int options = 0; + int res = ::removexattr( cyName.c_str(), name, options ); +#else + int res = ::removexattr( cyName.c_str(), name ); +#endif + return (res == -1) ? -errno : res; +} + +int encfs_removexattr( const char *path, const char *name ) +{ + return withCipherPath( "removexattr", path, _do_removexattr, name ); +} + +#endif // HAVE_XATTR + diff --git a/encfs/encfs.h b/fs/encfs.h similarity index 99% rename from encfs/encfs.h rename to fs/encfs.h index 3256300..912e9fd 100644 --- a/encfs/encfs.h +++ b/fs/encfs.h @@ -21,7 +21,7 @@ #ifndef _encfs_incl_ #define _encfs_incl_ -#include "config.h" +#include "base/config.h" #include #include diff --git a/fs/test.cpp b/fs/test.cpp new file mode 100644 index 0000000..f7c9af4 --- /dev/null +++ b/fs/test.cpp @@ -0,0 +1,556 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2003, Valient Gough + * + * This library is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more + * details. + * + */ + +#include "fs/encfs.h" + +#include +#include +#include +#include + +#include "base/config.h" +#include "base/Interface.h" +#include "base/Error.h" +#include "cipher/Cipher.h" +#include "cipher/MemoryPool.h" +#include "fs/DirNode.h" +#include "fs/FileUtils.h" +#include "fs/StreamNameIO.h" +#include "fs/BlockNameIO.h" +#include "fs/NullNameIO.h" + +#include + +#ifdef HAVE_SSL +#define NO_DES +#include +#ifndef OPENSSL_NO_ENGINE +#include +#endif +#endif + +#include + +#if HAVE_TR1_UNORDERED_SET +#include +using std::tr1::unordered_set; +#else +#include +using std::unordered_set; +#endif + +using namespace std; + +const int FSBlockSize = 256; + +static +int checkErrorPropogation( const shared_ptr &cipher, + int size, int byteToChange, const CipherKey &key ) +{ + MemBlock orig; + orig.allocate(size); + MemBlock data; + data.allocate(size); + + for(int i=0; istreamEncode( data.data, size, 0, key ); + else + cipher->blockEncode( data.data, size, 0, key ); + + // intoduce an error in the encoded data, so we can check error propogation + if(byteToChange >= 0 && byteToChange < size) + { + unsigned char previousValue = data.data[byteToChange]; + do + { + data.data[byteToChange] = rand(); + } while(data.data[byteToChange] == previousValue); + } + + if(size != FSBlockSize) + cipher->streamDecode( data.data, size, 0, key ); + else + cipher->blockDecode( data.data, size, 0, key ); + + int numByteErrors = 0; + for(int i=0; i \"" << encName.c_str() << "\""; + + // decrypt name + string decName = dirNode.plainPath( encName.c_str() ); + + if(decName == *orig) + { + if(verbose) + cerr << " OK\n"; + } else + { + if(verbose) + cerr << " FAILED (got " << decName << ")\n"; + return false; + } + + orig++; + } + + if (collisionTest) + { + if (verbose) + cerr << "Checking for name collections, this will take a while..\n"; + // check for collision rate + char buf[64]; + unordered_set encryptedNames; + for (long i=0; i < 10000000; i++) + { + snprintf(buf, sizeof(buf), "%li", i); + string encName = dirNode.relativeCipherPath( buf ); + // simulate a case-insisitive filesystem.. + std::transform(encName.begin(), encName.end(), encName.begin(), + ::toupper); + + if (encryptedNames.insert(encName).second == false) { + cerr << "collision detected after " << i << " iterations"; + break; + } + } + cerr << "NO collisions detected"; + } + + return true; +} + +bool runTests(const shared_ptr &cipher, bool verbose) +{ + // create a random key + if(verbose) + cerr << "Generating new key, output will be different on each run\n\n"; + CipherKey key = cipher->newRandomKey(); + + if(verbose) + cerr << "Testing key save / restore :"; + { + CipherKey encodingKey = cipher->newRandomKey(); + int encodedKeySize = cipher->encodedKeySize(); + unsigned char *keyBuf = new unsigned char [ encodedKeySize ]; + + cipher->writeKey( key, keyBuf, encodingKey ); + CipherKey key2 = cipher->readKey( keyBuf, encodingKey ); + if(!key2) + { + if(verbose) + cerr << " FAILED (decode error)\n"; + return false; + } + + if(cipher->compareKey( key, key2 )) + { + if(verbose) + cerr << " OK\n"; + } else + { + if(verbose) + cerr << " FAILED\n"; + return false; + } + } + + if(verbose) + cerr << "Testing Config interface load / store :"; + { + CipherKey encodingKey = cipher->newRandomKey(); + int encodedKeySize = cipher->encodedKeySize(); + unsigned char *keyBuf = new unsigned char [ encodedKeySize ]; + + cipher->writeKey( key, keyBuf, encodingKey ); + + // store in config struct.. + EncfsConfig cfg; + cfg.mutable_cipher()->MergeFrom(cipher->interface()); + EncryptedKey *encryptedKey = cfg.mutable_key(); + encryptedKey->set_size(8 * cipher->keySize()); + encryptedKey->set_ciphertext( keyBuf, encodedKeySize ); + cfg.set_block_size(FSBlockSize); + + // save config + string data; + google::protobuf::TextFormat::PrintToString(cfg, &data); + + // read back in and check everything.. + EncfsConfig cfg2; + google::protobuf::TextFormat::ParseFromString(data, &cfg2); + + // check.. + rAssert( implements(cfg.cipher(),cfg2.cipher()) ); + rAssert( cfg.key().size() == cfg2.key().size() ); + rAssert( cfg.block_size() == cfg2.block_size() ); + + // try decoding key.. + + CipherKey key2 = cipher->readKey( (unsigned char *)cfg2.key().ciphertext().data(), encodingKey ); + if(!key2) + { + if(verbose) + cerr << " FAILED (decode error)\n"; + return false; + } + + if(cipher->compareKey( key, key2 )) + { + if(verbose) + cerr << " OK\n"; + } else + { + if(verbose) + cerr << " FAILED\n"; + return false; + } + } + + FSConfigPtr fsCfg = FSConfigPtr(new FSConfig); + fsCfg->cipher = cipher; + fsCfg->key = key; + fsCfg->config.reset(new EncfsConfig); + fsCfg->config->set_block_size(FSBlockSize); + fsCfg->opts.reset(new EncFS_Opts); + + if(verbose) + cerr << "Testing name encode/decode (stream coding w/ IV chaining)\n"; + if (cipher->hasStreamMode()) + { + fsCfg->opts->idleTracking = false; + fsCfg->config->set_unique_iv(false); + + fsCfg->nameCoding.reset( new StreamNameIO( + StreamNameIO::CurrentInterface(), cipher, key ) ); + fsCfg->nameCoding->setChainedNameIV( true ); + + DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); + + if(!testNameCoding( dirNode, verbose )) + return false; + } + + if(verbose) + cerr << "Testing name encode/decode (block coding w/ IV chaining)\n"; + { + fsCfg->opts->idleTracking = false; + fsCfg->config->set_unique_iv(false); + fsCfg->nameCoding.reset( new BlockNameIO( + BlockNameIO::CurrentInterface(), cipher, key ) ); + fsCfg->nameCoding->setChainedNameIV( true ); + + DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); + + if(!testNameCoding( dirNode, verbose )) + return false; + } + + if(verbose) + cerr << "Testing name encode/decode (block coding w/ IV chaining, base32)\n"; + { + fsCfg->opts->idleTracking = false; + fsCfg->config->set_unique_iv(false); + fsCfg->nameCoding.reset( new BlockNameIO( + BlockNameIO::CurrentInterface(), cipher, key, true ) ); + fsCfg->nameCoding->setChainedNameIV( true ); + + DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); + + if(!testNameCoding( dirNode, verbose )) + return false; + } + + if(!verbose) + { + if (cipher->hasStreamMode()) + { + // test stream mode, this time without IV chaining + fsCfg->nameCoding = + shared_ptr( new StreamNameIO( + StreamNameIO::CurrentInterface(), cipher, key ) ); + fsCfg->nameCoding->setChainedNameIV( false ); + + DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); + + if(!testNameCoding( dirNode, verbose )) + return false; + } + + { + // test block mode, this time without IV chaining + fsCfg->nameCoding = shared_ptr( new BlockNameIO( + BlockNameIO::CurrentInterface(), cipher, key ) ); + fsCfg->nameCoding->setChainedNameIV( false ); + + DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); + + if(!testNameCoding( dirNode, verbose )) + return false; + } + } + + if(verbose) + cerr << "Testing block encode/decode on full block - "; + { + int numErrors = checkErrorPropogation( cipher, + FSBlockSize, -1, key ); + if(numErrors) + { + if(verbose) + cerr << " FAILED!\n"; + return false; + } else + { + if(verbose) + cerr << " OK\n"; + } + } + if(verbose) + cerr << "Testing block encode/decode on partial block - "; + if (cipher->hasStreamMode()) + { + int numErrors = checkErrorPropogation( cipher, + FSBlockSize-1, -1, key ); + if(numErrors) + { + if(verbose) + cerr << " FAILED!\n"; + return false; + } else + { + if(verbose) + cerr << " OK\n"; + } + } + + if(verbose) + cerr << "Checking error propogation in partial block:\n"; + if (cipher->hasStreamMode()) + { + int minChanges = FSBlockSize-1; + int maxChanges = 0; + int minAt = 0; + int maxAt = 0; + for(int i=0; i maxChanges) + { + maxChanges = numErrors; + maxAt = i; + } + } + + if(verbose) + { + cerr << "modification of 1 byte affected between " << minChanges + << " and " << maxChanges << " decoded bytes\n"; + cerr << "minimum change at byte " << minAt + << " and maximum at byte " << maxAt << "\n"; + } + } + if(verbose) + cerr << "Checking error propogation on full block:\n"; + { + int minChanges = FSBlockSize; + int maxChanges = 0; + int minAt = 0; + int maxAt = 0; + for(int i=0; i maxChanges) + { + maxChanges = numErrors; + maxAt = i; + } + } + + if(verbose) + { + cerr << "modification of 1 byte affected between " << minChanges + << " and " << maxChanges << " decoded bytes\n"; + cerr << "minimum change at byte " << minAt + << " and maximum at byte " << maxAt << "\n"; + } + } + + return true; +} + + +int main(int argc, char *argv[]) +{ + FLAGS_logtostderr = 1; + FLAGS_minloglevel = 1; + + google::InitGoogleLogging(argv[0]); + google::InstallFailureSignalHandler(); + +#ifdef HAVE_SSL + SSL_load_error_strings(); + SSL_library_init(); + +#ifndef OPENSSL_NO_ENGINE + ENGINE_load_builtin_engines(); + ENGINE_register_all_ciphers(); + ENGINE_register_all_digests(); + ENGINE_register_all_RAND(); +#endif +#endif + + srand( time(0) ); + + // get a list of the available algorithms + std::list algorithms = + Cipher::GetAlgorithmList(); + std::list::const_iterator it; + cerr << "Supported Crypto interfaces:\n"; + for(it = algorithms.begin(); it != algorithms.end(); ++it) + { + cerr << it->name + << " ( " << it->iface.name() << " " + << it->iface.major() << ":" + << it->iface.minor() << ":" + << it->iface.age() << " ) : " << it->description << "\n"; + cerr << " - key length " << it->keyLength.min() << " to " + << it->keyLength.max() << " , block size " << it->blockSize.min() + << " to " << it->blockSize.max() << "\n"; + } + cerr << "\n"; + + cerr << "Testing interfaces\n"; + for(it = algorithms.begin(); it != algorithms.end(); ++it) + { + int blockSize = it->blockSize.closest( 256 ); + for(int keySize = it->keyLength.min(); keySize <= it->keyLength.max(); + keySize += it->keyLength.inc()) + { + cerr << it->name << ", key length " << keySize + << ", block size " << blockSize << ": "; + + shared_ptr cipher = Cipher::New( it->name, keySize ); + if(!cipher) + { + cerr << "FAILED TO CREATE\n"; + } else + { + try + { + if(runTests( cipher, false )) + cerr << "OK\n"; + else + cerr << "FAILED\n"; + } catch( Error &er ) + { + cerr << "Error: " << er.what() << "\n"; + } + } + } + } + + // run one test with verbose output too.. + shared_ptr cipher = Cipher::New("AES", 192); + if(!cipher) + { + cerr << "\nNo AES cipher found, skipping verbose test.\n"; + } else + { + cerr << "\nVerbose output for " << cipher->interface().name() + << " test, key length " << cipher->keySize()*8 << ", block size " + << FSBlockSize << ":\n"; + + runTests( cipher, true ); + } + + MemoryPool::destroyAll(); + + return 0; +} + + + diff --git a/fs/test_BlockIO.cpp b/fs/test_BlockIO.cpp new file mode 100644 index 0000000..c81462e --- /dev/null +++ b/fs/test_BlockIO.cpp @@ -0,0 +1,67 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2012 Valient Gough + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include + +#include + +#include "cipher/MemoryPool.h" + +#include "fs/testing.h" +#include "fs/FileUtils.h" +#include "fs/FSConfig.h" +#include "fs/MemFileIO.h" +#include "fs/MemBlockFileIO.h" + +namespace { + +TEST(BlockFileIOTest, BasicIO) { + // Base for comparison. + MemFileIO base(1024); + ASSERT_EQ(1024, base.getSize()); + + FSConfigPtr cfg = makeConfig( Cipher::New("Null"), 512); + MemBlockFileIO block(512, cfg); + block.truncate(1024); + ASSERT_EQ(1024, block.getSize()); + + MemBlock mb; + mb.allocate(256); + + IORequest req; + req.offset = 0; + req.data = mb.data; + req.dataLen = 256; + + for (int i = 0; i < 4; i++) { + req.offset = i * 256; + memset(req.data, 0, req.dataLen); + ASSERT_TRUE(base.write(req)); + + req.offset = i * 256; + memset(req.data, 0, req.dataLen); + ASSERT_TRUE(block.write(req)); + } + + ASSERT_NO_FATAL_FAILURE(compare(&base, &block, 0, 1024)); +} + +} // namespace + diff --git a/fs/test_IO.cpp b/fs/test_IO.cpp new file mode 100644 index 0000000..a89317e --- /dev/null +++ b/fs/test_IO.cpp @@ -0,0 +1,95 @@ + +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2012 Valient Gough + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include + +#include +#include "fs/testing.h" + +#include "cipher/Cipher.h" +#include "cipher/MemoryPool.h" + +#include "fs/CipherFileIO.h" +#include "fs/FileUtils.h" +#include "fs/FSConfig.h" +#include "fs/MACFileIO.h" +#include "fs/MemFileIO.h" + +namespace { + +TEST(MemIOTest, BasicIO) { + MemFileIO io(1024); + ASSERT_EQ(1024, io.getSize()); + + MemBlock mb; + mb.allocate(256); + + IORequest req; + req.offset = 0; + req.data = mb.data; + req.dataLen = 256; + + for (int i = 0; i < 4; i++) { + req.offset = i * 256; + memset(req.data, 0, req.dataLen); + ASSERT_TRUE(io.write(req)); + } + + for (int i = 0; i < 4; i++) { + req.offset = i * 256; + ASSERT_EQ(req.dataLen, io.read(req)); + } +} + +void testMacIO(FSConfigPtr& cfg) { + shared_ptr base(new MemFileIO(0)); + shared_ptr test(new MACFileIO(base, cfg)); + + shared_ptr dup(new MemFileIO(0)); + comparisonTest(cfg, test.get(), dup.get()); +} + +TEST(IOTest, NullMacIO) { + runWithCipher("Null", 512, testMacIO); +} + +TEST(IOTest, MacIO) { + runWithAllCiphers(testMacIO); +} + +void testCipherIO(FSConfigPtr& cfg) { + shared_ptr base(new MemFileIO(0)); + shared_ptr test(new CipherFileIO(base, cfg)); + + shared_ptr dup(new MemFileIO(0)); + comparisonTest(cfg, test.get(), dup.get()); +} + +TEST(IOTest, NullCipherFileIO) { + runWithCipher("Null", 512, testCipherIO); +} + +TEST(IOTest, CipherFileIO) { + runWithAllCiphers(testCipherIO); +} + +} // namespace + diff --git a/encfs/testextpass b/fs/testextpass similarity index 100% rename from encfs/testextpass rename to fs/testextpass diff --git a/fs/testing.cpp b/fs/testing.cpp new file mode 100644 index 0000000..819f7d5 --- /dev/null +++ b/fs/testing.cpp @@ -0,0 +1,171 @@ + +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2012 Valient Gough + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + + +#include "fs/testing.h" + +#include +#include + +#include + +#include "cipher/Cipher.h" +#include "cipher/MemoryPool.h" + +#include "fs/MemFileIO.h" +#include "fs/FileUtils.h" +#include "fs/FSConfig.h" +#include "fs/MACFileIO.h" + +using namespace std; + +FSConfigPtr makeConfig(const shared_ptr& cipher, int blockSize) { + FSConfigPtr cfg = FSConfigPtr(new FSConfig); + cfg->cipher = cipher; + cfg->key = cipher->newRandomKey(); + cfg->config.reset(new EncfsConfig); + cfg->config->set_block_size(blockSize); + cfg->opts.reset(new EncFS_Opts); + + return cfg; +} + +void runWithCipher(const string& cipherName, int blockSize, + void (*func)(FSConfigPtr& config)) { + shared_ptr cipher = Cipher::New(cipherName); + ASSERT_TRUE(cipher.get() != NULL); + + FSConfigPtr cfg = makeConfig(cipher, blockSize); + ASSERT_NO_FATAL_FAILURE(func(cfg)); +} + +void runWithAllCiphers(void (*func)(FSConfigPtr& config)) { + list algorithms = Cipher::GetAlgorithmList(); + list::const_iterator it; + for (it = algorithms.begin(); it != algorithms.end(); ++it) { + int blockSize = it->blockSize.closest(512); + int keyLength = it->keyLength.closest(128); + SCOPED_TRACE(testing::Message() << "Testng with cipher " << it->name + << ", blocksize " << blockSize << ", keyLength " << keyLength); + shared_ptr cipher = Cipher::New(it->name, keyLength); + ASSERT_TRUE(cipher.get() != NULL); + + FSConfigPtr cfg = makeConfig(cipher, blockSize); + ASSERT_NO_FATAL_FAILURE(func(cfg)); + } +} + +void truncate(FileIO* a, FileIO* b, int len) { + SCOPED_TRACE(testing::Message() << "Truncate from " << a->getSize() + << " to len " << len); + a->truncate(len); + ASSERT_EQ(len, a->getSize()); + + b->truncate(len); + ASSERT_EQ(len, b->getSize()); + + compare(a, b, 0, len); +} + +void writeRandom(FSConfigPtr& cfg, FileIO* a, FileIO* b, int offset, int len) { + SCOPED_TRACE(testing::Message() << "Write random " << offset << ", " << len); + + if (a->getSize() < offset + len) + a->truncate(offset + len); + + unsigned char *buf = new unsigned char[len]; + ASSERT_TRUE(cfg->cipher->randomize(buf, len, false)); + + IORequest req; + req.data = new unsigned char[len]; + req.dataLen = len; + + memcpy(req.data, buf, len); + req.offset = offset; + ASSERT_TRUE(a->write(req)); + + memcpy(req.data, buf, len); + req.offset = offset; + ASSERT_TRUE(b->write(req)); + + compare(a, b, offset, len); + + delete[] buf; +} + +void compare(FileIO* a, FileIO* b, int offset, int len) { + SCOPED_TRACE(testing::Message() << "compare " << offset << ", " << len + << " from file length " << a->getSize()); + unsigned char *buf1 = new unsigned char[len]; + unsigned char *buf2 = new unsigned char[len]; + memset(buf1, 0, len); + memset(buf2, 0, len); + + IORequest req; + req.offset = offset; + req.data = buf1; + req.dataLen = len; + ssize_t size1 = a->read(req); + + req.offset = offset; + req.data = buf2; + req.dataLen = len; + ssize_t size2 = b->read(req); + + ASSERT_EQ(size1, size2); + for(int i = 0; i < len; i++) { + bool match = (buf1[i] == buf2[i]); + ASSERT_TRUE(match) << "mismatched data at offset " << i << " of " << len; + if(!match) { + break; + } + } + + delete[] buf1; + delete[] buf2; +} + +void comparisonTest(FSConfigPtr& cfg, FileIO* a, FileIO* b) { + const int size = 18*1024; + writeRandom(cfg, a, b, 0, size); + if (testing::Test::HasFatalFailure()) return; + compare(a, b, 0, size); + if (testing::Test::HasFatalFailure()) return; + + for (int i = 0; i < 10000; i++) { + SCOPED_TRACE(testing::Message() << "Test Loop " << i); + int len = 128 + random() % 2048; + int offset = (len == a->getSize()) ? 0 + : random() % (a->getSize() - len); + writeRandom(cfg, a, b, offset, len); + if (testing::Test::HasFatalFailure()) return; + ASSERT_EQ(a->getSize(), b->getSize()); + } + + SCOPED_TRACE("Final Compare"); + compare(a, b, 0, a->getSize()); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + diff --git a/fs/testing.h b/fs/testing.h new file mode 100644 index 0000000..a4aeec3 --- /dev/null +++ b/fs/testing.h @@ -0,0 +1,23 @@ +#ifndef _TESTING_incl_ +#define _TESTING_incl_ + +#include + +#include "cipher/Cipher.h" +#include "fs/FileUtils.h" +#include "fs/FSConfig.h" + +class FileIO; + +FSConfigPtr makeConfig(const shared_ptr& cipher, int blockSize); + +void runWithCipher(const std::string& cipherName, int blockSize, + void (*func)(FSConfigPtr& config)); +void runWithAllCiphers(void (*func)(FSConfigPtr& config)); + +void comparisonTest(FSConfigPtr& cfg, FileIO* a, FileIO* b); + +void compare(FileIO* a, FileIO* b, int offset, int len); + +#endif + diff --git a/m4/Makefile.am b/m4/Makefile.am deleted file mode 100644 index 33905df..0000000 --- a/m4/Makefile.am +++ /dev/null @@ -1,10 +0,0 @@ -EXTRA_DIST = *.m4 - -#\ -# ax_pthread.m4 intl.m4 lib-ld.m4 ltsugar.m4 size_max.m4 \ -# codeset.m4 intldir.m4 lib-link.m4 ltversion.m4 stdint_h.m4 \ -# gettext.m4 intlmacosx.m4 lib-prefix.m4 lt~obsolete.m4 uintmax_t.m4 \ -# glibc2.m4 intmax.m4 libtool.m4 nls.m4 visibility.m4 \ -# glibc21.m4 inttypes-pri.m4 lock.m4 po.m4 wchar_t.m4 \ -# iconv.m4 inttypes_h.m4 longlong.m4 printf-posix.m4 wint_t.m4 \ -# intdiv0.m4 lcmessage.m4 ltoptions.m4 progtest.m4 xsize.m4 diff --git a/m4/ax_ext_check_header.m4 b/m4/ax_ext_check_header.m4 deleted file mode 100644 index af52745..0000000 --- a/m4/ax_ext_check_header.m4 +++ /dev/null @@ -1,58 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_ext_check_header.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_EXT_CHECK_HEADER(
, ). -# -# DESCRIPTION -# -# Check for
with -I for each path in if need be. -# The first sucecssful path is chosen (eg if you say -# AX_EXT_CHECK_HEADER(foo.h, bar baz qux) and -Ibaz works then -Iqux will -# not be tested. -# -# Any -I flags that locate a header are added to CFLAGS and CPPFLAGS. AS -# with AC_CHECK_HEADERS it causes HAVE_
_H to be defined as 1. -# -# Example: -# -# AX_EXT_HAVE_HEADER(openssl/rsa.h, /usr/local/include /usr/local/ssl/include /usr/local/openssl/include) -# -# LICENSE -# -# Copyright (c) 2008 Duncan Simpson -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 7 - -AC_DEFUN([AX_EXT_HAVE_HEADER], -[AC_LANG_PUSH(C) - AC_CHECK_HEADER($1, [$3 got="yes"], [$4 got="no"], $5) - hdr=`echo $1 | $as_tr_sh` - for dir in $2; do - if test "x${got}" = "xno"; then - ext_hashdr_cvdir=`echo $dir | $as_tr_sh` - AC_CACHE_CHECK([for $1 library with -I$dir], - [ext_cv${ext_hashdr_cvdir}_hashdr_${hdr}], - [ext_have_hdr_save_cflags=${CFLAGS} - CFLAGS="${CFLAGS} -I${dir}" - AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM([#include <$1>])], - [got="yes"; eval "ext_cv${ext_hashdr_cvdir}_hashdr_${hdr}"="yes"], - [got="no"; eval "ext_cv${ext_hashdr_cvdir}_hashdr_${hdr}"="no"]) - CFLAGS=$ext_have_hdr_save_cflags]) - if eval `echo 'test x${'ext_cv${ext_hashdr_cvdir}_hashdr_${hdr}'}' = "xyes"`; then - CFLAGS="${CFLAGS} -I${dir}" - CPPFLAGS="${CPPFLAGS} -I${dir}" - got="yes"; - hdr=`echo $1 | $as_tr_cpp` - AC_DEFINE_UNQUOTED(HAVE_${hdr}, 1, - [Define this if you have the $1 header]) - fi; fi; done -AC_LANG_POP]) diff --git a/m4/ax_ext_have_lib.m4 b/m4/ax_ext_have_lib.m4 deleted file mode 100644 index 4b84ba0..0000000 --- a/m4/ax_ext_have_lib.m4 +++ /dev/null @@ -1,72 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_ext_have_lib.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_EXT_HAVE_LIB(, , , ) -# -# DESCRIPTION -# -# AX_EXT_HAVE_LIB is identical to AC_SEARCH_LIBS with the exception that -# will add -L when looking, and use a different variable for -# each directory. -# -# Any required -L flags are added to LDFLAGS and located -# libraies are added to LIBS -# -# Some libraries are unlinkable without other extra libraries, which can -# be specified in the 4th argument. The mysql client library needs -lz, -# for example. -# -# Example: -# -# AX_EXT_HAVE_LIB(/lib /usr/lib /usr/local/lib /usr/lib/mysql /usr/local/mysql/lib, mysqlclient, mysql_init, [-lz]) -# -# which finds the mysql client library if succeds system when it tries -# with -L/usr/lib/mysql then it adds -lmysqlclient to LIBS and -# -L/usr/lib/mysql to LDFLAGS. -# -# The test itself is based on the autoconf 2.53 version of AC_SEARCH_LIBS. -# -# LICENSE -# -# Copyright (c) 2008 Duncan Simpson -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 9 - -AC_DEFUN([AX_EXT_HAVE_LIB], -[ -new_ldflags=${LDFLAGS} -new_libs=$LIBS -AC_CHECK_LIB([$2], $3, new_libs="-l$2"; ext_lib_found="yes", ext_lib_found="no") -for dir in $1 -do -if test $ext_lib_found = no -then -ext_haslib_cvdir=`echo $dir | $as_tr_sh` -AC_CACHE_CHECK([for $2 library with -L$dir], [ext_cv${ext_haslib_cvdir}_haslib_$2], -[ext_func_search_save_LIBS=$LIBS -ext_func_save_ldflags=${LDFLAGS} -LIBS="-l$2 $4 ${ext_func_search_save_LIBS}" -LDFLAGS="-L$dir ${ext_func_save_ldflags}" -AC_TRY_LINK_FUNC([$3], [eval "ext_cv${ext_haslib_cvdir}_haslib_$2"="yes"], -[eval "ext_cv${ext_haslib_cvdir}_haslib_$2"="no"]) -LIBS=$ext_func_search_save_LIBS -LDFLAGS=$ext_func_save_ldflags -]) -if eval `echo 'test x${'ext_cv${ext_haslib_cvdir}_haslib_$2'}' = "xyes"`; then -new_libs="-l$2" -new_ldflags="-L${dir} ${new_ldflags}" -ext_lib_found="yes" -fi -fi -done -LIBS="$new_libs ${LIBS}" -LDFLAGS=$new_ldflags -]) diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4 deleted file mode 100644 index e20a388..0000000 --- a/m4/ax_pthread.m4 +++ /dev/null @@ -1,309 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_pthread.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) -# -# DESCRIPTION -# -# This macro figures out how to build C programs using POSIX threads. It -# sets the PTHREAD_LIBS output variable to the threads library and linker -# flags, and the PTHREAD_CFLAGS output variable to any special C compiler -# flags that are needed. (The user can also force certain compiler -# flags/libs to be tested by setting these environment variables.) -# -# Also sets PTHREAD_CC to any special C compiler that is needed for -# multi-threaded programs (defaults to the value of CC otherwise). (This -# is necessary on AIX to use the special cc_r compiler alias.) -# -# NOTE: You are assumed to not only compile your program with these flags, -# but also link it with them as well. e.g. you should link with -# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS -# -# If you are only building threads programs, you may wish to use these -# variables in your default LIBS, CFLAGS, and CC: -# -# LIBS="$PTHREAD_LIBS $LIBS" -# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" -# CC="$PTHREAD_CC" -# -# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant -# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name -# (e.g. PTHREAD_CREATE_UNDETACHED on AIX). -# -# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the -# PTHREAD_PRIO_INHERIT symbol is defined when compiling with -# PTHREAD_CFLAGS. -# -# ACTION-IF-FOUND is a list of shell commands to run if a threads library -# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it -# is not found. If ACTION-IF-FOUND is not specified, the default action -# will define HAVE_PTHREAD. -# -# Please let the authors know if this macro fails on any platform, or if -# you have any other suggestions or comments. This macro was based on work -# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help -# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by -# Alejandro Forero Cuervo to the autoconf macro repository. We are also -# grateful for the helpful feedback of numerous users. -# -# Updated for Autoconf 2.68 by Daniel Richard G. -# -# LICENSE -# -# Copyright (c) 2008 Steven G. Johnson -# Copyright (c) 2011 Daniel Richard G. -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 17 - -AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) -AC_DEFUN([AX_PTHREAD], [ -AC_REQUIRE([AC_CANONICAL_HOST]) -AC_LANG_PUSH([C]) -ax_pthread_ok=no - -# We used to check for pthread.h first, but this fails if pthread.h -# requires special compiler flags (e.g. on True64 or Sequent). -# It gets checked for in the link test anyway. - -# First of all, check if the user has set any of the PTHREAD_LIBS, -# etcetera environment variables, and if threads linking works using -# them: -if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then - save_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - save_LIBS="$LIBS" - LIBS="$PTHREAD_LIBS $LIBS" - AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) - AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) - AC_MSG_RESULT($ax_pthread_ok) - if test x"$ax_pthread_ok" = xno; then - PTHREAD_LIBS="" - PTHREAD_CFLAGS="" - fi - LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" -fi - -# We must check for the threads library under a number of different -# names; the ordering is very important because some systems -# (e.g. DEC) have both -lpthread and -lpthreads, where one of the -# libraries is broken (non-POSIX). - -# Create a list of thread flags to try. Items starting with a "-" are -# C compiler flags, and other items are library names, except for "none" -# which indicates that we try without any flags at all, and "pthread-config" -# which is a program returning the flags for the Pth emulation library. - -ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" - -# The ordering *is* (sometimes) important. Some notes on the -# individual items follow: - -# pthreads: AIX (must check this before -lpthread) -# none: in case threads are in libc; should be tried before -Kthread and -# other compiler flags to prevent continual compiler warnings -# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) -# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) -# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) -# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) -# -pthreads: Solaris/gcc -# -mthreads: Mingw32/gcc, Lynx/gcc -# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it -# doesn't hurt to check since this sometimes defines pthreads too; -# also defines -D_REENTRANT) -# ... -mt is also the pthreads flag for HP/aCC -# pthread: Linux, etcetera -# --thread-safe: KAI C++ -# pthread-config: use pthread-config program (for GNU Pth library) - -case "${host_cpu}-${host_os}" in - *solaris*) - - # On Solaris (at least, for some versions), libc contains stubbed - # (non-functional) versions of the pthreads routines, so link-based - # tests will erroneously succeed. (We need to link with -pthreads/-mt/ - # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather - # a function called by this macro, so we could check for that, but - # who knows whether they'll stub that too in a future libc.) So, - # we'll just look for -pthreads and -lpthread first: - - ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" - ;; - - *-darwin*) - ax_pthread_flags="-pthread $ax_pthread_flags" - ;; -esac - -if test x"$ax_pthread_ok" = xno; then -for flag in $ax_pthread_flags; do - - case $flag in - none) - AC_MSG_CHECKING([whether pthreads work without any flags]) - ;; - - -*) - AC_MSG_CHECKING([whether pthreads work with $flag]) - PTHREAD_CFLAGS="$flag" - ;; - - pthread-config) - AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) - if test x"$ax_pthread_config" = xno; then continue; fi - PTHREAD_CFLAGS="`pthread-config --cflags`" - PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" - ;; - - *) - AC_MSG_CHECKING([for the pthreads library -l$flag]) - PTHREAD_LIBS="-l$flag" - ;; - esac - - save_LIBS="$LIBS" - save_CFLAGS="$CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - - # Check for various functions. We must include pthread.h, - # since some functions may be macros. (On the Sequent, we - # need a special flag -Kthread to make this header compile.) - # We check for pthread_join because it is in -lpthread on IRIX - # while pthread_create is in libc. We check for pthread_attr_init - # due to DEC craziness with -lpthreads. We check for - # pthread_cleanup_push because it is one of the few pthread - # functions on Solaris that doesn't have a non-functional libc stub. - # We try pthread_create on general principles. - AC_LINK_IFELSE([AC_LANG_PROGRAM([#include - static void routine(void *a) { a = 0; } - static void *start_routine(void *a) { return a; }], - [pthread_t th; pthread_attr_t attr; - pthread_create(&th, 0, start_routine, 0); - pthread_join(th, 0); - pthread_attr_init(&attr); - pthread_cleanup_push(routine, 0); - pthread_cleanup_pop(0) /* ; */])], - [ax_pthread_ok=yes], - []) - - LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" - - AC_MSG_RESULT($ax_pthread_ok) - if test "x$ax_pthread_ok" = xyes; then - break; - fi - - PTHREAD_LIBS="" - PTHREAD_CFLAGS="" -done -fi - -# Various other checks: -if test "x$ax_pthread_ok" = xyes; then - save_LIBS="$LIBS" - LIBS="$PTHREAD_LIBS $LIBS" - save_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - - # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. - AC_MSG_CHECKING([for joinable pthread attribute]) - attr_name=unknown - for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do - AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], - [int attr = $attr; return attr /* ; */])], - [attr_name=$attr; break], - []) - done - AC_MSG_RESULT($attr_name) - if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then - AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, - [Define to necessary symbol if this constant - uses a non-standard name on your system.]) - fi - - AC_MSG_CHECKING([if more special flags are required for pthreads]) - flag=no - case "${host_cpu}-${host_os}" in - *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; - *-osf* | *-hpux*) flag="-D_REENTRANT";; - *solaris*) - if test "$GCC" = "yes"; then - flag="-D_REENTRANT" - else - flag="-mt -D_REENTRANT" - fi - ;; - esac - AC_MSG_RESULT(${flag}) - if test "x$flag" != xno; then - PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" - fi - - AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], - ax_cv_PTHREAD_PRIO_INHERIT, [ - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT;]])], - [ax_cv_PTHREAD_PRIO_INHERIT=yes], - [ax_cv_PTHREAD_PRIO_INHERIT=no]) - ]) - AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], - AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.])) - - LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" - - # More AIX lossage: must compile with xlc_r or cc_r - if test x"$GCC" != xyes; then - AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) - else - PTHREAD_CC=$CC - fi -else - PTHREAD_CC="$CC" -fi - -AC_SUBST(PTHREAD_LIBS) -AC_SUBST(PTHREAD_CFLAGS) -AC_SUBST(PTHREAD_CC) - -# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: -if test x"$ax_pthread_ok" = xyes; then - ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) - : -else - ax_pthread_ok=no - $2 -fi -AC_LANG_POP -])dnl AX_PTHREAD diff --git a/makedist.sh b/makedist.sh deleted file mode 100644 index fefded9..0000000 --- a/makedist.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -rm m4/[c-z]*.m4 - -echo Creating autoconf scripts... -sh ./reconfig.sh - -echo Configuring... -./configure - -sh ./makedist2.sh - diff --git a/makedist2.sh.in b/makedist2.sh.in deleted file mode 100644 index 8034705..0000000 --- a/makedist2.sh.in +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh - -# create distribution file -make dist - -# create tar archive and signature -tarArchive=@PACKAGE@-@VERSION@.tgz -mv @PACKAGE@-@VERSION@.tar.gz $tarArchive -# let the user know why they're being asked for a passpharse -echo "Signing tar archive - enter GPG password"; -gpg --detach-sign -a $tarArchive - -# create rpms -#cp $tarArchive /usr/src/packages/SOURCES -#echo "Building signed RPM files - enter GPG password"; -#rpmbuild -ba --sign @PACKAGE@.spec - -# move all distribution files to dist directory -mkdir dist -mv $tarArchive dist -mv $tarArchive.asc dist -#mv /usr/src/packages/SRPMS/@PACKAGE@-@VERSION@-@RELEASE@.src.rpm dist -#mv /usr/src/packages/RPMS/i586/@PACKAGE@-@VERSION@-@RELEASE@.i586.rpm dist - -# cleanup -#rm /usr/src/packages/SOURCES/$tarArchive - diff --git a/po/CMakeLists.txt b/po/CMakeLists.txt new file mode 100644 index 0000000..feff967 --- /dev/null +++ b/po/CMakeLists.txt @@ -0,0 +1,37 @@ + +file (GLOB PO_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.po") +file (GLOB CXX_FILES RELATIVE ${PROJECT_SOURCE_DIR} "*/*.cpp") + +set (GettextTranslate_GMO_BINARY true) + +include (GetTextTranslate) + + + +#find_package (Gettext REQUIRED) +#find_program (GETTEXT_XGETTEXT_EXE xgettext) +# +#if (NOT GETTEXT_FOUND) +# message (STATUS " Gettext not found") +#endif (NOT GETTEXT_FOUND) +#if (NOT GETTEXT_XGETTEXT_EXE) +# message (STATUS " Can't find xgettext") +#endif (NOT GETTEXT_XGETTEXT_EXE) +# +# +#if (GETTEXT_FOUND AND GETTEXT_XGETTEXT_EXE) +# add_custom_command( +# OUTPUT ${PROJECT_NAME}.pot +# COMMAND ${GETTEXT_XGETTEXT_EXE} --omit-header --keyword=_ --keyword=N_ ${CXX_FILES} -o ${PROJECT_NAME}.pot +# ) +# +# if (PO_FILES) +# message (STATUS "Translations will be built!") +# gettext_create_translations (${PROJECT_NAME}.pot ALL ${PO_FILES}) +# else (PO_FILES) +# message (STATUS "Translations not found!") +# endif (PO_FILES) +#else (GETTEXT_FOUND AND GETTEXT_XGETTEXT_EXE) +# message (STATUS "Translations won't be built.") +#endif (GETTEXT_FOUND AND GETTEXT_XGETTEXT_EXE) + diff --git a/po/ChangeLog b/po/ChangeLog deleted file mode 100644 index 4734f6e..0000000 --- a/po/ChangeLog +++ /dev/null @@ -1,5 +0,0 @@ -2008-05-22 gettextize - - * Makefile.in.in: Upgrade to gettext-0.17. - * POTFILES.in: New file. - diff --git a/po/Makefile.in.in b/po/Makefile.in.in deleted file mode 100644 index fecf500..0000000 --- a/po/Makefile.in.in +++ /dev/null @@ -1,429 +0,0 @@ -# Makefile for PO directory in any package using GNU gettext. -# Copyright (C) 1995-1997, 2000-2007 by Ulrich Drepper -# -# This file can be copied and used freely without restrictions. It can -# be used in projects which are not available under the GNU General Public -# License but which still want to provide support for the GNU gettext -# functionality. -# Please note that the actual code of GNU gettext is covered by the GNU -# General Public License and is *not* in the public domain. -# -# Origin: gettext-0.17 -GETTEXT_MACRO_VERSION = 0.17 - -PACKAGE = @PACKAGE@ -VERSION = @VERSION@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ - -SHELL = /bin/sh -@SET_MAKE@ - -srcdir = @srcdir@ -top_srcdir = @top_srcdir@ -VPATH = @srcdir@ - -prefix = @prefix@ -exec_prefix = @exec_prefix@ -datarootdir = @datarootdir@ -datadir = @datadir@ -localedir = @localedir@ -gettextsrcdir = $(datadir)/gettext/po - -INSTALL = @INSTALL@ -INSTALL_DATA = @INSTALL_DATA@ - -# We use $(mkdir_p). -# In automake <= 1.9.x, $(mkdir_p) is defined either as "mkdir -p --" or as -# "$(mkinstalldirs)" or as "$(install_sh) -d". For these automake versions, -# @install_sh@ does not start with $(SHELL), so we add it. -# In automake >= 1.10, @mkdir_p@ is derived from ${MKDIR_P}, which is defined -# either as "/path/to/mkdir -p" or ".../install-sh -c -d". For these automake -# versions, $(mkinstalldirs) and $(install_sh) are unused. -mkinstalldirs = $(SHELL) @install_sh@ -d -install_sh = $(SHELL) @install_sh@ -MKDIR_P = @MKDIR_P@ -mkdir_p = @mkdir_p@ - -GMSGFMT_ = @GMSGFMT@ -GMSGFMT_no = @GMSGFMT@ -GMSGFMT_yes = @GMSGFMT_015@ -GMSGFMT = $(GMSGFMT_$(USE_MSGCTXT)) -MSGFMT_ = @MSGFMT@ -MSGFMT_no = @MSGFMT@ -MSGFMT_yes = @MSGFMT_015@ -MSGFMT = $(MSGFMT_$(USE_MSGCTXT)) -XGETTEXT_ = @XGETTEXT@ -XGETTEXT_no = @XGETTEXT@ -XGETTEXT_yes = @XGETTEXT_015@ -XGETTEXT = $(XGETTEXT_$(USE_MSGCTXT)) -MSGMERGE = msgmerge -MSGMERGE_UPDATE = @MSGMERGE@ --update -MSGINIT = msginit -MSGCONV = msgconv -MSGFILTER = msgfilter - -POFILES = @POFILES@ -GMOFILES = @GMOFILES@ -UPDATEPOFILES = @UPDATEPOFILES@ -DUMMYPOFILES = @DUMMYPOFILES@ -DISTFILES.common = Makefile.in.in remove-potcdate.sin \ -$(DISTFILES.common.extra1) $(DISTFILES.common.extra2) $(DISTFILES.common.extra3) -DISTFILES = $(DISTFILES.common) Makevars POTFILES.in \ -$(POFILES) $(GMOFILES) \ -$(DISTFILES.extra1) $(DISTFILES.extra2) $(DISTFILES.extra3) - -POTFILES = \ - -CATALOGS = @CATALOGS@ - -# Makevars gets inserted here. (Don't remove this line!) - -.SUFFIXES: -.SUFFIXES: .po .gmo .mo .sed .sin .nop .po-create .po-update - -.po.mo: - @echo "$(MSGFMT) -c -o $@ $<"; \ - $(MSGFMT) -c -o t-$@ $< && mv t-$@ $@ - -.po.gmo: - @lang=`echo $* | sed -e 's,.*/,,'`; \ - test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ - echo "$${cdcmd}rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics -o $${lang}.gmo $${lang}.po"; \ - cd $(srcdir) && rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics -o t-$${lang}.gmo $${lang}.po && mv t-$${lang}.gmo $${lang}.gmo - -.sin.sed: - sed -e '/^#/d' $< > t-$@ - mv t-$@ $@ - - -all: check-macro-version all-@USE_NLS@ - -all-yes: stamp-po -all-no: - -# Ensure that the gettext macros and this Makefile.in.in are in sync. -check-macro-version: - @test "$(GETTEXT_MACRO_VERSION)" = "@GETTEXT_MACRO_VERSION@" \ - || { echo "*** error: gettext infrastructure mismatch: using a Makefile.in.in from gettext version $(GETTEXT_MACRO_VERSION) but the autoconf macros are from gettext version @GETTEXT_MACRO_VERSION@" 1>&2; \ - exit 1; \ - } - -# $(srcdir)/$(DOMAIN).pot is only created when needed. When xgettext finds no -# internationalized messages, no $(srcdir)/$(DOMAIN).pot is created (because -# we don't want to bother translators with empty POT files). We assume that -# LINGUAS is empty in this case, i.e. $(POFILES) and $(GMOFILES) are empty. -# In this case, stamp-po is a nop (i.e. a phony target). - -# stamp-po is a timestamp denoting the last time at which the CATALOGS have -# been loosely updated. Its purpose is that when a developer or translator -# checks out the package via CVS, and the $(DOMAIN).pot file is not in CVS, -# "make" will update the $(DOMAIN).pot and the $(CATALOGS), but subsequent -# invocations of "make" will do nothing. This timestamp would not be necessary -# if updating the $(CATALOGS) would always touch them; however, the rule for -# $(POFILES) has been designed to not touch files that don't need to be -# changed. -stamp-po: $(srcdir)/$(DOMAIN).pot - test ! -f $(srcdir)/$(DOMAIN).pot || \ - test -z "$(GMOFILES)" || $(MAKE) $(GMOFILES) - @test ! -f $(srcdir)/$(DOMAIN).pot || { \ - echo "touch stamp-po" && \ - echo timestamp > stamp-poT && \ - mv stamp-poT stamp-po; \ - } - -# Note: Target 'all' must not depend on target '$(DOMAIN).pot-update', -# otherwise packages like GCC can not be built if only parts of the source -# have been downloaded. - -# This target rebuilds $(DOMAIN).pot; it is an expensive operation. -# Note that $(DOMAIN).pot is not touched if it doesn't need to be changed. -$(DOMAIN).pot-update: $(POTFILES) $(srcdir)/POTFILES.in remove-potcdate.sed - if LC_ALL=C grep 'GNU @PACKAGE@' $(top_srcdir)/* 2>/dev/null | grep -v 'libtool:' >/dev/null; then \ - package_gnu='GNU '; \ - else \ - package_gnu=''; \ - fi; \ - if test -n '$(MSGID_BUGS_ADDRESS)' || test '$(PACKAGE_BUGREPORT)' = '@'PACKAGE_BUGREPORT'@'; then \ - msgid_bugs_address='$(MSGID_BUGS_ADDRESS)'; \ - else \ - msgid_bugs_address='$(PACKAGE_BUGREPORT)'; \ - fi; \ - case `$(XGETTEXT) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \ - '' | 0.[0-9] | 0.[0-9].* | 0.1[0-5] | 0.1[0-5].* | 0.16 | 0.16.[0-1]*) \ - $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \ - --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) @XGETTEXT_EXTRA_OPTIONS@ \ - --files-from=$(srcdir)/POTFILES.in \ - --copyright-holder='$(COPYRIGHT_HOLDER)' \ - --msgid-bugs-address="$$msgid_bugs_address" \ - ;; \ - *) \ - $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \ - --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) @XGETTEXT_EXTRA_OPTIONS@ \ - --files-from=$(srcdir)/POTFILES.in \ - --copyright-holder='$(COPYRIGHT_HOLDER)' \ - --package-name="$${package_gnu}@PACKAGE@" \ - --package-version='@VERSION@' \ - --msgid-bugs-address="$$msgid_bugs_address" \ - ;; \ - esac - test ! -f $(DOMAIN).po || { \ - if test -f $(srcdir)/$(DOMAIN).pot; then \ - sed -f remove-potcdate.sed < $(srcdir)/$(DOMAIN).pot > $(DOMAIN).1po && \ - sed -f remove-potcdate.sed < $(DOMAIN).po > $(DOMAIN).2po && \ - if cmp $(DOMAIN).1po $(DOMAIN).2po >/dev/null 2>&1; then \ - rm -f $(DOMAIN).1po $(DOMAIN).2po $(DOMAIN).po; \ - else \ - rm -f $(DOMAIN).1po $(DOMAIN).2po $(srcdir)/$(DOMAIN).pot && \ - mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \ - fi; \ - else \ - mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \ - fi; \ - } - -# This rule has no dependencies: we don't need to update $(DOMAIN).pot at -# every "make" invocation, only create it when it is missing. -# Only "make $(DOMAIN).pot-update" or "make dist" will force an update. -$(srcdir)/$(DOMAIN).pot: - $(MAKE) $(DOMAIN).pot-update - -# This target rebuilds a PO file if $(DOMAIN).pot has changed. -# Note that a PO file is not touched if it doesn't need to be changed. -$(POFILES): $(srcdir)/$(DOMAIN).pot - @lang=`echo $@ | sed -e 's,.*/,,' -e 's/\.po$$//'`; \ - if test -f "$(srcdir)/$${lang}.po"; then \ - test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ - echo "$${cdcmd}$(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot"; \ - cd $(srcdir) && $(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot; \ - else \ - $(MAKE) $${lang}.po-create; \ - fi - - -install: install-exec install-data -install-exec: -install-data: install-data-@USE_NLS@ - if test "$(PACKAGE)" = "gettext-tools"; then \ - $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \ - for file in $(DISTFILES.common) Makevars.template; do \ - $(INSTALL_DATA) $(srcdir)/$$file \ - $(DESTDIR)$(gettextsrcdir)/$$file; \ - done; \ - for file in Makevars; do \ - rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \ - done; \ - else \ - : ; \ - fi -install-data-no: all -install-data-yes: all - $(mkdir_p) $(DESTDIR)$(datadir) - @catalogs='$(CATALOGS)'; \ - for cat in $$catalogs; do \ - cat=`basename $$cat`; \ - lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ - dir=$(localedir)/$$lang/LC_MESSAGES; \ - $(mkdir_p) $(DESTDIR)$$dir; \ - if test -r $$cat; then realcat=$$cat; else realcat=$(srcdir)/$$cat; fi; \ - $(INSTALL_DATA) $$realcat $(DESTDIR)$$dir/$(DOMAIN).mo; \ - echo "installing $$realcat as $(DESTDIR)$$dir/$(DOMAIN).mo"; \ - for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \ - if test -n "$$lc"; then \ - if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \ - link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \ - mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ - mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ - (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \ - for file in *; do \ - if test -f $$file; then \ - ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \ - fi; \ - done); \ - rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ - else \ - if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \ - :; \ - else \ - rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \ - mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ - fi; \ - fi; \ - rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \ - ln -s ../LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \ - ln $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \ - cp -p $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \ - echo "installing $$realcat link as $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo"; \ - fi; \ - done; \ - done - -install-strip: install - -installdirs: installdirs-exec installdirs-data -installdirs-exec: -installdirs-data: installdirs-data-@USE_NLS@ - if test "$(PACKAGE)" = "gettext-tools"; then \ - $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \ - else \ - : ; \ - fi -installdirs-data-no: -installdirs-data-yes: - $(mkdir_p) $(DESTDIR)$(datadir) - @catalogs='$(CATALOGS)'; \ - for cat in $$catalogs; do \ - cat=`basename $$cat`; \ - lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ - dir=$(localedir)/$$lang/LC_MESSAGES; \ - $(mkdir_p) $(DESTDIR)$$dir; \ - for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \ - if test -n "$$lc"; then \ - if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \ - link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \ - mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ - mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ - (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \ - for file in *; do \ - if test -f $$file; then \ - ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \ - fi; \ - done); \ - rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ - else \ - if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \ - :; \ - else \ - rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \ - mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ - fi; \ - fi; \ - fi; \ - done; \ - done - -# Define this as empty until I found a useful application. -installcheck: - -uninstall: uninstall-exec uninstall-data -uninstall-exec: -uninstall-data: uninstall-data-@USE_NLS@ - if test "$(PACKAGE)" = "gettext-tools"; then \ - for file in $(DISTFILES.common) Makevars.template; do \ - rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \ - done; \ - else \ - : ; \ - fi -uninstall-data-no: -uninstall-data-yes: - catalogs='$(CATALOGS)'; \ - for cat in $$catalogs; do \ - cat=`basename $$cat`; \ - lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ - for lc in LC_MESSAGES $(EXTRA_LOCALE_CATEGORIES); do \ - rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \ - done; \ - done - -check: all - -info dvi ps pdf html tags TAGS ctags CTAGS ID: - -mostlyclean: - rm -f remove-potcdate.sed - rm -f stamp-poT - rm -f core core.* $(DOMAIN).po $(DOMAIN).1po $(DOMAIN).2po *.new.po - rm -fr *.o - -clean: mostlyclean - -distclean: clean - rm -f Makefile Makefile.in POTFILES *.mo - -maintainer-clean: distclean - @echo "This command is intended for maintainers to use;" - @echo "it deletes files that may require special tools to rebuild." - rm -f stamp-po $(GMOFILES) - -distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) -dist distdir: - $(MAKE) update-po - @$(MAKE) dist2 -# This is a separate target because 'update-po' must be executed before. -dist2: stamp-po $(DISTFILES) - dists="$(DISTFILES)"; \ - if test "$(PACKAGE)" = "gettext-tools"; then \ - dists="$$dists Makevars.template"; \ - fi; \ - if test -f $(srcdir)/$(DOMAIN).pot; then \ - dists="$$dists $(DOMAIN).pot stamp-po"; \ - fi; \ - if test -f $(srcdir)/ChangeLog; then \ - dists="$$dists ChangeLog"; \ - fi; \ - for i in 0 1 2 3 4 5 6 7 8 9; do \ - if test -f $(srcdir)/ChangeLog.$$i; then \ - dists="$$dists ChangeLog.$$i"; \ - fi; \ - done; \ - if test -f $(srcdir)/LINGUAS; then dists="$$dists LINGUAS"; fi; \ - for file in $$dists; do \ - if test -f $$file; then \ - cp -p $$file $(distdir) || exit 1; \ - else \ - cp -p $(srcdir)/$$file $(distdir) || exit 1; \ - fi; \ - done - -update-po: Makefile - $(MAKE) $(DOMAIN).pot-update - test -z "$(UPDATEPOFILES)" || $(MAKE) $(UPDATEPOFILES) - $(MAKE) update-gmo - -# General rule for creating PO files. - -.nop.po-create: - @lang=`echo $@ | sed -e 's/\.po-create$$//'`; \ - echo "File $$lang.po does not exist. If you are a translator, you can create it through 'msginit'." 1>&2; \ - exit 1 - -# General rule for updating PO files. - -.nop.po-update: - @lang=`echo $@ | sed -e 's/\.po-update$$//'`; \ - if test "$(PACKAGE)" = "gettext-tools"; then PATH=`pwd`/../src:$$PATH; fi; \ - tmpdir=`pwd`; \ - echo "$$lang:"; \ - test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ - echo "$${cdcmd}$(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$lang.new.po"; \ - cd $(srcdir); \ - if $(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$tmpdir/$$lang.new.po; then \ - if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \ - rm -f $$tmpdir/$$lang.new.po; \ - else \ - if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \ - :; \ - else \ - echo "msgmerge for $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \ - exit 1; \ - fi; \ - fi; \ - else \ - echo "msgmerge for $$lang.po failed!" 1>&2; \ - rm -f $$tmpdir/$$lang.new.po; \ - fi - -$(DUMMYPOFILES): - -update-gmo: Makefile $(GMOFILES) - @: - -Makefile: Makefile.in.in Makevars $(top_builddir)/config.status @POMAKEFILEDEPS@ - cd $(top_builddir) \ - && $(SHELL) ./config.status $(subdir)/$@.in po-directories - -force: - -# Tell versions [3.59,3.63) of GNU make not to export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/po/POTFILES.in b/po/POTFILES.in index 667e27c..ec651e8 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1 +1,11 @@ -# List of source files which contain translatable strings. +# For updating this file, look at the result of: +# $ head -3 po/POTFILES.in > /tmp/POTFILES.in && grep -l 'i18n.h' ../*.{cpp,h}* >> /tmp/POTFILES.in && cp -f /tmp/POTFILES.in po/ + +../cipher/SSL_Cipher.cpp +../encfs/main.cpp +../fs/BlockFileIO.cpp +../fs/BlockNameIO.cpp +../fs/FileUtils.cpp +../fs/MACFileIO.cpp +../fs/StreamNameIO.cpp +../util/encfsctl.cpp diff --git a/po/Rules-quot b/po/Rules-quot deleted file mode 100644 index 9c2a995..0000000 --- a/po/Rules-quot +++ /dev/null @@ -1,47 +0,0 @@ -# Special Makefile rules for English message catalogs with quotation marks. - -DISTFILES.common.extra1 = quot.sed boldquot.sed en@quot.header en@boldquot.header insert-header.sin Rules-quot - -.SUFFIXES: .insert-header .po-update-en - -en@quot.po-create: - $(MAKE) en@quot.po-update -en@boldquot.po-create: - $(MAKE) en@boldquot.po-update - -en@quot.po-update: en@quot.po-update-en -en@boldquot.po-update: en@boldquot.po-update-en - -.insert-header.po-update-en: - @lang=`echo $@ | sed -e 's/\.po-update-en$$//'`; \ - if test "$(PACKAGE)" = "gettext"; then PATH=`pwd`/../src:$$PATH; GETTEXTLIBDIR=`cd $(top_srcdir)/src && pwd`; export GETTEXTLIBDIR; fi; \ - tmpdir=`pwd`; \ - echo "$$lang:"; \ - ll=`echo $$lang | sed -e 's/@.*//'`; \ - LC_ALL=C; export LC_ALL; \ - cd $(srcdir); \ - if $(MSGINIT) -i $(DOMAIN).pot --no-translator -l $$ll -o - 2>/dev/null | sed -f $$tmpdir/$$lang.insert-header | $(MSGCONV) -t UTF-8 | $(MSGFILTER) sed -f `echo $$lang | sed -e 's/.*@//'`.sed 2>/dev/null > $$tmpdir/$$lang.new.po; then \ - if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \ - rm -f $$tmpdir/$$lang.new.po; \ - else \ - if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \ - :; \ - else \ - echo "creation of $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \ - exit 1; \ - fi; \ - fi; \ - else \ - echo "creation of $$lang.po failed!" 1>&2; \ - rm -f $$tmpdir/$$lang.new.po; \ - fi - -en@quot.insert-header: insert-header.sin - sed -e '/^#/d' -e 's/HEADER/en@quot.header/g' $(srcdir)/insert-header.sin > en@quot.insert-header - -en@boldquot.insert-header: insert-header.sin - sed -e '/^#/d' -e 's/HEADER/en@boldquot.header/g' $(srcdir)/insert-header.sin > en@boldquot.insert-header - -mostlyclean: mostlyclean-quot -mostlyclean-quot: - rm -f *.insert-header diff --git a/po/boldquot.sed b/po/boldquot.sed deleted file mode 100644 index 4b937aa..0000000 --- a/po/boldquot.sed +++ /dev/null @@ -1,10 +0,0 @@ -s/"\([^"]*\)"/“\1”/g -s/`\([^`']*\)'/‘\1’/g -s/ '\([^`']*\)' / ‘\1’ /g -s/ '\([^`']*\)'$/ ‘\1’/g -s/^'\([^`']*\)' /‘\1’ /g -s/“”/""/g -s/“/“/g -s/”/”/g -s/‘/‘/g -s/’/’/g diff --git a/po/en@boldquot.header b/po/en@boldquot.header deleted file mode 100644 index fedb6a0..0000000 --- a/po/en@boldquot.header +++ /dev/null @@ -1,25 +0,0 @@ -# All this catalog "translates" are quotation characters. -# The msgids must be ASCII and therefore cannot contain real quotation -# characters, only substitutes like grave accent (0x60), apostrophe (0x27) -# and double quote (0x22). These substitutes look strange; see -# http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html -# -# This catalog translates grave accent (0x60) and apostrophe (0x27) to -# left single quotation mark (U+2018) and right single quotation mark (U+2019). -# It also translates pairs of apostrophe (0x27) to -# left single quotation mark (U+2018) and right single quotation mark (U+2019) -# and pairs of quotation mark (0x22) to -# left double quotation mark (U+201C) and right double quotation mark (U+201D). -# -# When output to an UTF-8 terminal, the quotation characters appear perfectly. -# When output to an ISO-8859-1 terminal, the single quotation marks are -# transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to -# grave/acute accent (by libiconv), and the double quotation marks are -# transliterated to 0x22. -# When output to an ASCII terminal, the single quotation marks are -# transliterated to apostrophes, and the double quotation marks are -# transliterated to 0x22. -# -# This catalog furthermore displays the text between the quotation marks in -# bold face, assuming the VT100/XTerm escape sequences. -# diff --git a/po/en@quot.header b/po/en@quot.header deleted file mode 100644 index a9647fc..0000000 --- a/po/en@quot.header +++ /dev/null @@ -1,22 +0,0 @@ -# All this catalog "translates" are quotation characters. -# The msgids must be ASCII and therefore cannot contain real quotation -# characters, only substitutes like grave accent (0x60), apostrophe (0x27) -# and double quote (0x22). These substitutes look strange; see -# http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html -# -# This catalog translates grave accent (0x60) and apostrophe (0x27) to -# left single quotation mark (U+2018) and right single quotation mark (U+2019). -# It also translates pairs of apostrophe (0x27) to -# left single quotation mark (U+2018) and right single quotation mark (U+2019) -# and pairs of quotation mark (0x22) to -# left double quotation mark (U+201C) and right double quotation mark (U+201D). -# -# When output to an UTF-8 terminal, the quotation characters appear perfectly. -# When output to an ISO-8859-1 terminal, the single quotation marks are -# transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to -# grave/acute accent (by libiconv), and the double quotation marks are -# transliterated to 0x22. -# When output to an ASCII terminal, the single quotation marks are -# transliterated to apostrophes, and the double quotation marks are -# transliterated to 0x22. -# diff --git a/po/insert-header.sin b/po/insert-header.sin deleted file mode 100644 index b26de01..0000000 --- a/po/insert-header.sin +++ /dev/null @@ -1,23 +0,0 @@ -# Sed script that inserts the file called HEADER before the header entry. -# -# At each occurrence of a line starting with "msgid ", we execute the following -# commands. At the first occurrence, insert the file. At the following -# occurrences, do nothing. The distinction between the first and the following -# occurrences is achieved by looking at the hold space. -/^msgid /{ -x -# Test if the hold space is empty. -s/m/m/ -ta -# Yes it was empty. First occurrence. Read the file. -r HEADER -# Output the file's contents by reading the next line. But don't lose the -# current line while doing this. -g -N -bb -:a -# The hold space was nonempty. Following occurrences. Do nothing. -x -:b -} diff --git a/po/quot.sed b/po/quot.sed deleted file mode 100644 index 0122c46..0000000 --- a/po/quot.sed +++ /dev/null @@ -1,6 +0,0 @@ -s/"\([^"]*\)"/“\1”/g -s/`\([^`']*\)'/‘\1’/g -s/ '\([^`']*\)' / ‘\1’ /g -s/ '\([^`']*\)'$/ ‘\1’/g -s/^'\([^`']*\)' /‘\1’ /g -s/“”/""/g diff --git a/po/remove-potcdate.sin b/po/remove-potcdate.sin deleted file mode 100644 index 2436c49..0000000 --- a/po/remove-potcdate.sin +++ /dev/null @@ -1,19 +0,0 @@ -# Sed script that remove the POT-Creation-Date line in the header entry -# from a POT file. -# -# The distinction between the first and the following occurrences of the -# pattern is achieved by looking at the hold space. -/^"POT-Creation-Date: .*"$/{ -x -# Test if the hold space is empty. -s/P/P/ -ta -# Yes it was empty. First occurrence. Remove the line. -g -d -bb -:a -# The hold space was nonempty. Following occurrences. Do nothing. -x -:b -} diff --git a/reconfig.sh b/reconfig.sh deleted file mode 100644 index 9f86811..0000000 --- a/reconfig.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -autoreconf -i - diff --git a/subdirs b/subdirs deleted file mode 100644 index 6897267..0000000 --- a/subdirs +++ /dev/null @@ -1,3 +0,0 @@ -admin -autom4te.cache -encfs diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt new file mode 100644 index 0000000..e362709 --- /dev/null +++ b/util/CMakeLists.txt @@ -0,0 +1,40 @@ + +include_directories (${Encfs_SOURCE_DIR}/base) +link_directories (${Encfs_BINARY_DIR}/base) + +include_directories (${Encfs_SOURCE_DIR}/cipher) +link_directories (${Encfs_BINARY_DIR}/cipher) + +include_directories (${Encfs_SOURCE_DIR}/fs) +link_directories (${Encfs_BINARY_DIR}/fs) + +# TODO: move FUSE code into encfs-fs. +find_package (FUSE REQUIRED) +include_directories (${FUSE_INCLUDE_DIR}) + +include_directories (${CMAKE_BINARY_DIR}/base) + + +add_executable (encfsctl + encfsctl.cpp) +target_link_libraries (encfsctl + encfs-fs + encfs-cipher + encfs-base + ${GLOG_LIBRARIES} + ${FUSE_LIBRARIES} +) + +if (POD2MAN) + add_custom_target(util-man ALL + COMMAND ${POD2MAN} -u --section=1 --release=${ENCFS_VERSION} + --center="Encrypted Filesystem" + ${CMAKE_CURRENT_SOURCE_DIR}/encfsctl.pod + encfsctl.1) + install (FILES ${CMAKE_CURRENT_BINARY_DIR}/encfsctl.1 + DESTINATION + share/man/man1) +endif (POD2MAN) + +install (TARGETS encfsctl DESTINATION bin) + diff --git a/util/encfsctl.cpp b/util/encfsctl.cpp new file mode 100644 index 0000000..8b89113 --- /dev/null +++ b/util/encfsctl.cpp @@ -0,0 +1,844 @@ +/***************************************************************************** + * Author: Valient Gough + * + ***************************************************************************** + * Copyright (c) 2004, Valient Gough + * + * This program is free software; you can distribute it and/or modify it under + * the terms of the GNU General Public License (GPL), as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + + +#include "fs/encfs.h" + +#include "base/autosprintf.h" +#include "base/config.h" +#include "base/Error.h" +#include "base/i18n.h" + +#include "cipher/Cipher.h" + +#include "fs/FileUtils.h" +#include "fs/Context.h" +#include "fs/FileNode.h" +#include "fs/DirNode.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#ifdef HAVE_SSL +#define NO_DES +#include +#endif + +using namespace std; +using namespace gnu; + + +static int showInfo( int argc, char **argv ); +static int showVersion( int argc, char **argv ); +static int chpasswd( int argc, char **argv ); +static int chpasswdAutomaticly( int argc, char **argv ); +static int cmd_ls( int argc, char **argv ); +static int cmd_decode( int argc, char **argv ); +static int cmd_encode( int argc, char **argv ); +static int cmd_showcruft( int argc, char **argv ); +static int cmd_cat( int argc, char **argv ); +static int cmd_export( int argc, char **argv ); +static int cmd_showKey( int argc, char **argv ); + +struct CommandOpts +{ + const char *name; + int minOptions; + int maxOptions; + int (*func)(int argc, char **argv); + const char *argStr; + const char *usageStr; +} commands[] = +{ + {"info", 1, 1, showInfo, "(root dir)", + // xgroup(usage) + gettext_noop(" -- show information (Default command)")}, + {"showKey", 1, 1, cmd_showKey, "(root dir)", + // xgroup(usage) + gettext_noop(" -- show key")}, + {"passwd", 1, 1, chpasswd, "(root dir)", + // xgroup(usage) + gettext_noop(" -- change password for volume")}, + {"autopasswd", 1, 1, chpasswdAutomaticly, "(root dir)", + // xgroup(usage) + gettext_noop(" -- change password for volume, taking password" + " from standard input.\n\tNo prompts are issued.")}, + {"ls", 1, 2, cmd_ls, 0,0}, + {"showcruft", 1, 1, cmd_showcruft, "(root dir)", + // xgroup(usage) + gettext_noop(" -- show undecodable filenames in the volume")}, + {"cat", 2, 2, cmd_cat, "(root dir) path", + // xgroup(usage) + gettext_noop(" -- decodes the file and cats it to standard out")}, + {"decode", 1, 100, cmd_decode, "[--extpass=prog] (root dir) [encoded-name ...]", + // xgroup(usage) + gettext_noop(" -- decodes name and prints plaintext version")}, + {"encode", 1, 100, cmd_encode, "[--extpass=prog] (root dir) [plaintext-name ...]", + // xgroup(usage) + gettext_noop(" -- encodes a filename and print result")}, + {"export", 2, 2, cmd_export, "(root dir) path", + // xgroup(usage) + gettext_noop(" -- decrypts a volume and writes results to path")}, + {"--version", 0, 0, showVersion, "", + // xgroup(usage) + gettext_noop(" -- print version number and exit")}, + {0,0,0,0,0,0} +}; + + +static +void usage(const char *name) +{ + cerr << autosprintf(_("encfsctl version %s"), VERSION) << "\n" + << _("Usage:\n") + // displays usage commands, eg "./encfs (root dir) ..." + // xgroup(usage) + << autosprintf(_("%s (root dir)\n" + " -- displays information about the filesystem, or \n"), name); + + int offset = 0; + while(commands[offset].name != 0) + { + if( commands[offset].argStr != 0 ) + { + cerr << "encfsctl " << commands[offset].name << " " + << commands[offset].argStr << "\n" + << gettext( commands[offset].usageStr ) << "\n"; + } + ++offset; + } + + cerr << "\n" + // xgroup(usage) + << autosprintf(_("Example: \n%s info ~/.crypt\n"), name) + << "\n"; +} + +static bool checkDir( string &rootDir ) +{ + if( !isDirectory( rootDir.c_str() )) + { + cerr << autosprintf(_("directory %s does not exist.\n"), + rootDir.c_str()); + return false; + } + if(rootDir[ rootDir.length()-1 ] != '/') + rootDir.append("/"); + + return true; +} + +static int showVersion( int argc, char **argv ) +{ + (void)argc; + (void)argv; + // xgroup(usage) + cerr << autosprintf(_("encfsctl version %s"), VERSION) << "\n"; + + return EXIT_SUCCESS; +} + +static int showInfo( int argc, char **argv ) +{ + (void)argc; + string rootDir = argv[1]; + if( !checkDir( rootDir )) + return EXIT_FAILURE; + + EncfsConfig config; + ConfigType type = readConfig( rootDir, config ); + + // show information stored in config.. + switch(type) + { + case Config_None: + // xgroup(diag) + cout << _("Unable to load or parse config file\n"); + return EXIT_FAILURE; + case Config_Prehistoric: + // xgroup(diag) + cout << _("A really old EncFS filesystem was found. \n" + "It is not supported in this EncFS build.\n"); + return EXIT_FAILURE; + case Config_V3: + // xgroup(diag) + cout << "\n" << autosprintf(_("Version 3 configuration; " + "created by %s\n"), config.creator().c_str()); + break; + case Config_V4: + // xgroup(diag) + cout << "\n" << autosprintf(_("Version 4 configuration; " + "created by %s\n"), config.creator().c_str()); + break; + case Config_V5: + case Config_V6: + case Config_V7: + // xgroup(diag) + cout << "\n" << autosprintf(_("Version %i configuration; " + "created by %s (revision %i)\n"), + type, + config.creator().c_str(), + config.revision()); + break; + } + + showFSInfo( config ); + + return EXIT_SUCCESS; +} + +static RootPtr initRootInfo(int &argc, char ** &argv) +{ + RootPtr result; + shared_ptr opts( new EncFS_Opts() ); + opts->createIfNotFound = false; + opts->checkKey = false; + + static struct option long_options[] = { + {"extpass", 1, 0, 'p'}, + {0,0,0,0} + }; + + for(;;) + { + int option_index = 0; + + int res = getopt_long( argc, argv, "", + long_options, &option_index); + if(res == -1) + break; + + switch(res) + { + case 'p': + opts->passwordProgram.assign(optarg); + break; + default: + LOG(WARNING) << "getopt error: " << res; + break; + } + } + + argc -= optind; + argv += optind; + + if(argc == 0) + { + cerr << _("Incorrect number of arguments") << "\n"; + } else + { + opts->rootDir = string( argv[0] ); + + --argc; + ++argv; + + if(checkDir( opts->rootDir )) + result = initFS( NULL, opts ); + + if(!result) + cerr << _("Unable to initialize encrypted filesystem - check path.\n"); + } + + return result; +} + +static RootPtr initRootInfo(const char* crootDir) +{ + string rootDir(crootDir); + RootPtr result; + + if(checkDir( rootDir )) + { + shared_ptr opts( new EncFS_Opts() ); + opts->rootDir = rootDir; + opts->createIfNotFound = false; + opts->checkKey = false; + result = initFS( NULL, opts ); + } + + if(!result) + cerr << _("Unable to initialize encrypted filesystem - check path.\n"); + + return result; +} + +static int cmd_showKey( int argc, char **argv ) +{ + RootPtr rootInfo = initRootInfo(argv[1]); + + if(!rootInfo) + return EXIT_FAILURE; + else + { + // encode with itself + string b64Key = rootInfo->cipher->encodeAsString( + rootInfo->volumeKey, rootInfo->volumeKey ); + + cout << b64Key << "\n"; + + return EXIT_SUCCESS; + } +} + +static int cmd_decode( int argc, char **argv ) +{ + RootPtr rootInfo = initRootInfo(argc, argv); + if(!rootInfo) + return EXIT_FAILURE; + + if(argc > 0) + { + for(int i=0; iroot->plainPath( argv[i] ); + cout << name << "\n"; + } + } else + { + char buf[PATH_MAX+1]; + while(cin.getline(buf,PATH_MAX)) + { + cout << rootInfo->root->plainPath( buf ) << "\n"; + } + } + return EXIT_SUCCESS; +} + +static int cmd_encode( int argc, char **argv ) +{ + RootPtr rootInfo = initRootInfo(argc, argv); + if(!rootInfo) + return EXIT_FAILURE; + + if(argc > 0) + { + for(int i=0; iroot->cipherPathWithoutRoot(argv[i]); + cout << name << "\n"; + } + } else + { + char buf[PATH_MAX+1]; + while(cin.getline(buf,PATH_MAX)) + { + cout << rootInfo->root->cipherPathWithoutRoot( buf ) << "\n"; + } + } + return EXIT_SUCCESS; +} + +static int cmd_ls( int argc, char **argv ) +{ + (void)argc; + + RootPtr rootInfo = initRootInfo(argv[1]); + + if(!rootInfo) + return EXIT_FAILURE; + + // show files in directory + { + DirTraverse dt = rootInfo->root->openDir("/"); + if(dt.valid()) + { + for(string name = dt.nextPlaintextName(); !name.empty(); + name = dt.nextPlaintextName()) + { + shared_ptr fnode = + rootInfo->root->lookupNode( name.c_str(), "encfsctl-ls" ); + struct stat stbuf; + fnode->getAttr( &stbuf ); + + struct tm stm; + localtime_r( &stbuf.st_mtime, &stm ); + stm.tm_year += 1900; + // TODO: when I add "%s" to the end and name.c_str(), I get a + // seg fault from within strlen. Why ??? + printf("%11i %4i-%02i-%02i %02i:%02i:%02i %s\n", + int(stbuf.st_size), + int(stm.tm_year), int(stm.tm_mon), int(stm.tm_mday), + int(stm.tm_hour), int(stm.tm_min), int(stm.tm_sec), + name.c_str()); + } + } + } + + return EXIT_SUCCESS; +} + +// apply an operation to every block in the file +template +int processContents( const shared_ptr &rootInfo, + const char *path, T &op ) +{ + int errCode = 0; + shared_ptr node = rootInfo->root->openNode( path, "encfsctl", + O_RDONLY, &errCode ); + + if(!node) + { + // try treating filename as an enciphered path + string plainName = rootInfo->root->plainPath( path ); + node = rootInfo->root->lookupNode( plainName.c_str(), "encfsctl" ); + if(node) + { + errCode = node->open( O_RDONLY ); + if(errCode < 0) + node.reset(); + } + } + + if(!node) + { + cerr << "unable to open " << path << "\n"; + return errCode; + } else + { + unsigned char buf[512]; + int blocks = (node->getSize() + sizeof(buf)-1) / sizeof(buf); + // read all the data in blocks + for(int i=0; iread(i*sizeof(buf), buf, sizeof(buf)); + int res = op(buf, bytes); + if(res < 0) + return res; + } + } + return 0; +} + +class WriteOutput +{ + int _fd; +public: + WriteOutput(int fd) { _fd = fd; } + ~WriteOutput() { close(_fd); } + + int operator()(const void *buf, int count) + { + return (int)write(_fd, buf, count); + } +}; + +static int cmd_cat( int argc, char **argv ) +{ + (void)argc; + RootPtr rootInfo = initRootInfo(argv[1]); + + if(!rootInfo) + return EXIT_FAILURE; + + const char *path = argv[2]; + WriteOutput output(STDOUT_FILENO); + int errCode = processContents( rootInfo, path, output ); + + return errCode; +} + +static int copyLink(const struct stat &stBuf, + const shared_ptr &rootInfo, + const string &cpath, const string &destName ) +{ + vector buf(stBuf.st_size+1, 0); + int res = ::readlink( cpath.c_str(), &buf[0], stBuf.st_size ); + if(res == -1) + { + cerr << "unable to readlink of " << cpath << "\n"; + return EXIT_FAILURE; + } + + buf[res] = '\0'; + string decodedLink = rootInfo->root->plainPath(&buf[0]); + + res = ::symlink( decodedLink.c_str(), destName.c_str() ); + if(res == -1) + { + cerr << "unable to create symlink for " << cpath + << " to " << decodedLink << "\n"; + } + + return EXIT_SUCCESS; +} + +static int copyContents(const shared_ptr &rootInfo, + const char* encfsName, const char* targetName) +{ + shared_ptr node = + rootInfo->root->lookupNode( encfsName, "encfsctl" ); + + if(!node) + { + cerr << "unable to open " << encfsName << "\n"; + return EXIT_FAILURE; + } else + { + struct stat st; + + if(node->getAttr(&st) != 0) + return EXIT_FAILURE; + + if((st.st_mode & S_IFLNK) == S_IFLNK) + { + string d = rootInfo->root->cipherPath(encfsName); + char linkContents[PATH_MAX+2]; + + if(readlink (d.c_str(), linkContents, PATH_MAX + 1) <= 0) + { + cerr << "unable to read link " << encfsName << "\n"; + return EXIT_FAILURE; + } + symlink(rootInfo->root->plainPath(linkContents).c_str(), + targetName); + } else + { + int outfd = creat(targetName, st.st_mode); + + WriteOutput output(outfd); + processContents( rootInfo, encfsName, output ); + } + } + return EXIT_SUCCESS; +} + +static bool endsWith(const string &str, char ch) +{ + if(str.empty()) + return false; + else + return str[str.length()-1] == ch; +} + +static int traverseDirs(const shared_ptr &rootInfo, + string volumeDir, string destDir) +{ + if(!endsWith(volumeDir, '/')) + volumeDir.append("/"); + if(!endsWith(destDir, '/')) + destDir.append("/"); + + // Lookup directory node so we can create a destination directory + // with the same permissions + { + struct stat st; + shared_ptr dirNode = + rootInfo->root->lookupNode( volumeDir.c_str(), "encfsctl" ); + if(dirNode->getAttr(&st)) + return EXIT_FAILURE; + + mkdir(destDir.c_str(), st.st_mode); + } + + // show files in directory + DirTraverse dt = rootInfo->root->openDir(volumeDir.c_str()); + if(dt.valid()) + { + for(string name = dt.nextPlaintextName(); !name.empty(); + name = dt.nextPlaintextName()) + { + // Recurse to subdirectories + if(name != "." && name != "..") + { + string plainPath = volumeDir + name; + string cpath = rootInfo->root->cipherPath(plainPath.c_str()); + string destName = destDir + name; + + int r = EXIT_SUCCESS; + struct stat stBuf; + if( !lstat( cpath.c_str(), &stBuf )) + { + if( S_ISDIR( stBuf.st_mode ) ) + { + traverseDirs(rootInfo, (plainPath + '/').c_str(), + destName + '/'); + } else if( S_ISLNK( stBuf.st_mode )) + { + r = copyLink( stBuf, rootInfo, cpath, destName ); + } else + { + r = copyContents(rootInfo, plainPath.c_str(), + destName.c_str()); + } + } else + { + r = EXIT_FAILURE; + } + + if(r != EXIT_SUCCESS) + return r; + } + } + } + return EXIT_SUCCESS; +} + +static int cmd_export( int argc, char **argv ) +{ + (void)argc; + + RootPtr rootInfo = initRootInfo(argv[1]); + + if(!rootInfo) + return EXIT_FAILURE; + + string destDir = argv[2]; + // if the dir doesn't exist, then create it (with user permission) + if(!checkDir(destDir) && !userAllowMkdir(destDir.c_str(), 0700)) + return EXIT_FAILURE; + + return traverseDirs(rootInfo, "/", destDir); +} + +int showcruft( const shared_ptr &rootInfo, const char *dirName ) +{ + int found = 0; + DirTraverse dt = rootInfo->root->openDir( dirName ); + if(dt.valid()) + { + bool showedDir = false; + for(string name = dt.nextInvalid(); !name.empty(); + name = dt.nextInvalid()) + { + string cpath = rootInfo->root->cipherPath( dirName ); + cpath += '/'; + cpath += name; + + if(!showedDir) + { + // just before showing a list of files in a directory + cout << autosprintf(_("In directory %s: \n"), dirName); + showedDir = true; + } + ++found; + cout << cpath << "\n"; + } + + // now go back and look for directories to recurse into.. + dt = rootInfo->root->openDir( dirName ); + if(dt.valid()) + { + for(string name = dt.nextPlaintextName(); !name.empty(); + name = dt.nextPlaintextName()) + { + if( name == "." || name == "..") + continue; + + string plainPath = dirName; + plainPath += '/'; + plainPath += name; + + string cpath = rootInfo->root->cipherPath( plainPath.c_str() ); + + if(isDirectory( cpath.c_str() )) + found += showcruft( rootInfo, plainPath.c_str() ); + } + } + } + + return found; +} + +/* + iterate recursively through the filesystem and print out names of files + which have filenames which cannot be decoded with the given key.. + */ +static int cmd_showcruft( int argc, char **argv ) +{ + (void)argc; + + RootPtr rootInfo = initRootInfo(argv[1]); + + if(!rootInfo) + return EXIT_FAILURE; + + int filesFound = showcruft( rootInfo, "/" ); + + cerr << autosprintf("Found %i invalid file(s).", filesFound) << "\n"; + + return EXIT_SUCCESS; +} + +static int do_chpasswd( bool useStdin, bool annotate, int argc, char **argv ) +{ + (void)argc; + string rootDir = argv[1]; + if( !checkDir( rootDir )) + return EXIT_FAILURE; + + EncfsConfig config; + ConfigType cfgType = readConfig( rootDir, config ); + + if(cfgType == Config_None) + { + cout << _("Unable to load or parse config file\n"); + return EXIT_FAILURE; + } + + // instanciate proper cipher + shared_ptr cipher = getCipher(config); + if(!cipher) + { + cout << autosprintf(_("Unable to find specified cipher \"%s\"\n"), + config.cipher().name().c_str()); + return EXIT_FAILURE; + } + + // ask for existing password + cout << _("Enter current Encfs password\n"); + if (annotate) + cerr << "$PROMPT$ passwd" << endl; + CipherKey userKey = getUserKey( config, useStdin ); + if(!userKey) + return EXIT_FAILURE; + + // decode volume key using user key -- at this point we detect an incorrect + // password if the key checksum does not match (causing readKey to fail). + CipherKey volumeKey = cipher->readKey( + (const unsigned char *)config.key().ciphertext().data(), userKey ); + + if(!volumeKey) + { + cout << _("Invalid password\n"); + return EXIT_FAILURE; + } + + // Now, get New user key.. + userKey.reset(); + cout << _("Enter new Encfs password\n"); + + // create new key + if( useStdin ) + { + if (annotate) + cerr << "$PROMPT$ new_passwd" << endl; + } + + userKey = getNewUserKey( config, useStdin, string(), string() ); + + // re-encode the volume key using the new user key and write it out.. + int result = EXIT_FAILURE; + if(userKey) + { + int encodedKeySize = cipher->encodedKeySize(); + unsigned char *keyBuf = new unsigned char[ encodedKeySize ]; + + // encode volume key with new user key + cipher->writeKey( volumeKey, keyBuf, userKey ); + userKey.reset(); + + EncryptedKey *key = config.mutable_key(); + key->set_ciphertext( keyBuf, encodedKeySize ); + delete[] keyBuf; + + if(saveConfig( rootDir, config )) + { + // password modified -- changes volume key of filesystem.. + cout << _("Volume Key successfully updated.\n"); + result = EXIT_SUCCESS; + } else + { + cout << _("Error saving modified config file.\n"); + } + } else + { + cout << _("Error creating key\n"); + } + + volumeKey.reset(); + + return result; +} + +static int chpasswd( int argc, char **argv ) +{ + return do_chpasswd( false, false, argc, argv ); +} + +static int chpasswdAutomaticly( int argc, char **argv ) +{ + return do_chpasswd( true, false, argc, argv ); +} + + +int main(int argc, char **argv) +{ + FLAGS_logtostderr = 1; + FLAGS_minloglevel = 1; + + google::InitGoogleLogging(argv[0]); + google::InstallFailureSignalHandler(); + +#ifdef LOCALEDIR + setlocale( LC_ALL, "" ); + bindtextdomain( PACKAGE, LOCALEDIR ); + textdomain( PACKAGE ); +#endif + +#ifdef HAVE_SSL + SSL_load_error_strings(); + SSL_library_init(); +#endif + + if(argc < 2) + { + usage( argv[0] ); + return EXIT_FAILURE; + } + + if(argc == 2 && !(*argv[1] == '-' && *(argv[1]+1) == '-')) + { + // default command when only 1 argument given -- treat the argument as + // a directory.. + return showInfo( argc, argv ); + } else + { + // find the specified command + int offset = 0; + while(commands[offset].name != 0) + { + if(!strcmp( argv[1], commands[offset].name )) + break; + ++offset; + } + + if(commands[offset].name == 0) + { + cerr << autosprintf(_("invalid command: \"%s\""), argv[1]) << "\n"; + } else + { + if((argc-2 < commands[offset].minOptions) || + (argc-2 > commands[offset].maxOptions)) + { + cerr << autosprintf( + _("Incorrect number of arguments for command \"%s\""), + argv[1]) << "\n"; + } else + return (*commands[offset].func)( argc-1, argv+1 ); + } + } + + return EXIT_FAILURE; +} diff --git a/encfs/encfsctl.pod b/util/encfsctl.pod similarity index 100% rename from encfs/encfsctl.pod rename to util/encfsctl.pod diff --git a/encfs/encfssh b/util/encfssh similarity index 100% rename from encfs/encfssh rename to util/encfssh diff --git a/encfs/makeKey.cpp b/util/makeKey.cpp similarity index 100% rename from encfs/makeKey.cpp rename to util/makeKey.cpp