Import version 1.4.0

git-svn-id: http://encfs.googlecode.com/svn/trunk@2 db9cf616-1c43-0410-9cb8-a902689de0d6
This commit is contained in:
Valient Gough 2008-01-07 08:09:04 +00:00
parent b676408ce1
commit 4fc836f032
83 changed files with 22731 additions and 0 deletions

8
AUTHORS Normal file
View File

@ -0,0 +1,8 @@
Valient Gough <vgough@pobox.com>
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.

674
COPYING Normal file
View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

1190
ChangeLog Normal file

File diff suppressed because it is too large Load Diff

23
INSTALL Normal file
View File

@ -0,0 +1,23 @@
Encfs uses the GNU autoconf / automake toolchain to create makefiles.
The configure script is automatically generated, but is part of most EncFS
distributions. If you have a distribution that does not contain the configure
script, then you can generate it by either running the "makeconf.sh" script, or
running "make -f Makefile.dist".
To build encfs, run:
./configure
make
This creates two executables, encfs and encfsctl in the encfs directory. You
can install to in a system directory via "make install". If the default path
(/usr/local) is not where you want things installed, then use the "--prefix"
option to configure to specify the install prefix.
Encfs and encfsctl can also be installed by hand. They need no special
permissions. You may also want the man pages encfs.1 and encfsctl.1.

17
Makefile.am Normal file
View File

@ -0,0 +1,17 @@
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

2
Makefile.common Normal file
View File

@ -0,0 +1,2 @@
KDE_OPTIONS = qtonly

8
Makefile.dist Normal file
View File

@ -0,0 +1,8 @@
default: all
all:
aclocal
autoheader
automake
autoconf

132
README Normal file
View File

@ -0,0 +1,132 @@
For notes about internationalization, see README-NLS.
EncFS is a program which provides an encrypted virtual filesystem for Linux
using the FUSE kernel module ( see http://sourceforge.net/projects/avf to
download the latest version of FUSE ). FUSE provides a loadable kernel module
which exports a filesystem interface to user-mode. EncFS runs entirely in
user-mode and acts as a transparent encrypted filesystem.
Usage:
- To see command line options, see the man page for encfs and encfsctl, or for
brief usage message, run the programs without an argument (or '-h'):
% encfs -h
% man encfs
- To create a new encrypted filesystem:
% encfs [source dir] [destination mount point]
eg.: "encfs ~/.crypt ~/crypt". Both directories should already exist,
although Encfs will ask if it can create them if they do not. If the
"~/.crypt" directory does not already contain encrypted filesystem data,
then the user is prompted for a password for the new encryption directory.
The encrypted files will be stored in ~/.crypt, and plaintext access will be
through ~/crypt
- To mount an existing filesystem:
% encfs [source dir] [destination mount point]
This works just like creating a new filesystem. If the Encfs control file
is found in the directory, then an attempt is made to mount an existing
filesystem. If the control file is not found, then the filesystem is
created.
Technology:
- Encfs uses algorithms from third-party libraries (OpenSSL is the default) 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.
- EncFS has two encryption modes, which are used in different places:
- Stream encryption:
Used for filenames and partial blocks at the end of files.
The cipher is run in CFB stream mode in multiple passes over the data,
with data order reversal between passes to make data more
interdependent.
- Block encryption:
Fixed size filesystem blocks are encrypted using the cipher in CBC
mode. The filesystem block size is a multiple of the cipher block
size, and is configurable on filesystem creation and can be up to 4096
bytes in size. Each block has a deterministic initialization vector
which allows for simple random access to blocks within a file.
- Filename encryption:
Filenames are encrypted using either a stream mode or a block mode, in both
cases with an initialization vector based on the HMAC checksum of the
filename.
Using a deterministic initial vector allows fast directory lookups, as no
salt value needs to be looked up when converting from plaintext name to
encrypted name. It also means very similar filenames (such as "foo1" and
"foo2") will encrypt to very different values, to frustrate any attempt to
see how closely related two files are based on their encrypted names.
- Data blocks are handled in fixed size blocks (64 byte blocks for Encfs
versions 0.2 - 0.6, and user specified sizes in newer versions of Encfs,
defaulting to 512 byte block). The block size is set during creation of the
filesystem and is constant thereafter.
Full filesystem blocks are encrypted in the cipher's block mode as described
above. Partial filesystem blocks are encrypted using the cipher's stream
mode, which involves multiple passes over the data along with data
reordering to make the data in the partial block highly interdependent.
For both modes this means that a change to a byte in the encrypted stream
may affecting several bytes in the deciphered stream. This makes it hard
for any change at all to go unnoticed.
An additional option is to store Message Authentication Codes with each
filesystem block. This adds about 8 bytes of overhead per block and a
large performance penalty, but makes it possible detect any modification
within a block.
Also during filesystem creation, one can enable per-file initialization
vectors. This causes a header with a random initialization vector to be
maintained with each file. Each file then has its own 64 bit initialization
vector which is augmented by the block number - so that each block within a
file has a unique initialization vector. This makes it infeasible to copy a
whole block from one file to another.
Backward Compatibility:
At the top level of the raw (encrypted) storage for an EncFS filesystem is a
configuration file, created automatically by EncFS when a new filesystem is
made.
In Encfs versions 0.2 to 0.6, the file was called ".encfs3" - meaning
version 3 of the Encfs configuration file format (earlier versions 1 and 2
were prior to the encfs public release). EncFS 1.0.x used ".encfs4", and
the Encfs 1.1.x uses yet another format (".encfs5"). The encfsctl program
can be used to show information about a filesystem.
Encfs 1.1 can read and write to existing filesystems, but older versions
will not be able to mount a filesystem created by a newer version, as the
newer versions use algorithms and/or new options which were not previously
available.
Utility:
In addition to the "encfs" main program, a utility "encfsctl" has been
provided which can perform some operations on encfs filesystems. Encfsctl
can display information about the filesystem for the curious (the encryption
algorithm used, key length, block size), and more importantly it can also
change the user-supplied password used to encrypt the volume key.
Dependencies:
Encfs uses the OpenSSL toolkit (http://www.openssl.org) by default.
OpenSSL is not covered by the GPL, and some people are concerned about the
licenses being incompatible. Although I believe it should be clear that I
intended to allow linking encfs with OpenSSL, I will make it more explicit:
As a special exception to encfs's GPL license, the copyright holders give
permission to link the code or portions of this program with the OpenSSL
library, and distribute linked combinations including the two. This
exception should be construed as narrowly as possible to allow OpenSSL to be
used and distributed as part of encfs.

71
README-NLS Normal file
View File

@ -0,0 +1,71 @@
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
If your language is not included in this distribution, you may want
to check if translated text is already available online in Rosetta.
If not, consider translating some of the strings, which will then be
included in the next EncFS release.

7
TRANSLATORS Normal file
View File

@ -0,0 +1,7 @@
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/

3
acinclude.m4 Normal file
View File

@ -0,0 +1,3 @@
m4_include([acx_pthread.m4])

199
acx_pthread.m4 Normal file
View File

@ -0,0 +1,199 @@
dnl Available from the GNU Autoconf Macro Archive at:
dnl http://www.gnu.org/software/ac-archive/htmldoc/acx_pthread.html
dnl
AC_DEFUN([ACX_PTHREAD], [
AC_REQUIRE([AC_CANONICAL_HOST])
AC_LANG_SAVE
AC_LANG_C
acx_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, acx_pthread_ok=yes)
AC_MSG_RESULT($acx_pthread_ok)
if test x"$acx_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.
acx_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)
# 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 -pthread or
# -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:
acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags"
;;
esac
if test x"$acx_pthread_ok" = xno; then
for flag in $acx_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(acx_pthread_config, pthread-config, yes, no)
if test x"$acx_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_TRY_LINK([#include <pthread.h>],
[pthread_t th; pthread_join(th, 0);
pthread_attr_init(0); pthread_cleanup_push(0, 0);
pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
[acx_pthread_ok=yes])
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
AC_MSG_RESULT($acx_pthread_ok)
if test "x$acx_pthread_ok" = xyes; then
break;
fi
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
done
fi
# Various other checks:
if test "x$acx_pthread_ok" = xyes; then
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Detect AIX lossage: threads are created detached by default
# and the JOINABLE attribute has a nonstandard name (UNDETACHED).
AC_MSG_CHECKING([for joinable pthread attribute])
AC_TRY_LINK([#include <pthread.h>],
[int attr=PTHREAD_CREATE_JOINABLE;],
ok=PTHREAD_CREATE_JOINABLE, ok=unknown)
if test x"$ok" = xunknown; then
AC_TRY_LINK([#include <pthread.h>],
[int attr=PTHREAD_CREATE_UNDETACHED;],
ok=PTHREAD_CREATE_UNDETACHED, ok=unknown)
fi
if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then
AC_DEFINE(PTHREAD_CREATE_JOINABLE, $ok,
[Define to the necessary symbol if this constant
uses a non-standard name on your system.])
fi
AC_MSG_RESULT(${ok})
if test x"$ok" = xunknown; then
AC_MSG_WARN([we do not know how to create joinable pthreads])
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";;
*solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
esac
AC_MSG_RESULT(${flag})
if test "x$flag" != xno; then
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
fi
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
# More AIX lossage: must compile with cc_r
AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC})
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"$acx_pthread_ok" = xyes; then
ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
:
else
acx_pthread_ok=no
$2
fi
AC_LANG_RESTORE
])dnl ACX_PTHREAD

187
configure.ac Normal file
View File

@ -0,0 +1,187 @@
dnl Process this file with autoconf to produce a configure script.
AC_INIT(encfs/encfs.h) dnl a source file from your sub dir
AM_INIT_AUTOMAKE(encfs, 1.4.0) dnl searches for some needed programs
dnl without this order in this file, automake will be confused!
dnl
AM_CONFIG_HEADER(config.h)
dnl This ksh/zsh feature conflicts with `cd blah ; pwd`
unset CDPATH
AC_LANG_CPLUSPLUS
AC_PROG_CXX
RELEASE=1
AC_SUBST(RELEASE)
dnl almost the same like KDE_SET_PEFIX but the path is /usr/local
dnl
unset CDPATH
dnl make /usr/local the default for the installation
AC_PREFIX_DEFAULT(/usr/local)
AM_GNU_GETTEXT([external])
AM_GNU_GETTEXT_VERSION(0.14.1)
AM_MKINSTALLDIRS
dnl AC_LIB_LINKFLAGS([asprintf]) # use internal copy
dnl create only shared libtool-libraries
dnl These can be overridden by command line arguments
AC_ENABLE_SHARED(yes)
AC_ENABLE_STATIC(no)
AM_CONDITIONAL( BUILD_STATIC, test "x$enable_static" = "xyes" )
dnl only build either static or shared, not both..
if test "x$enable_static" = "xyes"; then
enable_shared=no
AC_DEFINE(BUILD_STATIC, [1], [Building static library])
fi
AC_PROG_LIBTOOL
ACX_PTHREAD
dnl Need to include any user specified flags in the tests below, as they might
dnl specify required include directories..
FUSEFLAGS="-D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=26"
CPPFLAGS="$CPPFLAGS $USER_INCLUDES $FUSEFLAGS -D__STDC_FORMAT_MACROS"
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $USER_INCLUDES"
LDFLAGS="$LDFLAGS $PTHREAD_LIBS $USER_LDFLAGS"
if test "$GXX" = "yes"; then
CXXFLAGS="-W -Wall -Wshadow -Wpointer-arith -Wwrite-strings $CXXFLAGS"
dnl CXXFLAGS="$CXXFLAGS -Wformat=2 -Wconversion"
fi
AC_CHECK_HEADER(fuse.h,,
[AC_MSG_ERROR([
Can't find fuse.h - add the search path to CPPFLAGS and
rerun configure, eg:
export CPPFLAGS=-I/usr/local/include ])])
AC_CHECK_LIB(fuse,fuse_new, [FUSE_LIBS="-lfuse"],
[AC_MSG_ERROR([
Can't find libfuse.a - add the search path to LDFLAGS
and rerun configure, eg:
export LDFLAGS=-L/usr/local/lib ])],)
AC_CHECK_HEADERS([ulockmgr.h])
AC_CHECK_LIB(ulockmgr,ulockmgr_op, [FUSE_LIBS="$FUSE_LIBS -lulockmgr"])
AC_SUBST(FUSE_LIBS)
# check for a supported FUSE_MAJOR_VERSION.
AC_MSG_CHECKING([For supported FUSE API version])
AC_RUN_IFELSE([
AC_LANG_PROGRAM([[#include "fuse.h"]],
[[
if(FUSE_MAJOR_VERSION == 2 && FUSE_MINOR_VERSION >= 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 Check for valgrind headers..
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",
[ # do old-style test instead..
AC_MSG_WARN([Checking for librlog the hard way])
AC_CHECK_LIB(rlog, RLogVersion, [RLOG_LIBS="-lrlog"],
[AC_MSG_ERROR([EncFS depends on librlog, by the same author.])]) ])
AC_CHECK_HEADER(boost/shared_ptr.hpp,,
[AC_MSG_ERROR([
Can't find boost/shared_.h - add the boost include dir to CPPFLAGS and
rerun configure, eg:
export CPPFLAGS=-I/usr/local/include ])])
# 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

32
dk2ChangeLog Normal file
View File

@ -0,0 +1,32 @@
#!/usr/bin/perl -w
use strict;
open(LOG, "darcs changes|") || die "Can't open 'dk changes': $!";
my $lastDate = "";
while(<LOG>)
{
if( /^(\w+)\s+(\w+)\s+(\d+)\s+([0-9:]+)\s+(\w+)\s+(\d+)\s+(.*)$/ )
{
my $date = "$1 $2 $3 $6";
if($date ne $lastDate)
{
$lastDate = $date;
print "$date $7\n";
}
} else
{
s/^\s+//;
if(/\w/)
{
print "\t$_";
} else
{
#print "\n";
}
}
}
close(LOG)

207
encfs.spec.in Normal file
View File

@ -0,0 +1,207 @@
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 <vgough at pobox dot com>
#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 <vgough@pobox.com>
- 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 <vgough@pobox.com>
- 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 <vgough@pobox.com>
- 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 <vgough@pobox.com>
- 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 <vgough@pobox.com>
- 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 <vgough@pobox.com>
- Release 1.1.10
- fix problems with recursive rename
- fix incorrect error codes from xattr functions
* Tue Aug 15 2004 Valient Gough <vgough@pobox.com>
- Release 1.1.9
- fix another rename bug (affected filesystems with 'paranoia' configuration)
* Mon Aug 14 2004 Valient Gough <vgough@pobox.com>
- Release 1.1.8
- Improve MAC block header processing.
* Sat Aug 12 2004 Valient Gough <vgough@pobox.com>
- Release 1.1.7
- fix bug in truncate() for unopened files.
* Mon Aug 9 2004 Valient Gough <vgough@pobox.com>
- 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 <vgough@pobox.com>
- Release 1.1.5
* Sat Jul 10 2004 Valient Gough <vgough@pobox.com>
- Release 1.1.4
- add external password prompt support.
* Thu Jun 24 2004 Valient Gough <vgough@pobox.com>
- Release 1.1.3
* Fri May 28 2004 Valient Gough <vgough@pobox.com>
- 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 <vgough@pobox.com>
- 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 <vgough@pobox.com>
- 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 <vgough@pobox.com>
- Release 1.0.5
- Allow truncate call to extend file (only shrinking was supported)
* Fri Mar 26 2004 Valient Gough <vgough@pobox.com>
- Release 1.0.4
- Large speed improvement.
- Add support for FUSE major version 2 API.
* Thu Mar 18 2004 Valient Gough <vgough@pobox.com>
- Release 1.0.3
- Fix bugs in truncation and padding code.
* Sat Mar 13 2004 Valient Gough <vgough@pobox.com>
- 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 <vgough@pobox.com>
- 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 <vgough@pobox.com>
- 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 <vgough@pobox.com>
- 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 <vgough@pobox.com>
- 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)

431
encfs/BlockFileIO.cpp Normal file
View File

@ -0,0 +1,431 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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 "BlockFileIO.h"
#include "MemoryPool.h"
#include <string.h>
#include <rlog/rlog.h>
#include "i18n.h"
template<typename Type>
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 dataSize )
: _blockSize( dataSize )
, _allowHoles( false )
{
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;
}
void BlockFileIO::allowHoles( bool allow )
{
_allowHoles = allow;
}
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) && !_allowHoles )
{
// 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
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;
}

69
encfs/BlockFileIO.h Normal file
View File

@ -0,0 +1,69 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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.
*/
#ifndef _BlockFileIO_incl_
#define _BlockFileIO_incl_
#include "FileIO.h"
/*
Implements block scatter / gather interface. Requires derived classes to
implement readOneBlock() / writeOneBlock() at a minimum.
When a partial block write is requested it will be turned into a read of
the existing block, merge with the write request, and a write of the full
block.
*/
class BlockFileIO : public FileIO
{
public:
BlockFileIO(int blockDataSize);
virtual ~BlockFileIO();
// implemented in terms of blocks.
virtual ssize_t read( const IORequest &req ) const;
virtual bool write( const IORequest &req );
virtual int blockSize() const;
protected:
// default is false, but setting this to true will allow holes to be stored
// in the file. Only works if supported by the underlying FileIO
// implementation..
void allowHoles( bool allow );
int truncate( 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
// block aligned, and the request size will not be larger then 1 block.
virtual ssize_t readOneBlock( const IORequest &req ) const =0;
virtual bool writeOneBlock( const IORequest &req ) =0;
ssize_t cacheReadOneBlock( const IORequest &req ) const;
bool cacheWriteOneBlock( const IORequest &req );
int _blockSize;
bool _allowHoles;
// cache last block for speed...
mutable IORequest _cache;
};
#endif

213
encfs/BlockNameIO.cpp Normal file
View File

@ -0,0 +1,213 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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 "BlockNameIO.h"
#include "Cipher.h"
#include "base64.h"
#include <string.h>
#include <rlog/rlog.h>
#include <rlog/Error.h>
#include <rlog/RLogChannel.h>
#include "i18n.h"
using namespace rlog;
using namespace rel;
using namespace boost;
static RLogChannel * Info = DEF_CHANNEL( "info/nameio", Log_Info );
static shared_ptr<NameIO> NewBlockNameIO( const Interface &iface,
const shared_ptr<Cipher> &cipher, const CipherKey &key )
{
int blockSize = 8;
if(cipher)
blockSize = cipher->cipherBlockSize();
return shared_ptr<NameIO>( new BlockNameIO( iface, cipher, key, blockSize));
}
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(),
NewBlockNameIO);
/*
- 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..
*/
Interface BlockNameIO::CurrentInterface()
{
// implement major version 3 and 2
return Interface("nameio/block", 3, 0, 1);
}
BlockNameIO::BlockNameIO( const rel::Interface &iface,
const shared_ptr<Cipher> &cipher,
const CipherKey &key, int blockSize )
: _interface( iface.current() )
, _bs( blockSize )
, _cipher( cipher )
, _key( key )
{
// just to be safe..
rAssert( blockSize < 128 );
}
BlockNameIO::~BlockNameIO()
{
}
Interface BlockNameIO::interface() const
{
return CurrentInterface();
}
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
return B256ToB64Bytes( encodedNameLen );
}
int BlockNameIO::maxDecodedNameLen( int encodedNameLen ) const
{
int decLen256 = 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 encLen64 = B256ToB64Bytes( encodedStreamLen );
changeBase2Inline( (unsigned char *)encodedName, encodedStreamLen,
8, 6, true );
B64ToAscii( (unsigned char *)encodedName, encLen64 );
return encLen64;
}
int BlockNameIO::decodeName( const char *encodedName, int length,
uint64_t *iv, char *plaintextName ) const
{
int decLen256 = 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,
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;
}

65
encfs/BlockNameIO.h Normal file
View File

@ -0,0 +1,65 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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.
*/
#ifndef _BlockNameIO_incl_
#define _BlockNameIO_incl_
#include "NameIO.h"
#include "CipherKey.h"
#include <boost/shared_ptr.hpp>
class Cipher;
/*
Implement NameIO interface for filename encoding. Uses cipher in block
mode to encode filenames. The filenames are padded to be a multiple of the
cipher block size.
*/
class BlockNameIO : public NameIO
{
public:
static rel::Interface CurrentInterface();
BlockNameIO( const rel::Interface &iface,
const boost::shared_ptr<Cipher> &cipher,
const CipherKey &key, int blockSize );
virtual ~BlockNameIO();
virtual rel::Interface interface() const;
virtual int maxEncodedNameLen( int plaintextNameLen ) const;
virtual int maxDecodedNameLen( int encodedNameLen ) const;
// hack to help with static builds
static bool Enabled();
protected:
virtual int encodeName( const char *plaintextName, int length,
uint64_t *iv, char *encodedName ) const;
virtual int decodeName( const char *encodedName, int length,
uint64_t *iv, char *plaintextName ) const;
private:
int _interface;
int _bs;
boost::shared_ptr<Cipher> _cipher;
CipherKey _key;
};
#endif

208
encfs/Cipher.cpp Normal file
View File

@ -0,0 +1,208 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2002-2004, 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 "config.h"
#include "Cipher.h"
#include "Interface.h"
#include "Range.h"
#include <map>
#include <list>
#include <string>
#include <iostream>
// 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 "NullCipher.h"
#include "SSL_Cipher.h"
using namespace std;
using namespace rel;
using boost::shared_ptr;
#define REF_MODULE(TYPE) \
if( !TYPE::Enabled() ) \
cerr << "referenceModule: should never happen\n";
static
void AddSymbolReferences()
{
REF_MODULE(SSL_Cipher)
REF_MODULE(NullCipher)
}
struct CipherAlg
{
bool hidden;
Cipher::CipherConstructor constructor;
string description;
Interface iface;
Range keyLength;
Range blockSize;
};
typedef multimap< string, CipherAlg> CipherMap_t;
static CipherMap_t *gCipherMap = NULL;
std::list<Cipher::CipherAlgorithm>
Cipher::GetAlgorithmList( bool includeHidden )
{
AddSymbolReferences();
list<CipherAlgorithm> result;
if(!gCipherMap)
return result;
CipherMap_t::const_iterator it;
CipherMap_t::const_iterator mapEnd = gCipherMap->end();
for(it = gCipherMap->begin(); it != mapEnd; ++it)
{
if(includeHidden || !it->second.hidden)
{
CipherAlgorithm tmp;
tmp.name = it->first;
tmp.description = it->second.description;
tmp.iface = it->second.iface;
tmp.keyLength = it->second.keyLength;
tmp.blockSize = it->second.blockSize;
result.push_back( tmp );
}
}
return result;
}
bool Cipher::Register(const char *name, const char *description,
const Interface &iface, CipherConstructor fn, bool hidden)
{
Range keyLength(-1,-1,1);
Range blockSize(-1,-1,1);
return Cipher::Register( name, description, iface,
keyLength, blockSize, fn, hidden );
}
bool Cipher::Register(const char *name, const char *description,
const Interface &iface, const Range &keyLength,
const Range &blockSize,
CipherConstructor fn, bool hidden)
{
if(!gCipherMap)
gCipherMap = new CipherMap_t;
CipherAlg ca;
ca.hidden = hidden;
ca.constructor = fn;
ca.description = description;
ca.iface = iface;
ca.keyLength = keyLength;
ca.blockSize = blockSize;
gCipherMap->insert( make_pair(string(name), ca) );
return true;
}
shared_ptr<Cipher> Cipher::New(const string &name, int keyLen)
{
shared_ptr<Cipher> result;
if(gCipherMap)
{
CipherMap_t::const_iterator it = gCipherMap->find( name );
if(it != gCipherMap->end())
{
CipherConstructor fn = it->second.constructor;
// use current interface..
result = (*fn)( it->second.iface, keyLen );
}
}
return result;
}
shared_ptr<Cipher> Cipher::New( const Interface &iface, int keyLen )
{
shared_ptr<Cipher> result;
if(gCipherMap)
{
CipherMap_t::const_iterator it;
CipherMap_t::const_iterator mapEnd = gCipherMap->end();
for(it = gCipherMap->begin(); it != mapEnd; ++it)
{
// TODO: we should look for the newest implementation..
if( it->second.iface.implements( iface ) )
{
CipherConstructor fn = it->second.constructor;
// pass in requested interface..
result = (*fn)( iface, keyLen );
// if we're not going to compare the options, then just stop
// now..
break;
}
}
}
return result;
}
Cipher::Cipher()
{
}
Cipher::~Cipher()
{
}
unsigned int Cipher::MAC_32( const unsigned char *src, int len,
const CipherKey &key, uint64_t *chainedIV ) const
{
uint64_t mac64 = MAC_64( src, len, key, chainedIV );
unsigned int mac32 = ((mac64 >> 32) & 0xffffffff) ^ (mac64 & 0xffffffff);
return mac32;
}
unsigned int Cipher::MAC_16( const unsigned char *src, int len,
const CipherKey &key, uint64_t *chainedIV ) const
{
uint64_t mac64 = MAC_64( src, len, key, chainedIV );
unsigned int mac32 = ((mac64 >> 32) & 0xffffffff) ^ (mac64 & 0xffffffff);
unsigned int mac16 = ((mac32 >> 16) & 0xffff) ^ (mac32 & 0xffff);
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 );
}

151
encfs/Cipher.h Normal file
View File

@ -0,0 +1,151 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2002-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.
*
*/
#ifndef _Cipher_incl_
#define _Cipher_incl_
#include "encfs.h"
#include "Range.h"
#include "Interface.h"
#include "CipherKey.h"
#include <string>
#include <list>
#include <inttypes.h>
using boost::shared_ptr;
/*
Mostly pure virtual interface defining operations on a cipher.
Cipher's should register themselves so they can be instanciated via
Cipher::New().
*/
class Cipher
{
public:
// if no key length was indicated when cipher was registered, then keyLen
// <= 0 will be used.
typedef boost::shared_ptr<Cipher> (*CipherConstructor)( const rel::Interface &iface,
int keyLenBits );
struct CipherAlgorithm
{
std::string name;
std::string description;
rel::Interface iface;
Range keyLength;
Range blockSize;
};
typedef std::list<CipherAlgorithm> AlgorithmList;
static AlgorithmList GetAlgorithmList( bool includeHidden = false );
static boost::shared_ptr<Cipher> New( const rel::Interface &iface,
int keyLen = -1);
static boost::shared_ptr<Cipher> New( const std::string &cipherName,
int keyLen = -1 );
static bool Register(const char *cipherName,
const char *description,
const rel::Interface &iface,
CipherConstructor constructor,
bool hidden = false);
static bool Register(const char *cipherName,
const char *description,
const rel::Interface &iface,
const Range &keyLength, const Range &blockSize,
CipherConstructor constructor,
bool hidden = false);
Cipher();
virtual ~Cipher();
virtual rel::Interface interface() const =0;
// create a new key based on a password
virtual CipherKey newKey(const char *password, int passwdLength) =0;
// create a new random key
virtual CipherKey newRandomKey() =0;
// data must be len encodedKeySize()
virtual CipherKey readKey(const unsigned char *data,
const CipherKey &encodingKey,
bool checkKey = true) =0;
virtual void writeKey(const CipherKey &key, unsigned char *data,
const CipherKey &encodingKey) =0;
// for testing purposes
virtual bool compareKey( const CipherKey &A, const CipherKey &B ) const =0;
// meta-data about the cypher
virtual int keySize() const=0;
virtual int encodedKeySize() const=0; // size
virtual int cipherBlockSize() const=0; // size of a cipher block
// 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.
virtual void randomize( unsigned char *buf, int len ) const =0;
// 64 bit MAC of the data with the given key
virtual uint64_t MAC_64( const unsigned char *src, int len,
const CipherKey &key, uint64_t *chainedIV = 0 ) const =0;
// based on reductions of MAC_64
unsigned int MAC_32( const unsigned char *src, int len,
const CipherKey &key, uint64_t *chainedIV = 0 ) const;
unsigned int MAC_16( const unsigned char *src, int len,
const CipherKey &key, uint64_t *chainedIV = 0 ) const;
// functional interfaces
/*
Stream encoding of data in-place. The stream data can be any length.
*/
virtual bool streamEncode( unsigned char *data, int len,
uint64_t iv64, const CipherKey &key) const=0;
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.
*/
virtual bool blockEncode(unsigned char *buf, int size,
uint64_t iv64, const CipherKey &key) const=0;
virtual bool blockDecode(unsigned char *buf, int size,
uint64_t iv64, const CipherKey &key) const=0;
};
#endif

424
encfs/CipherFileIO.cpp Normal file
View File

@ -0,0 +1,424 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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 "CipherFileIO.h"
#include "Cipher.h"
#include "MemoryPool.h"
#include <rlog/rlog.h>
#include <fcntl.h>
#include <errno.h>
using boost::shared_ptr;
/*
- 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 rel::Interface CipherFileIO_iface("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<FileIO> &_base,
const shared_ptr<Cipher> &_cipher,
const CipherKey &_key, int fsBlockSize,
bool uniqueIV, bool _reverseEncryption )
: BlockFileIO( fsBlockSize )
, base( _base )
, cipher( _cipher )
, key( _key )
, haveHeader( uniqueIV )
, externalIV( 0 )
, fileIV( 0 )
, lastFlags( 0 )
, reverseEncryption( _reverseEncryption )
{
static bool warnOnce = false;
if(!warnOnce)
warnOnce = checkSize( fsBlockSize, cipher->cipherBlockSize() );
}
CipherFileIO::~CipherFileIO()
{
}
rel::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
{
cipher->randomize( buf, 8 );
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;
if(haveHeader)
{
IORequest tmpReq = req;
tmpReq.offset += HEADER_SIZE;
readSize = base->read( tmpReq );
} else
readSize = base->read( req );
bool ok;
if(readSize > 0)
{
if(haveHeader && fileIV == 0)
const_cast<CipherFileIO*>(this)->initHeader();
if(readSize != bs)
{
ok = streamRead( req.data, (int)readSize, blockNum ^ fileIV);
} else
{
ok = blockRead( req.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 (!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 (!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 (reverseEncryption)
return cipher->blockEncode( buf, size, _iv64, key );
else
return cipher->blockDecode( buf, size, _iv64, key );
}
bool CipherFileIO::streamRead( unsigned char *buf, int size,
uint64_t _iv64 ) const
{
if (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();
}

85
encfs/CipherFileIO.h Normal file
View File

@ -0,0 +1,85 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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.
*/
#ifndef _CipherFileIO_incl_
#define _CipherFileIO_incl_
#include "BlockFileIO.h"
#include "CipherKey.h"
#include <inttypes.h>
class Cipher;
/*
Implement the FileIO interface encrypting data in blocks.
Uses BlockFileIO to handle the block scatter / gather issues.
*/
class CipherFileIO : public BlockFileIO
{
public:
CipherFileIO( const boost::shared_ptr<FileIO> &base,
const boost::shared_ptr<Cipher> &cipher,
const CipherKey &key, int blockSize,
bool uniqueIV, bool reverseEncryption );
virtual ~CipherFileIO();
virtual rel::Interface interface() const;
virtual void setFileName( const char *fileName );
virtual const char *getFileName() const;
virtual bool setIV( uint64_t iv );
virtual int open( int flags );
virtual int getAttr( struct stat *stbuf ) const;
virtual off_t getSize() const;
virtual int truncate( off_t size );
virtual bool isWritable() const;
private:
virtual ssize_t readOneBlock( const IORequest &req ) const;
virtual bool writeOneBlock( const IORequest &req );
void initHeader();
bool writeHeader();
bool blockRead( unsigned char *buf, int size,
uint64_t iv64 ) const;
bool streamRead( unsigned char *buf, int size,
uint64_t iv64 ) const;
bool blockWrite( unsigned char *buf, int size,
uint64_t iv64 ) const;
bool streamWrite( unsigned char *buf, int size,
uint64_t iv64 ) const;
boost::shared_ptr<FileIO> base;
boost::shared_ptr<Cipher> cipher;
CipherKey key;
// if haveHeader is true, then we have a transparent file header which
// contains a 64 bit initialization vector.
bool haveHeader;
bool externalIVChaining;
uint64_t externalIV;
uint64_t fileIV;
int lastFlags;
bool reverseEncryption;
};
#endif

30
encfs/CipherKey.cpp Normal file
View File

@ -0,0 +1,30 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2007, Valient Gough
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "CipherKey.h"
AbstractCipherKey::AbstractCipherKey()
{
}
AbstractCipherKey::~AbstractCipherKey()
{
}

36
encfs/CipherKey.h Normal file
View File

@ -0,0 +1,36 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2007, Valient Gough
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _CipherKey_incl_
#define _CipherKey_incl_
#include <boost/shared_ptr.hpp>
class AbstractCipherKey
{
public:
AbstractCipherKey();
virtual ~AbstractCipherKey();
};
typedef boost::shared_ptr<AbstractCipherKey> CipherKey;
#endif

159
encfs/ConfigReader.cpp Normal file
View File

@ -0,0 +1,159 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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 "ConfigReader.h"
#include <rlog/rlog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
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<numEntries; ++i)
{
string key, value;
in >> 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<string, ConfigVar>::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<string, ConfigVar>::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 ];
}

63
encfs/ConfigReader.h Normal file
View File

@ -0,0 +1,63 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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.
*/
#ifndef _ConfigReader_incl_
#define _ConfigReader_incl_
#include <string>
#include <map>
#include "ConfigVar.h"
/*
handles Configuration load / store for Encfs filesystems.
loading existing config file example:
ConfigReader cfg;
cfg.load( filesystemConfigFile );
Interface iface;
cfg["cipher"] >> iface;
creating new config example:
ConfigReader cfg;
cfg["cipher"] << cipher->interface();
*/
class ConfigReader
{
public:
ConfigReader();
~ConfigReader();
bool load(const char *fileName);
bool save(const char *fileName) const;
ConfigVar toVar() const;
bool loadFromVar( ConfigVar &var );
ConfigVar operator[](const std::string &varName) const;
ConfigVar &operator[](const std::string &varName);
private:
std::map<std::string, ConfigVar> vars;
};
#endif

247
encfs/ConfigVar.cpp Normal file
View File

@ -0,0 +1,247 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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 "ConfigVar.h"
#include <rlog/rlog.h>
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;
}

80
encfs/ConfigVar.h Normal file
View File

@ -0,0 +1,80 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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.
*/
#ifndef _ConfigVar_incl_
#define _ConfigVar_incl_
#include <string>
#include <boost/shared_ptr.hpp>
using boost::shared_ptr;
class ConfigVar
{
struct ConfigVarData
{
std::string buffer;
int offset;
};
shared_ptr<ConfigVarData> pd;
public:
ConfigVar();
ConfigVar(const std::string &buffer);
ConfigVar(const ConfigVar &src);
~ConfigVar();
ConfigVar & operator = (const ConfigVar &src);
// reset read/write offset..
void resetOffset();
// read bytes
int read(unsigned char *buffer, int size) const;
// write bytes..
int write(const unsigned char *data, int size);
int readInt() const;
int readInt( int defaultValue ) const;
void writeInt(int value);
bool readBool( bool defaultValue ) const;
void writeString(const char *data, int size);
// return amount of data in var
int size() const;
// return data pointer - returns front of data pointer, not the current
// position.
const char *buffer() const;
// return current position in data() buffer.
int at() const;
};
ConfigVar & operator << (ConfigVar &, bool);
ConfigVar & operator << (ConfigVar &, int);
ConfigVar & operator << (ConfigVar &, const std::string &str);
const ConfigVar & operator >> (const ConfigVar &, bool &);
const ConfigVar & operator >> (const ConfigVar &, int &);
const ConfigVar & operator >> (const ConfigVar &, std::string &str);
#endif

182
encfs/Context.cpp Normal file
View File

@ -0,0 +1,182 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2007, Valient Gough
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "FileNode.h"
#include "Context.h"
#include "Mutex.h"
#include "FileUtils.h"
#include <rlog/rlog.h>
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<DirNode> EncFS_Context::getRoot(int *errCode)
{
shared_ptr<DirNode> 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<DirNode> &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<FileNode> EncFS_Context::lookupNode(const char *path)
{
Lock lock( contextMutex );
FileMap::iterator it = openFiles.find( std::string(path) );
if(it != openFiles.end())
{
rInfo("found existing node for %s", path);
// all the items in the set point to the same node.. so just use the
// first
return (*it->second.begin())->node;
} else
{
rInfo("no node found for %s", path);
return shared_ptr<FileNode>();
}
}
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<Placeholder *> val = it->second;
openFiles.erase(it);
openFiles[ std::string(to) ] = val;
}
}
shared_ptr<FileNode> EncFS_Context::getNode(void *pl)
{
Placeholder *ph = (Placeholder*)pl;
return ph->node;
}
void *EncFS_Context::putNode(const char *path,
const boost::shared_ptr<FileNode> &node)
{
Lock lock( contextMutex );
Placeholder *pl = new Placeholder( node );
openFiles[ std::string(path) ].insert(pl);
rInfo("added open node record for %s", path);
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);
rInfo("released open node record for %s", path);
// if no more references to this file, remove the record all together
if(it->second.empty())
{
rInfo("last open node closed for %s", path);
// 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;
}

106
encfs/Context.h Normal file
View File

@ -0,0 +1,106 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 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.
*/
#ifndef _Context_incl_
#define _Context_incl_
#include "encfs.h"
#include <boost/shared_ptr.hpp>
#include <set>
#ifdef USE_HASHMAP
#include <ext/hash_map>
#else
#include <map>
#endif
using boost::shared_ptr;
struct EncFS_Args;
struct EncFS_Opts;
class EncFS_Context
{
public:
EncFS_Context();
~EncFS_Context();
shared_ptr<FileNode> getNode(void *ptr);
shared_ptr<FileNode> lookupNode(const char *path);
int getAndResetUsageCounter();
int openFileCount() const;
void *putNode(const char *path, const shared_ptr<FileNode> &node);
void eraseNode(const char *path, void *placeholder);
void renameNode(const char *oldName, const char *newName);
void setRoot(const shared_ptr<DirNode> &root);
shared_ptr<DirNode> getRoot(int *err);
bool isMounted();
shared_ptr<EncFS_Args> args;
shared_ptr<EncFS_Opts> opts;
bool publicFilesystem;
// root path to cipher dir
std::string rootCipherDir;
// for idle monitor
bool running;
pthread_t monitorThread;
pthread_cond_t wakeupCond;
pthread_mutex_t wakeupMutex;
private:
/* This placeholder is what is referenced in FUSE context (passed to
* callbacks).
*
* A FileNode may be opened many times, but only one FileNode instance per
* file is kept. Rather then doing reference counting in FileNode, we
* store a unique Placeholder for each open() until the corresponding
* release() is called. shared_ptr then does our reference counting for
* us.
*/
struct Placeholder
{
shared_ptr<FileNode> node;
Placeholder( const shared_ptr<FileNode> &ptr ) : node(ptr) {}
};
// set of open files, indexed by path
#ifdef USE_HASHMAP
typedef __gnu_cxx::hash_map<std::string,
std::set<Placeholder*> > FileMap;
#else
typedef std::map< std::string,
std::set<Placeholder*> > FileMap;
#endif
mutable pthread_mutex_t contextMutex;
FileMap openFiles;
int usageCount;
shared_ptr<DirNode> root;
};
int remountFS( EncFS_Context *ctx );
#endif

808
encfs/DirNode.cpp Normal file
View File

@ -0,0 +1,808 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2003-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 "DirNode.h"
#include "FileUtils.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#ifdef linux
#include <sys/fsuid.h>
#endif
#include "Context.h"
#include "Cipher.h"
#include "Mutex.h"
#include <rlog/rlog.h>
#include <rlog/Error.h>
#include <iostream>
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<DIR> &_dirPtr,
uint64_t _iv, const shared_ptr<NameIO> &_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> &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<RenameEl> > renameList;
list<RenameEl>::const_iterator last;
public:
RenameOp( DirNode *_dn, const shared_ptr< list<RenameEl> > &_renameList )
: dn(_dn), renameList(_renameList)
{
last = renameList->begin();
}
RenameOp(const RenameOp &src)
: dn(src.dn)
, renameList(src.renameList)
, last(src.last)
{
}
~RenameOp()
{
if(renameList)
{
// got a bunch of decoded filenames sitting in memory.. do a little
// cleanup before leaving..
list<RenameEl>::iterator it;
for(it = renameList->begin(); it != renameList->end(); ++it)
{
it->oldPName.assign( it->oldPName.size(), ' ' );
it->newPName.assign( it->newPName.size(), ' ' );
}
}
}
void reset()
{
renameList.reset();
}
operator bool () const
{
return renameList;
}
bool apply()
{
try
{
while(last != renameList->end())
{
// backing store rename.
rDebug("renaming %s -> %s",
last->oldCName.c_str(), last->newCName.c_str());
// 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;
}
++last;
}
return true;
} catch( rlog::Error &err )
{
err.log( _RLWarningChannel );
return false;
}
}
void 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<RenameEl>::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 shared_ptr<Config> &_config)
{
pthread_mutex_init( &mutex, 0 );
Lock _lock( mutex );
ctx = _ctx;
rootDir = sourceDir;
config = _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 = config->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::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() );
shared_ptr<DIR> dp( ::opendir( cyName.c_str() ), DirDeleter() );
if(!dp)
{
rDebug("opendir error %s", strerror(errno));
return DirTraverse( dp, 0, shared_ptr<NameIO>() );
} else
{
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<RenameEl> &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> dir = shared_ptr<DIR>(
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<RenameOp>
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<RenameEl> > renameList(new list<RenameEl>);
shared_ptr<RenameOp> op( new RenameOp(this, renameList) );
if(!genRenameList( *renameList.get(), fromP, toP ))
{
rWarning("Error during generation of recursive rename list");
op.reset();
}
return op;
}
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<FileNode> toNode = findOrCreate( toPlaintext );
shared_ptr<RenameOp> 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
{
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();
}
} 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( config->externalIVChaining )
{
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<FileNode> DirNode::renameNode( const char *from, const char *to )
{
return renameNode( from, to, true );
}
shared_ptr<FileNode> DirNode::renameNode( const char *from, const char *to,
bool forwardMode )
{
shared_ptr<FileNode> 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<FileNode> DirNode::findOrCreate( const char *plainName)
{
shared_ptr<FileNode> node;
if(ctx)
node = ctx->lookupNode( plainName );
if(!node)
{
uint64_t iv = 0;
string cipherName = naming->encodePath( plainName, &iv );
node.reset( new FileNode( this,
config->fsSubVersion,
plainName,
(rootDir + cipherName).c_str(),
config->cipher, config->key,
config->blockSize, config->blockMACBytes,
config->blockMACRandBytes,
config->uniqueIV,
config->externalIVChaining,
config->forceDecode,
config->reverseEncryption) );
if(config->externalIVChaining)
node->setName(0, 0, iv);
rLog(Info, "created FileNode for %s", node->cipherName());
}
return node;
}
shared_ptr<FileNode>
DirNode::lookupNode( const char *plainName, const char * requestor )
{
(void)requestor;
Lock _lock( mutex );
shared_ptr<FileNode> 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<FileNode>
DirNode::openNode( const char *plainName, const char * requestor, int flags,
int *result )
{
(void)requestor;
rAssert( result != NULL );
Lock _lock( mutex );
shared_ptr<FileNode> node = findOrCreate( plainName );
if( node && (*result = node->open( flags )) >= 0 )
return node;
else
return shared_ptr<FileNode>();
}
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;
}

210
encfs/DirNode.h Normal file
View File

@ -0,0 +1,210 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2003, 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.
*/
#ifndef _DirNode_incl_
#define _DirNode_incl_
#include <inttypes.h>
#include <dirent.h>
#include <sys/types.h>
#include <map>
#include <list>
#include <vector>
#include <string>
#include "FileNode.h"
#include "NameIO.h"
#include "CipherKey.h"
using boost::shared_ptr;
class Cipher;
class RenameOp;
struct RenameEl;
class EncFS_Context;
class DirTraverse
{
public:
DirTraverse(const shared_ptr<DIR> &dirPtr, uint64_t iv,
const shared_ptr<NameIO> &naming);
DirTraverse(const DirTraverse &src);
~DirTraverse();
DirTraverse &operator = (const DirTraverse &src);
// returns FALSE to indicate an invalid DirTraverse (such as when
// an invalid directory is requested for traversal)
bool valid() const;
// return next plaintext filename
// If fileType is not 0, then it is used to return the filetype (or 0 if
// unknown)
std::string nextPlaintextName(int *fileType=0, ino_t *inode=0);
/* Return cipher name of next undecodable filename..
The opposite of nextPlaintextName(), as that skips undecodable names..
*/
std::string nextInvalid();
private:
shared_ptr<DIR> dir; // struct DIR
// initialization vector to use. Not very general purpose, but makes it
// more efficient to support filename IV chaining..
uint64_t iv;
shared_ptr<NameIO> naming;
};
inline bool DirTraverse::valid() const { return dir != 0; }
#ifdef USE_HASHMAP
namespace __gnu_cxx
{
template<> struct hash<std::string>
{
size_t operator() (const std::string &__s) const
{
return __stl_hash_string( __s.c_str() );
}
};
}
#endif
class DirNode
{
public:
struct Config
{
shared_ptr<Cipher> cipher; // cipher to use
CipherKey key; // cipher key to use
shared_ptr<NameIO> nameCoding; // filename encoding implementation
int fsSubVersion; // filesystem version number at creation
int blockSize; // file data block size
bool inactivityTimer; // enables inactivity tracking
int blockMACBytes; // >0 enables per-file-block MAC headers
int blockMACRandBytes; // random bytes in MAC headers
bool uniqueIV; // enable per-file initialization vectors
bool externalIVChaining;
bool forceDecode; // force decoding, even if errors are detected
bool reverseEncryption;
Config()
: fsSubVersion(0)
, blockSize(1)
, inactivityTimer( false )
, blockMACBytes( 0 )
, blockMACRandBytes( 0 )
, uniqueIV( false )
, externalIVChaining( false )
, forceDecode( false )
, reverseEncryption ( false )
{ }
};
// sourceDir points to where raw files are stored
DirNode( EncFS_Context *ctx,
const std::string &sourceDir, const shared_ptr<Config> &config );
~DirNode();
// return the path to the root directory
std::string rootDirectory();
// find files
shared_ptr<FileNode> lookupNode( const char *plaintextName,
const char *requestor );
/*
Combined lookupNode + node->open() call. If the open fails, then the
node is not retained. If the open succeeds, then the node is returned.
*/
shared_ptr<FileNode> openNode( const char *plaintextName,
const char *requestor, int flags, int *openResult );
std::string cipherPath( const char *plaintextPath );
std::string plainPath( const char *cipherPath );
// relative cipherPath is the same as cipherPath except that it doesn't
// prepent the mount point. That it, it doesn't return a fully qualified
// name, just a relative path within the encrypted filesystem.
std::string relativeCipherPath( const char *plaintextPath );
/*
Returns true if file names are dependent on the parent directory name.
If a directory name is changed, then all the filenames must also be
changed.
*/
bool hasDirectoryNameDependency() const;
// unlink the specified file
int unlink( const char *plaintextName );
// traverse directory
DirTraverse openDir( const char *plainDirName );
// uid and gid are used as the directory owner, only if not zero
int mkdir( const char *plaintextPath, mode_t mode,
uid_t uid = 0, gid_t gid = 0);
int rename( const char *fromPlaintext, const char *toPlaintext );
int link( const char *from, const char *to );
// returns idle time of filesystem in seconds
int idleSeconds();
protected:
/*
notify that a file is being renamed.
This renames the internal node, if any. If the file is not open, then
this call has no effect.
Returns the FileNode if it was found.
*/
shared_ptr<FileNode> renameNode( const char *from, const char *to );
shared_ptr<FileNode> renameNode( const char *from, const char *to,
bool forwardMode );
/*
when directory IV chaining is enabled, a directory can't be renamed
without renaming all its contents as well. recursiveRename should be
called after renaming the directory, passing in the plaintext from and
to paths.
*/
shared_ptr<RenameOp> newRenameOp( const char *from, const char *to );
private:
friend class RenameOp;
bool genRenameList( std::list<RenameEl> &list, const char *fromP,
const char *toP );
shared_ptr<FileNode> findOrCreate( const char *plainName);
pthread_mutex_t mutex;
EncFS_Context *ctx;
// passed in as configuration
std::string rootDir;
shared_ptr<Config> config;
// stored here to reduce access through config var..
shared_ptr<NameIO> naming;
};
#endif

38
encfs/FileIO.cpp Normal file
View File

@ -0,0 +1,38 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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 "FileIO.h"
FileIO::FileIO()
{
}
FileIO::~FileIO()
{
}
int FileIO::blockSize() const
{
return 1;
}
bool FileIO::setIV( uint64_t iv )
{
(void)iv;
return true;
}

84
encfs/FileIO.h Normal file
View File

@ -0,0 +1,84 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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.
*/
#ifndef _FileIO_incl_
#define _FileIO_incl_
#include "encfs.h"
#include <inttypes.h>
#include "Interface.h"
struct IORequest
{
off_t offset;
// amount of bytes to read/write.
int dataLen;
unsigned char *data;
IORequest();
};
inline IORequest::IORequest()
: offset(0)
, dataLen(0)
, data(0)
{
}
class FileIO
{
public:
FileIO();
virtual ~FileIO();
virtual rel::Interface interface() const =0;
// default implementation returns 1, meaning this is not block oriented.
virtual int blockSize() const;
virtual void setFileName(const char *fileName) =0;
virtual const char *getFileName() const =0;
// Not sure about this -- it is specific to CipherFileIO, but the
// alternative methods of exposing this interface aren't much nicer..
virtual bool setIV( uint64_t iv );
// open file for specified mode. There is no corresponding close, so a
// file is open until the FileIO interface is destroyed.
virtual int open( int flags ) =0;
// get filesystem attributes for a file
virtual int getAttr( struct stat *stbuf ) const =0;
virtual off_t getSize( ) const =0;
virtual ssize_t read( const IORequest &req ) const =0;
virtual bool write( const IORequest &req ) =0;
virtual int truncate( off_t size ) =0;
virtual bool isWritable() const =0;
private:
// not implemented..
FileIO( const FileIO & );
FileIO &operator = ( const FileIO & );
};
#endif

296
encfs/FileNode.cpp Normal file
View File

@ -0,0 +1,296 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2003-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 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 <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#ifdef linux
#include <sys/fsuid.h>
#endif
#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 <rlog/rlog.h>
#include <rlog/Error.h>
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_,
int fsSubVersion,
const char *plaintextName_, const char *cipherName_,
const shared_ptr<Cipher> &dataCipher, const CipherKey &key,
int blockSize, int blockMACBytes, int blockMACRandBytes, bool uniqueIV,
bool externalIVChaining_, bool forceDecode, bool reverseEncryption_ )
{
pthread_mutex_init( &mutex, 0 );
Lock _lock( mutex );
this->_pname = plaintextName_;
this->_cname = cipherName_;
this->parent = parent_;
this->externalIVChaining = externalIVChaining_;
this->reverseEncryption = reverseEncryption_;
// chain RawFileIO & CipherFileIO
shared_ptr<FileIO> rawIO( new RawFileIO( _cname ) );
io = shared_ptr<FileIO>(
new CipherFileIO( rawIO, dataCipher, key, blockSize,
uniqueIV, reverseEncryption ));
if(blockMACBytes)
{
io = shared_ptr<FileIO>(new MACFileIO(io, dataCipher, key,
blockSize,blockMACBytes,blockMACRandBytes,forceDecode));
}
}
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<FileIO> &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(externalIVChaining && !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(externalIVChaining && !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(gid != 0)
oldgid = setfsgid( gid );
/*
* 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;
}

104
encfs/FileNode.h Normal file
View File

@ -0,0 +1,104 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2003, 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.
*/
#ifndef _FileNode_incl_
#define _FileNode_incl_
#include "encfs.h"
#include "CipherKey.h"
#include <inttypes.h>
#include <sys/types.h>
#include <string>
class Cipher;
class FileIO;
class DirNode;
using boost::shared_ptr;
class FileNode
{
public:
FileNode(DirNode *parent,
int fsSubVersion, // version number for the filesystem
const char *plaintextName,
const char *cipherName,
const shared_ptr<Cipher> &cipher, const CipherKey &key, int blockSize,
int blockMACBytes, // per-block random bytes in header
int blockMACRandBytes, // per-block random bytes in header
bool uniqueIV, // enable per-file initialization vectors
bool externalIVChaining,
bool forceDecode, // decode, even if decoding errors are detected
bool reverseEncryption );
~FileNode();
const char *plaintextName() const;
const char *cipherName() const;
// directory portion of plaintextName
std::string plaintextParent() const;
// if setIVFirst is true, then the IV is changed before the name is changed
// (default). The reverse is also supported for special cases..
bool setName( const char *plaintextName, const char *cipherName,
uint64_t iv, bool setIVFirst = true);
// create node
// If uid/gid are not 0, then chown is used change ownership as specified
int mknod(mode_t mode, dev_t rdev, uid_t uid = 0, gid_t gid = 0);
// Returns < 0 on error (-errno), file descriptor on success.
int open(int flags) const;
// getAttr returns 0 on success, -errno on failure
int getAttr(struct stat *stbuf) const;
off_t getSize() const;
ssize_t read(off_t offset, unsigned char *data, ssize_t size) const;
bool write(off_t offset, unsigned char *data, ssize_t size);
// truncate the file to a particular size
int truncate( off_t size );
// datasync or full sync
int sync(bool dataSync);
private:
// doing locking at the FileNode level isn't as efficient as at the
// lowest level of RawFileIO, since that means locks are held longer
// (held during CPU intensive crypto operations!). However it makes it
// easier to avoid any race conditions with operations such as
// truncate() which may result in multiple calls down to the FileIO
// level.
mutable pthread_mutex_t mutex;
bool externalIVChaining;
bool reverseEncryption;
shared_ptr<FileIO> io;
std::string _pname; // plaintext name
std::string _cname; // encrypted name
DirNode *parent;
private:
FileNode(const FileNode &src);
FileNode &operator = (const FileNode &src);
};
#endif

1438
encfs/FileUtils.cpp Normal file

File diff suppressed because it is too large Load Diff

161
encfs/FileUtils.h Normal file
View File

@ -0,0 +1,161 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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.
*/
#ifndef _FileUtils_incl_
#define _FileUtils_incl_
#include "encfs.h"
#include "Interface.h"
#include "DirNode.h"
#include "CipherKey.h"
// true if the path points to an existing node (of any type)
bool fileExists( const char *fileName );
// true if path is a directory
bool isDirectory( const char *fileName );
// true if starts with '/'
bool isAbsolutePath( const char *fileName );
// pointer to just after the last '/'
const char *lastPathElement( const char *name );
std::string parentDirectory( const std::string &path );
// ask the user for permission to create the directory. If they say ok, then
// do it and return true.
bool userAllowMkdir( const char *dirPath, mode_t mode );
struct EncFSConfig
{
std::string creator;
int subVersion;
// interface of cipher
rel::Interface cipherIface;
// interface used for file name coding
rel::Interface nameIface;
int keySize; // reported in bits
int blockSize; // reported in bytes
std::string keyData;
int blockMACBytes; // MAC headers on blocks..
int blockMACRandBytes; // number of random bytes in the block header
bool uniqueIV; // per-file Initialization Vector
bool externalIVChaining; // IV seeding by filename IV chaining
bool chainedNameIV; // filename IV chaining
};
enum ConfigType
{
Config_None = 0,
Config_Prehistoric,
Config_V3,
Config_V4,
Config_V5
};
class Cipher;
struct EncFS_Root
{
boost::shared_ptr<Cipher> cipher;
CipherKey volumeKey;
boost::shared_ptr<DirNode> root;
EncFS_Root();
~EncFS_Root();
};
typedef boost::shared_ptr<EncFS_Root> RootPtr;
/*
Read existing config file. Looks for any supported configuration version.
*/
ConfigType readConfig( const std::string &rootDir, EncFSConfig *config );
/*
Save the configuration. Saves back as the same configuration type as was
read from.
*/
bool saveConfig( ConfigType type, const std::string &rootdir,
EncFSConfig *config );
struct EncFS_Opts
{
std::string rootDir;
bool createIfNotFound; // create filesystem if not found
bool idleTracking; // turn on idle monitoring of filesystem
bool mountOnDemand; // mounting on-demand
bool checkKey; // check crypto key decoding
bool forceDecode; // force decode on MAC block failures
std::string passwordProgram; // path to password program (or empty)
bool useStdin; // read password from stdin rather then prompting
bool ownerCreate; // set owner of new files to caller
bool reverseEncryption; // Reverse encryption
EncFS_Opts()
{
createIfNotFound = true;
idleTracking = false;
mountOnDemand = false;
checkKey = true;
forceDecode = false;
useStdin = false;
ownerCreate = false;
reverseEncryption = false;
}
};
class EncFS_Context;
RootPtr initFS( EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &opts );
RootPtr createV5Config( EncFS_Context *ctx, const std::string &rootDir,
bool enableIdleTracking,
bool forceDecode,
const std::string &passwordProgram, bool reverseEncryption );
void showFSInfo( const EncFSConfig &config );
// Read specifically a version 4 configuration file.
bool readV3Config( const char *configFile, EncFSConfig *config,
struct ConfigInfo *);
bool writeV3Config( const char *configFile, EncFSConfig *config);
bool readV4Config( const char *configFile, EncFSConfig *config,
struct ConfigInfo *);
bool writeV4Config( const char *configFile, EncFSConfig *config);
bool readV5Config( const char *configFile, EncFSConfig *config,
struct ConfigInfo *);
bool writeV5Config( const char *configFile, EncFSConfig *config);
CipherKey getUserKey(const boost::shared_ptr<Cipher> &cipher, bool useStdin);
CipherKey getUserKey(const std::string &passwordProgram,
const boost::shared_ptr<Cipher> &cipher,
const std::string &rootDir );
CipherKey getNewUserKey(const boost::shared_ptr<Cipher> &cipher);
#endif

226
encfs/Interface.cpp Normal file
View File

@ -0,0 +1,226 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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 "Interface.h"
#include "ConfigVar.h"
#include <rlog/rlog.h>
#include <rlog/RLogChannel.h>
using namespace rel;
using namespace rlog;
static RLogChannel * Info = DEF_CHANNEL( "info/iface", Log_Info );
Interface::Interface(const char *name_, int Current, int Revision, int Age)
: _name( name_ )
, _current( Current )
, _revision( Revision )
, _age( Age )
{
}
Interface::Interface(const std::string &name_, int Current,
int Revision, int Age)
: _name( name_ )
, _current( Current )
, _revision( Revision )
, _age( Age )
{
}
Interface::Interface(const Interface &src)
: _name( src._name )
, _current( src._current )
, _revision( src._revision )
, _age( src._age )
{
}
Interface::Interface()
: _current( 0 )
, _revision( 0 )
, _age( 0 )
{
}
Interface &Interface::operator = (const Interface &src)
{
_name = src._name;
_current = src._current;
_revision = src._revision;
_age = src._age;
return *this;
}
const std::string & Interface::name() const
{
return _name;
}
std::string & Interface::name()
{
return _name;
}
int Interface::current() const
{
return _current;
}
int &Interface::current()
{
return _current;
}
int Interface::revision() const
{
return _revision;
}
int &Interface::revision()
{
return _revision;
}
int Interface::age() const
{
return _age;
}
int &Interface::age()
{
return _age;
}
bool operator == (const Interface &A, const Interface &B)
{
return ( A.name() == B.name()
&& A.current() == B.current()
&& A.revision() == B.revision()
&& A.age() == B.age() );
}
bool operator != (const Interface &A, const Interface &B)
{
return ( A.name() != B.name()
|| A.current() != B.current()
|| A.revision() != B.revision()
|| A.age() != B.age() );
}
// zero branch method of getting comparison sign..
// tricky.. makes assumptions
#if 0
static int sign( int a, int b )
{
unsigned int ab = ((unsigned int)(a - b)) >> 31;
unsigned int ba = ((unsigned int)(b - a)) >> 31;
return 1 + ba - ab;
}
#else
// simple, easy to check, unlikely to break due to unforseen events..
static int sign( int a, int b )
{
if(a < b)
return 0;
else if(a == b)
return 1;
else
return 2;
}
#endif
static int diffSum( const Interface &A, const Interface &B )
{
int cS = sign( A.current() , B.current() );
int aS = sign( A.age(), B.age() );
int rS = sign( A.revision(), B.revision() );
return (cS * 3 + aS) * 3 + rS;
}
const int EqualVersion = (1 * 3 + 1) * 3 + 1;
bool Interface::implements(const Interface &B) const
{
rLog(Info, "checking if %s(%i:%i:%i) implements %s(%i:%i:%i)",
name().c_str(), current(), revision(), age(),
B.name().c_str(), B.current(), B.revision(), B.age());
if( name() != B.name() )
return false;
int currentDiff = current() - B.current();
return ( currentDiff >= 0 && currentDiff <= age() );
}
bool operator < (const Interface &A, const Interface &B)
{
if( A.name() == B.name() )
{
return ( diffSum(A,B) < EqualVersion );
} else
return A.name() < B.name();
}
bool operator > (const Interface &A, const Interface &B)
{
if( A.name() == B.name() )
{
return ( diffSum(A,B) > EqualVersion );
} else
return A.name() < B.name();
}
bool operator <= (const Interface &A, const Interface &B)
{
if( A.name() == B.name() )
{
return ( diffSum(A,B) <= EqualVersion );
} else
return A.name() < B.name();
}
bool operator >= (const Interface &A, const Interface &B)
{
if( A.name() == B.name() )
{
return ( diffSum(A,B) >= EqualVersion );
} else
return A.name() < B.name();
}
ConfigVar & operator << (ConfigVar &dst, const rel::Interface &iface)
{
dst << iface.name() << iface.current() << iface.revision() << iface.age();
return dst;
}
const ConfigVar & operator >> (const ConfigVar &src, Interface &iface)
{
src >> iface.name();
src >> iface.current();
src >> iface.revision();
src >> iface.age();
return src;
}

84
encfs/Interface.h Normal file
View File

@ -0,0 +1,84 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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.
*/
#ifndef _Interface_incl_
#define _Interface_incl_
#include <string>
class ConfigVar;
// part of REL library..
namespace rel
{
class Interface
{
public:
/*!
Version numbers as described by libtool: info://libtool/versioning
Current - the most recent interface api that is implemented.
Revision - the implementation number of the current interface.
Age - the difference between the newest and oldest interfaces that
are implemented.
*/
Interface( const char *name, int Current, int Revision, int Age );
Interface( const std::string &name, int Current, int Revision, int Age);
Interface(const Interface &src);
Interface();
// check if we implement the interface described by B.
// Note that A.implements(B) is not the same as B.implements(A)
// This checks the current() version and age() against B.current() for
// compatibility. Even if A.implements(B) is true, B > A may also be
// true, meaning B is a newer revision of the interface then A.
bool implements( const Interface &dst ) const;
const std::string &name() const;
int current() const;
int revision() const;
int age() const;
std::string &name();
int &current();
int &revision();
int &age();
Interface &operator = ( const Interface &src );
private:
std::string _name;
int _current;
int _revision;
int _age;
};
}
ConfigVar & operator << (ConfigVar &, const rel::Interface &);
const ConfigVar & operator >> (const ConfigVar &, rel::Interface &);
bool operator < (const rel::Interface &A, const rel::Interface &B);
bool operator > (const rel::Interface &A, const rel::Interface &B);
bool operator <= (const rel::Interface &A, const rel::Interface &B);
bool operator >= (const rel::Interface &A, const rel::Interface &B);
bool operator == (const rel::Interface &A, const rel::Interface &B);
bool operator != (const rel::Interface &A, const rel::Interface &B);
#endif

270
encfs/MACFileIO.cpp Normal file
View File

@ -0,0 +1,270 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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 "MACFileIO.h"
#include "MemoryPool.h"
#include <rlog/rlog.h>
#include <rlog/Error.h>
#include <rlog/RLogChannel.h>
#include <string.h>
#include "i18n.h"
using namespace rlog;
using namespace rel;
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!
//
// 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 rel::Interface MACFileIO_iface("FileIO/MAC", 2, 0, 0);
MACFileIO::MACFileIO( const shared_ptr<FileIO> &_base,
const shared_ptr<Cipher> &_cipher,
const CipherKey &_key, int fsBlockSize,
int _macBytes, int _randBytes,
bool warnOnlyMode )
: BlockFileIO( fsBlockSize - _macBytes - _randBytes )
, base( _base )
, cipher( _cipher )
, key( _key )
, macBytes( _macBytes )
, randBytes( _randBytes )
, warnOnly( warnOnlyMode )
{
rAssert( macBytes > 0 && macBytes <= 8 );
rAssert( randBytes >= 0 );
rLog(Info, "fs block size = %i, macBytes = %i, randBytes = %i",
fsBlockSize, macBytes, randBytes);
}
MACFileIO::~MACFileIO()
{
}
rel::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 );
if(readSize > headerSize)
{
// 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<macBytes; ++i, mac >>= 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)
cipher->randomize( newReq.data+macBytes, randBytes );
// 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<macBytes; ++i)
{
newReq.data[i] = mac & 0xff;
mac >>= 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();
}

69
encfs/MACFileIO.h Normal file
View File

@ -0,0 +1,69 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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.
*/
#ifndef _MACFileIO_incl_
#define _MACFileIO_incl_
#include "BlockFileIO.h"
#include "Cipher.h"
using boost::shared_ptr;
class MACFileIO : public BlockFileIO
{
public:
/*
If warnOnlyMode is enabled, then a MAC comparison failure will only
result in a warning message from encfs -- the garbled data will still
be made available..
*/
MACFileIO( const shared_ptr<FileIO> &base,
const shared_ptr<Cipher> &cipher,
const CipherKey &key, int blockSize,
int macBytes, int randBytes,
bool warnOnlyMode );
MACFileIO();
virtual ~MACFileIO();
virtual rel::Interface interface() const;
virtual void setFileName( const char *fileName );
virtual const char *getFileName() const;
virtual bool setIV( uint64_t iv );
virtual int open( int flags );
virtual int getAttr( struct stat *stbuf ) const;
virtual off_t getSize() const;
virtual int truncate( off_t size );
virtual bool isWritable() const;
private:
virtual ssize_t readOneBlock( const IORequest &req ) const;
virtual bool writeOneBlock( const IORequest &req );
shared_ptr<FileIO> base;
shared_ptr<Cipher> cipher;
CipherKey key;
int macBytes;
int randBytes;
bool warnOnly;
};
#endif

138
encfs/Makefile.am Normal file
View File

@ -0,0 +1,138 @@
include $(top_srcdir)/Makefile.common
ALL_INCLUDES = @RLOG_CFLAGS@ @OPENSSL_CFLAGS@
ALL_LDFLAGS = @RLOG_LIBS@ @OPENSSL_LIBS@ @FUSE_LIBS@
INCLUDES = $(all_includes) -I../intl
AM_CXXFLAGS = -DRLOG_COMPONENT="encfs" $(ALL_INCLUDES)
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
all-local: encfs-man.html
#encfs_LDADD = libencfs.la -lfuse
#encfsctl_LDADD = libencfs.la -lfuse
#test_LDADD = libencfs.la -lfuse
encfs_LDADD = libencfs.la $(ALL_LDFLAGS)
encfsctl_LDADD = libencfs.la $(ALL_LDFLAGS)
test_LDADD = libencfs.la $(ALL_LDFLAGS)
if BUILD_STATIC
encfs_LDFLAGS = -all-static
encfsctl_LDFLAGS = -all-static
test_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 3:0:0
libencfs_la_LIBADD = -lrlog
EXTRASRC = ../intl/autosprintf.cpp
if BUILD_OPENSSL
if BUILD_SSLCIPHER
EXTRASRC += SSL_Cipher.cpp
endif
endif
libencfs_la_SOURCES = \
readpassphrase.cpp \
base64.cpp \
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 \
${EXTRASRC}
encfs_SOURCES = \
encfs.cpp \
openssl.cpp \
main.cpp
test_SOURCES = \
test.cpp
encfsctl_SOURCES = \
encfsctl.cpp
noinst_HEADERS = \
base64.h \
BlockFileIO.h \
BlockNameIO.h \
CipherFileIO.h \
Cipher.h \
CipherKey.h \
ConfigReader.h \
ConfigVar.h \
Context.h \
DirNode.h \
encfs.h \
FileIO.h \
FileNode.h \
FileUtils.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
if BUILD_MAN
SUFFIXES = .1 .pod
# 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

151
encfs/MemoryPool.cpp Normal file
View File

@ -0,0 +1,151 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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 "MemoryPool.h"
#include <rlog/rlog.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include <pthread.h>
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#else
#define VALGRIND_MAKE_MEM_NOACCESS( a, b )
#define VALGRIND_MAKE_MEM_UNDEFINED( a, b )
#endif
using namespace rlog;
#if HAVE_SSL
# include <openssl/buffer.h>
#define BLOCKDATA( BLOCK ) (unsigned char*)BLOCK->data->data
#endif
struct BlockList
{
BlockList *next;
int size;
#ifdef HAVE_SSL
BUF_MEM *data;
#endif
};
static BlockList *allocBlock( int size )
{
BlockList *block = new BlockList;
block->size = size;
#ifdef HAVE_SSL
block->data = BUF_MEM_new( );
BUF_MEM_grow( block->data, size );
VALGRIND_MAKE_MEM_NOACCESS( block->data->data, block->data->max );
#endif
return block;
}
static void freeBlock( BlockList *el )
{
#ifdef HAVE_SSL
VALGRIND_MAKE_MEM_UNDEFINED( el->data->data, el->data->max );
BUF_MEM_free( el->data );
#endif
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;
}
}

54
encfs/MemoryPool.h Normal file
View File

@ -0,0 +1,54 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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 (LGPL), 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.
*
*/
#ifndef _MemoryPool_incl_
#define _MemoryPool_incl_
struct MemBlock
{
unsigned char *data;
void *internalData;
MemBlock();
};
inline MemBlock::MemBlock()
: data(0), internalData(0)
{
}
/*
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();
}
#endif

62
encfs/Mutex.h Normal file
View File

@ -0,0 +1,62 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2003, 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.
*/
#ifndef _Mutex_incl_
#define _Mutex_incl_
#include <pthread.h>
namespace rel
{
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();
private:
Lock(const Lock &src); // not allowed
Lock &operator = (const Lock &src); // not allowed
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;
}
}
#endif

348
encfs/NameIO.cpp Normal file
View File

@ -0,0 +1,348 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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 "NameIO.h"
#include "config.h"
#include <rlog/rlog.h>
#include <rlog/Error.h>
#include <map>
// 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 <iostream>
#include "BlockNameIO.h"
#include "StreamNameIO.h"
#include "NullNameIO.h"
using namespace std;
using namespace rel;
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> NameIO::New( const string &name,
const shared_ptr<Cipher> &cipher, const CipherKey &key)
{
shared_ptr<NameIO> 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> NameIO::New( const Interface &iface,
const shared_ptr<Cipher> &cipher, const CipherKey &key )
{
shared_ptr<NameIO> result;
if(gNameIOMap)
{
NameIOMap_t::const_iterator it;
NameIOMap_t::const_iterator end = gNameIOMap->end();
for(it = gNameIOMap->begin(); it != end; ++it)
{
if( it->second.iface.implements( 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 == '.');
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 );
}
*/

136
encfs/NameIO.h Normal file
View File

@ -0,0 +1,136 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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.
*/
#ifndef _NameIO_incl_
#define _NameIO_incl_
#include <string>
#include <list>
#include <inttypes.h>
#include "Interface.h"
#include "CipherKey.h"
using boost::shared_ptr;
class Cipher;
class NameIO
{
public:
typedef shared_ptr<NameIO> (*Constructor)( const rel::Interface &iface,
const shared_ptr<Cipher> &cipher, const CipherKey &key);
struct Algorithm
{
std::string name;
std::string description;
rel::Interface iface;
};
typedef std::list<Algorithm> AlgorithmList;
static AlgorithmList GetAlgorithmList( bool includeHidden = false );
static shared_ptr<NameIO> New( const rel::Interface &iface,
const shared_ptr<Cipher> &cipher, const CipherKey &key);
static shared_ptr<NameIO> New( const std::string &name,
const shared_ptr<Cipher> &cipher, const CipherKey &key);
static bool Register( const char *name, const char *description,
const rel::Interface &iface, Constructor constructor,
bool hidden = false);
NameIO();
virtual ~NameIO();
virtual rel::Interface interface() const =0;
void setChainedNameIV( bool enable );
bool getChainedNameIV() const;
void setReverseEncryption( bool enable );
bool getReverseEncryption() const;
std::string encodePath( const char *plaintextPath ) const;
std::string decodePath( const char *encodedPath ) const;
std::string encodePath( const char *plaintextPath, uint64_t *iv ) const;
std::string decodePath( const char *encodedPath, uint64_t *iv ) const;
virtual int maxEncodedNameLen( int plaintextNameLen ) const =0;
virtual int maxDecodedNameLen( int encodedNameLen ) const =0;
std::string encodeName( const char *plaintextName, int length ) const;
std::string decodeName( const char *encodedName, int length ) const;
protected:
virtual int encodeName( const char *plaintextName, int length,
char *encodedName ) const;
virtual int decodeName( const char *encodedName, int length,
char *plaintextName ) const;
virtual int encodeName( const char *plaintextName, int length,
uint64_t *iv, char *encodedName ) const =0;
virtual int decodeName( const char *encodedName, int length,
uint64_t *iv, char *plaintextName ) const =0;
private:
std::string recodePath( const char *path,
int (NameIO::*codingLen)(int) const,
int (NameIO::*codingFunc)(const char *, int,
uint64_t *, char *) const,
uint64_t *iv ) const;
std::string _encodePath( const char *plaintextPath, uint64_t *iv ) const;
std::string _decodePath( const char *encodedPath, uint64_t *iv ) const;
std::string _encodeName( const char *plaintextName, int length ) const;
std::string _decodeName( const char *encodedName, int length ) const;
bool chainedNameIV;
bool reverseEncryption;
};
/*
Helper macros for creating temporary buffers with an optimization that
below a given size (OptimizedSize) is allocated on the stack, and when a
larger size is requested it is allocated on the heap.
BUFFER_RESET should be called for the same name as BUFFER_INIT
*/
#define BUFFER_INIT( Name, OptimizedSize, Size ) \
char Name ## _Raw [ OptimizedSize ]; \
char *Name = Name ## _Raw; \
if( sizeof(Name ## _Raw) < 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);
#endif

177
encfs/NullCipher.cpp Normal file
View File

@ -0,0 +1,177 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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 "NullCipher.h"
#include "Range.h"
#include "Interface.h"
#include <boost/shared_ptr.hpp>
#include <rlog/rlog.h>
#include <string.h>
using namespace std;
using namespace rel;
using namespace rlog;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
static Interface NullInterface( "nullCipher", 1, 0, 0 );
static Range NullKeyRange(0);
static Range NullBlockRange(1,4096,1);
static shared_ptr<Cipher> NewNullCipher(const Interface &iface, int keyLen)
{
(void)keyLen;
return shared_ptr<Cipher>( 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);
class NullKey : public AbstractCipherKey
{
public:
NullKey() {}
virtual ~NullKey() {}
};
class NullDestructor
{
public:
NullDestructor() {}
NullDestructor(const NullDestructor &) {}
~NullDestructor() {}
NullDestructor &operator = (const NullDestructor &){ return *this; }
void operator ()(NullKey *&) {}
};
shared_ptr<AbstractCipherKey> gNullKey( new NullKey(), NullDestructor() );
NullCipher::NullCipher(const Interface &iface_)
{
this->iface = iface_;
}
NullCipher::~NullCipher()
{
}
Interface NullCipher::interface() const
{
return iface;
}
CipherKey NullCipher::newKey(const char *, int )
{
return gNullKey;
}
CipherKey NullCipher::newRandomKey()
{
return gNullKey;
}
void NullCipher::randomize( unsigned char *buf, int len ) const
{
memset( buf, 0, len );
}
uint64_t NullCipher::MAC_64(const unsigned char *, int ,
const CipherKey &, uint64_t *) const
{
return 0;
}
CipherKey NullCipher::readKey( const unsigned char *,
const CipherKey &, bool)
{
return gNullKey;
}
void NullCipher::writeKey(const CipherKey &, unsigned char *,
const CipherKey &)
{
}
bool NullCipher::compareKey(const CipherKey &A_,
const CipherKey &B_) const
{
shared_ptr<NullKey> A = dynamic_pointer_cast<NullKey>(A_);
shared_ptr<NullKey> B = dynamic_pointer_cast<NullKey>(B_);
return A.get() == B.get();
}
int NullCipher::encodedKeySize() const
{
return 0;
}
int NullCipher::keySize() const
{
return 0;
}
int NullCipher::cipherBlockSize() const
{
return 1;
}
bool NullCipher::streamEncode( unsigned char *src, int len,
uint64_t iv64, const CipherKey &key) const
{
(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
{
(void)src;
(void)len;
(void)iv64;
(void)key;
return true;
}
bool NullCipher::blockEncode( unsigned char *, int , uint64_t,
const CipherKey & ) const
{
return true;
}
bool NullCipher::blockDecode( unsigned char *, int, uint64_t,
const CipherKey & ) const
{
return true;
}
bool NullCipher::Enabled()
{
return true;
}

80
encfs/NullCipher.h Normal file
View File

@ -0,0 +1,80 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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.
*
*/
#ifndef _NullCipher_incl_
#define _NullCipher_incl_
#include "Cipher.h"
#include "Interface.h"
/*
Implements Cipher interface for a pass-through mode. May be useful for
testing, but that's it.
*/
class NullCipher : public Cipher
{
rel::Interface iface;
public:
NullCipher(const rel::Interface &iface);
virtual ~NullCipher();
virtual rel::Interface interface() const;
// create a new key based on a password
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 void randomize( unsigned char *buf, int len ) const;
virtual uint64_t MAC_64(const unsigned char *data, int len,
const CipherKey &key, uint64_t *chainedIV) const;
// functional interfaces
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;
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();
};
#endif

86
encfs/NullNameIO.cpp Normal file
View File

@ -0,0 +1,86 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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 "NullNameIO.h"
#include "Cipher.h"
#include "base64.h"
using namespace rel;
using boost::shared_ptr;
static shared_ptr<NameIO> NewNNIO( const Interface &,
const shared_ptr<Cipher> &, const CipherKey & )
{
return shared_ptr<NameIO>( new NullNameIO() );
}
static Interface NNIOIface("nameio/null", 1, 0, 0);
static bool NullNameIO_registered = NameIO::Register("Null",
"No encryption of filenames", NNIOIface, NewNNIO);
NullNameIO::NullNameIO( )
{
}
NullNameIO::~NullNameIO()
{
}
Interface NullNameIO::interface() const
{
return NNIOIface;
}
Interface NullNameIO::CurrentInterface()
{
return NNIOIface;
}
int NullNameIO::maxEncodedNameLen( int plaintextNameLen ) const
{
return plaintextNameLen;
}
int NullNameIO::maxDecodedNameLen( int encodedNameLen ) const
{
return encodedNameLen;
}
int NullNameIO::encodeName( const char *plaintextName, int length,
uint64_t *iv, char *encodedName ) const
{
memcpy( encodedName, plaintextName, length );
return length;
}
int NullNameIO::decodeName( const char *encodedName, int length,
uint64_t *iv, char *plaintextName ) const
{
memcpy( plaintextName, encodedName, length );
return length;
}
bool NullNameIO::Enabled()
{
return true;
}

49
encfs/NullNameIO.h Normal file
View File

@ -0,0 +1,49 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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.
*/
#ifndef _NullNameIO_incl_
#define _NullNameIO_incl_
#include "NameIO.h"
class NullNameIO : public NameIO
{
public:
static rel::Interface CurrentInterface();
NullNameIO( );
virtual ~NullNameIO();
virtual rel::Interface interface() const;
virtual int maxEncodedNameLen( int plaintextNameLen ) const;
virtual int maxDecodedNameLen( int encodedNameLen ) const;
// hack to help with static builds
static bool Enabled();
protected:
virtual int encodeName( const char *plaintextName, int length,
uint64_t *iv, char *encodedName ) const;
virtual int decodeName( const char *encodedName, int length,
uint64_t *iv, char *plaintextName ) const;
private:
};
#endif

110
encfs/Range.h Normal file
View File

@ -0,0 +1,110 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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.
*/
#ifndef _Range_incl_
#define _Range_incl_
class Range
{
int minVal;
int maxVal;
int increment;
public:
Range();
Range(int minMax);
Range(int min, int max, int increment);
bool allowed(int value) const;
int closest(int value) const;
int min() const;
int max() const;
int inc() const;
};
inline Range::Range(int minMax)
{
this->minVal = minMax;
this->maxVal = minMax;
this->increment = 1;
}
inline Range::Range(int min_, int max_, int increment_)
{
this->minVal = min_;
this->maxVal = max_;
this->increment = increment_;
if(increment == 0)
this->increment = 1;
}
inline Range::Range()
: minVal(-1)
, maxVal(-1)
, increment(1)
{
}
inline bool Range::allowed(int value) const
{
if(value >= minVal && value <= maxVal)
{
int tmp = value - minVal;
if((tmp % increment) == 0)
return true;
}
return false;
}
inline int Range::closest(int value) const
{
if(allowed(value))
return value;
else
if(value < minVal)
return minVal;
else
if(value > maxVal)
return maxVal;
// must be inbetween but not matched with increment
int tmp = value - minVal;
// try rounding to the nearest increment..
tmp += (increment >> 1);
tmp -= (tmp % increment);
return closest( value + tmp );
}
inline int Range::min() const
{
return minVal;
}
inline int Range::max() const
{
return maxVal;
}
inline int Range::inc() const
{
return increment;
}
#endif

322
encfs/RawFileIO.cpp Normal file
View File

@ -0,0 +1,322 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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.
*/
#ifdef linux
#define _XOPEN_SOURCE 500 // pick up pread , pwrite
#endif
#include <unistd.h>
#include "RawFileIO.h"
#include <rlog/rlog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
using namespace std;
static rel::Interface RawFileIO_iface("FileIO/Raw", 1, 0, 0);
FileIO *NewRawFileIO( const rel::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 );
}
rel::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<RawFileIO*>(this)->fileSize = stbuf.st_size;
const_cast<RawFileIO*>(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;
}

61
encfs/RawFileIO.h Normal file
View File

@ -0,0 +1,61 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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.
*/
#ifndef _RawFileIO_incl_
#define _RawFileIO_incl_
#include "FileIO.h"
#include <string>
class RawFileIO : public FileIO
{
public:
RawFileIO();
RawFileIO( const std::string &fileName );
virtual ~RawFileIO();
virtual rel::Interface interface() const;
virtual void setFileName( const char *fileName );
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;
protected:
std::string name;
bool knownSize;
off_t fileSize;
int fd;
int oldfd;
bool canWrite;
};
#endif

816
encfs/SSL_Cipher.cpp Normal file
View File

@ -0,0 +1,816 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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 <openssl/blowfish.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/hmac.h>
#include "SSL_Cipher.h"
#include "Range.h"
#include "MemoryPool.h"
#include "Mutex.h"
#include <string.h>
#include <sys/mman.h>
#include <rlog/rlog.h>
#include <rlog/Error.h>
#include "i18n.h"
using namespace std;
using namespace rel;
using namespace rlog;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
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.
Eliminated the salt code, since we don't use it.. Reason is that we're
using the derived key to encode random data. Since there is no known
plaintext, there is no ability for an attacker to pre-compute known
password->data mappings, which is what the salt is meant to frustrate.
*/
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, mds - offset );
if( toCopy )
{
memcpy( key, mdBuf+offset, toCopy );
key += toCopy;
nkey -= toCopy;
offset += toCopy;
}
toCopy = MIN( niv, 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;
}
// - 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
static Interface BlowfishInterface( "ssl/blowfish", 2, 1, 1 );
static Interface AESInterface( "ssl/aes", 2, 1, 1 );
#if defined(HAVE_EVP_BF)
static Range BFKeyRange(128,256,32);
static Range BFBlockRange(64,4096,8);
static shared_ptr<Cipher> 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<Cipher>( 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<Cipher> 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<Cipher>( 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 _ivLength length bytes for
// iv
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 );
memset( buffer, 0, keySize + ivLength );
// most likely fails unless we're running as root, or a user-page-lock
// kernel patch is applied..
mlock( buffer, keySize + ivLength );
}
SSLKey::~SSLKey()
{
memset( buffer, 0, keySize + ivLength );
OPENSSL_free( buffer );
munlock( buffer, keySize + ivLength );
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<SSLKey> &key )
{
return key->buffer;
}
inline unsigned char* IVData( const shared_ptr<SSLKey> &key )
{
return key->buffer + key->keySize;
}
void initKey(const shared_ptr<SSLKey> &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_DecryptInit_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.current() == 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)
{
const EVP_MD *md = EVP_sha1();
if(!md)
{
rError("Unknown digest SHA1");
return CipherKey();
}
shared_ptr<SSLKey> key( new SSLKey( _keySize, _ivLength) );
int bytes = 0;
if( iface.current() > 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 ];
// to avoid warnings of uninitialized data from valgrind
memset(tmpBuf, 0, sizeof(tmpBuf));
if(RAND_bytes( tmpBuf, bufLen ) == 0)
{
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 CipherKey();
}
}
shared_ptr<SSLKey> 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..
int bytes = BytesToKey( _keySize, _ivLength, EVP_sha1(), tmpBuf,
bufLen, 16, KeyData(key), IVData(key) );
if(bytes != (int)_keySize)
{
rWarning("newKey: BytesToKey returned %i, expecting %i key bytes",
bytes, _keySize);
}
memset( tmpBuf, 0, 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 != 0);
// 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;
}
void SSL_Cipher::randomize( unsigned char *buf, int len ) const
{
memset(buf, 0, len);
int result = RAND_pseudo_bytes( buf, len );
rAssert( result >= 0 );
}
uint64_t SSL_Cipher::MAC_64( const unsigned char *data, int len,
const CipherKey &key, uint64_t *chainedIV ) const
{
shared_ptr<SSLKey> mk = dynamic_pointer_cast<SSLKey>(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<SSLKey> mk = dynamic_pointer_cast<SSLKey>(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_CHECKSUM_BYTES; ++i)
checksum = (checksum << 8) | (unsigned int)data[i];
memcpy( tmpBuf, data+KEY_CHECKSUM_BYTES, _keySize + _ivLength );
streamDecode(tmpBuf, _keySize + _ivLength, checksum, masterKey);
// check for success
unsigned int checksum2 = MAC_32( tmpBuf, _keySize + _ivLength, masterKey );
if(checksum2 != checksum && checkKey)
{
rDebug("checksum mismatch: expected %u, got %u", checksum, checksum2);
rDebug("on decode of %i bytes", _keySize + _ivLength);
memset( tmpBuf, 0, sizeof(tmpBuf) );
return CipherKey();
}
shared_ptr<SSLKey> 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<SSLKey> key = dynamic_pointer_cast<SSLKey>(ckey);
rAssert(key->keySize == _keySize);
rAssert(key->ivLength == _ivLength);
shared_ptr<SSLKey> mk = dynamic_pointer_cast<SSLKey>(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<SSLKey> key1 = dynamic_pointer_cast<SSLKey>(A);
shared_ptr<SSLKey> key2 = dynamic_pointer_cast<SSLKey>(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, unsigned int seed,
const shared_ptr<SSLKey> &key) const
{
/* These multiplication constants chosen as they represent (non optimal)
Golumb rulers, the idea being to spread around the information in the
seed.
0x060a4011 : ruler length 26, 7 marks, 21 measurable lengths
0x0221040d : ruler length 25, 7 marks, 21 measurable lengths
*/
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<toFlip; ++i)
revBuf[i] = buf[toFlip - (i+1)];
memcpy( buf, revBuf, toFlip );
bytesLeft -= toFlip;
buf += toFlip;
}
memset(revBuf, 0, sizeof(revBuf));
}
static void shuffleBytes(unsigned char *buf, int size)
{
for(int i=0; i<size-1; ++i)
buf[i+1] ^= buf[i];
}
static void unshuffleBytes(unsigned char *buf, int size)
{
for(int i=size-1; i; --i)
buf[i] ^= buf[i-1];
}
/* Partial blocks are encoded with a stream cipher. We make multiple passes on
the data to ensure that the ends of the data depend on each other.
*/
bool SSL_Cipher::streamEncode(unsigned char *buf, int size,
uint64_t iv64, const CipherKey &ckey) const
{
rAssert( size > 0 );
shared_ptr<SSLKey> key = dynamic_pointer_cast<SSLKey>(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<SSLKey> key = dynamic_pointer_cast<SSLKey>(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<SSLKey> key = dynamic_pointer_cast<SSLKey>(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<SSLKey> key = dynamic_pointer_cast<SSLKey>(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;
}

141
encfs/SSL_Cipher.h Normal file
View File

@ -0,0 +1,141 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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.
*
*/
#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
using boost::shared_ptr;
/*
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
{
rel::Interface iface;
rel::Interface realIface;
const EVP_CIPHER *_blockCipher;
const EVP_CIPHER *_streamCipher;
unsigned int _keySize; // in bytes
unsigned int _ivLength;
public:
SSL_Cipher(const rel::Interface &iface, const rel::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 rel::Interface interface() const;
// create a new key based on a password
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 void randomize( unsigned char *buf, int len ) 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, unsigned int seed,
const shared_ptr<SSLKey> &key ) const;
};
#endif

205
encfs/StreamNameIO.cpp Normal file
View File

@ -0,0 +1,205 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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 "StreamNameIO.h"
#include "Cipher.h"
#include "base64.h"
#include <rlog/rlog.h>
#include <rlog/Error.h>
#include "i18n.h"
using namespace rel;
using namespace std;
static shared_ptr<NameIO> NewStreamNameIO( const Interface &iface,
const shared_ptr<Cipher> &cipher, const CipherKey &key)
{
return shared_ptr<NameIO>( 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 Interface("nameio/stream", 2, 1, 2);
}
StreamNameIO::StreamNameIO( const rel::Interface &iface,
const shared_ptr<Cipher> &cipher,
const CipherKey &key )
: _interface( iface.current() )
, _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;
}

57
encfs/StreamNameIO.h Normal file
View File

@ -0,0 +1,57 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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.
*/
#ifndef _StreamNameIO_incl_
#define _StreamNameIO_incl_
#include "NameIO.h"
#include "CipherKey.h"
class Cipher;
using boost::shared_ptr;
class StreamNameIO : public NameIO
{
public:
static rel::Interface CurrentInterface();
StreamNameIO( const rel::Interface &iface,
const shared_ptr<Cipher> &cipher,
const CipherKey &key );
virtual ~StreamNameIO();
virtual rel::Interface interface() const;
virtual int maxEncodedNameLen( int plaintextNameLen ) const;
virtual int maxDecodedNameLen( int encodedNameLen ) const;
// hack to help with static builds
static bool Enabled();
protected:
virtual int encodeName( const char *plaintextName, int length,
uint64_t *iv, char *encodedName ) const;
virtual int decodeName( const char *encodedName, int length,
uint64_t *iv, char *plaintextName ) const;
private:
int _interface;
shared_ptr<Cipher> _cipher;
CipherKey _key;
};
#endif

164
encfs/base64.cpp Normal file
View File

@ -0,0 +1,164 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2002-2004, 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 "base64.h"
// change between two powers of two, stored as the low bits of the bytes in the
// arrays.
// It is the caller's responsibility to make sure the output array is large
// enough.
void changeBase2(unsigned char *src, int srcLen, int src2Pow,
unsigned char *dst, int dstLen, int dst2Pow)
{
unsigned long work = 0;
int workBits = 0; // number of bits left in the work buffer
unsigned char *end = src + srcLen;
unsigned char *origDst = dst;
const int mask = (1 << dst2Pow) -1;
// copy the new bits onto the high bits of the stream.
// The bits that fall off the low end are the output bits.
while(src != end)
{
work |= ((unsigned long)(*src++)) << workBits;
workBits += src2Pow;
while(workBits >= dst2Pow)
{
*dst++ = work & mask;
work >>= dst2Pow;
workBits -= dst2Pow;
}
}
// now, we could have a partial value left in the work buffer..
if(workBits && ((dst - origDst) < dstLen))
*dst++ = work & mask;
}
/*
Same as changeBase2, except the output is written over the input data. The
output is assumed to be large enough to accept the data.
Uses the stack to store output values. Recurse every time a new value is
to be written, then write the value at the tail end of the recursion.
*/
static
void changeBase2Inline(unsigned char *src, int srcLen,
int src2Pow, int dst2Pow,
bool outputPartialLastByte,
unsigned long work,
int workBits,
unsigned char *outLoc)
{
const int mask = (1 << dst2Pow) -1;
if(!outLoc)
outLoc = src;
// copy the new bits onto the high bits of the stream.
// The bits that fall off the low end are the output bits.
while(srcLen && workBits < dst2Pow)
{
work |= ((unsigned long)(*src++)) << workBits;
workBits += src2Pow;
--srcLen;
}
// we have at least one value that can be output
char outVal = work & mask;
work >>= dst2Pow;
workBits -= dst2Pow;
if(srcLen)
{
// more input left, so recurse
changeBase2Inline( src, srcLen, src2Pow, dst2Pow,
outputPartialLastByte, work, workBits, outLoc+1);
*outLoc = outVal;
} else
{
// no input left, we can write remaining values directly
*outLoc++ = outVal;
// we could have a partial value left in the work buffer..
if(workBits && outputPartialLastByte)
*outLoc = work & mask;
}
}
void changeBase2Inline(unsigned char *src, int srcLen,
int src2Pow, int dst2Pow,
bool outputPartialLastByte)
{
changeBase2Inline(src, srcLen, src2Pow, dst2Pow,
outputPartialLastByte, 0, 0, 0);
}
// character set for ascii b64:
// ",-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
// a standard base64 (eg a64l doesn't use ',-' but uses './'. We don't
// do that because '/' is a reserved character, and it is useful not to have
// '.' included in the encrypted names, so that it can be reserved for files
// with special meaning.
static const char B642AsciiTable[] = ",-0123456789";
void B64ToAscii(unsigned char *in, int length)
{
for(int offset=0; offset<length; ++offset)
{
int ch = in[offset];
if(ch > 11)
{
if(ch > 37)
ch += 'a' - 38;
else
ch += 'A' - 12;
} else
ch = B642AsciiTable[ ch ];
in[offset] = ch;
}
}
static const unsigned char Ascii2B64Table[] =
" 01 23456789:; ";
// 0123456789 123456789 123456789 123456789 123456789 123456789 1234
// 0 1 2 3 4 5 6
void AsciiToB64(unsigned char *in, int length)
{
return AsciiToB64(in, in, length);
}
void AsciiToB64(unsigned char *out, const unsigned char *in, int length)
{
while(length--)
{
unsigned char ch = *in++;
if(ch >= 'A')
{
if(ch >= 'a')
ch += 38 - 'a';
else
ch += 12 - 'A';
} else
ch = Ascii2B64Table[ ch ] - '0';
*out++ = ch;
}
}

55
encfs/base64.h Normal file
View File

@ -0,0 +1,55 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2002-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.
*
*/
#ifndef _base64_incl_
#define _base64_incl_
inline int B64ToB256Bytes( int numB64Bytes )
{
return (numB64Bytes * 6) / 8; // round down
}
inline int B256ToB64Bytes( int numB256Bytes )
{
return (numB256Bytes * 8 + 5) / 6; // round up
}
/*
convert data between different bases - each being a power of 2.
*/
void changeBase2(unsigned char *src, int srcLength, int srcPow2,
unsigned char *dst, int dstLength, int dstPow2);
/*
same as changeBase2, but writes output over the top of input data.
*/
void changeBase2Inline(unsigned char *buf, int srcLength,
int srcPow2, int dst2Pow,
bool outputPartialLastByte);
// inplace translation from values [0,2^6] => base64 ASCII
void B64ToAscii(unsigned char *buf, int length);
// inplace translation from values base64 ASCII => [0,2^6]
void AsciiToB64(unsigned char *buf, int length);
void AsciiToB64(unsigned char *out, const unsigned char *in, int length);
#endif

4
encfs/docs/Makefile.am Normal file
View File

@ -0,0 +1,4 @@
####### kdevelop will overwrite this part!!! (begin)##########
####### kdevelop will overwrite this part!!! (end)############

View File

@ -0,0 +1,4 @@
####### kdevelop will overwrite this part!!! (begin)##########
####### kdevelop will overwrite this part!!! (end)############

765
encfs/encfs.cpp Normal file
View File

@ -0,0 +1,765 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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 <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <sys/statvfs.h>
#include <sys/time.h>
#include <sys/types.h>
#ifdef linux
#include <sys/fsuid.h>
#endif
#ifdef HAVE_ATTR_XATTR_H
#include <attr/xattr.h>
#elif HAVE_SYS_XATTR_H
#include <sys/xattr.h>
#endif
#ifdef HAVE_ULOCKMGR_H
extern "C" {
#include <ulockmgr.h>
}
#endif
#include <string>
#include <map>
#include <boost/tuple/tuple.hpp>
#include "DirNode.h"
#include "MemoryPool.h"
#include "FileUtils.h"
#include "Mutex.h"
#include "Context.h"
#include <rlog/rlog.h>
#include <rlog/Error.h>
#ifndef MIN
#define MIN(a,b) (((a)<(b)) ? (a): (b))
#endif
#define ESUCCESS 0
using namespace std;
using namespace rlog;
using rel::Lock;
using namespace boost;
#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<typename T>
static int withCipherPath( const char *opName, const char *path,
int (*op)(EncFS_Context *, const string &name, T data ), T data )
{
EncFS_Context *ctx = context();
int res = -EIO;
shared_ptr<DirNode> 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
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<typename T>
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<DirNode> FSRoot = ctx->getRoot(&res);
if(!FSRoot)
return res;
try
{
shared_ptr<FileNode> 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)
{
return fnode->getAttr(stbuf);
}
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<DirNode> 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<DirNode> FSRoot = ctx->getRoot(&res);
if(!FSRoot)
return res;
try
{
shared_ptr<FileNode> 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<FileNode> 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<DirNode> 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<FileNode> 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<DirNode> 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<char *, size_t> data )
{
char *buf = data.get<0>();
size_t size = data.get<1>();
int res = ESUCCESS;
shared_ptr<DirNode> 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<DirNode> 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<DirNode> 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<DirNode> 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<uid_t, gid_t> data)
{
int res = lchown( cyName.c_str(), data.get<0>(), data.get<1>() );
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 = utimes( 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<DirNode> FSRoot = ctx->getRoot(&res);
if(!FSRoot)
return res;
try
{
shared_ptr<FileNode> 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<unsigned char *, size_t, off_t> data)
{
return fnode->read( data.get<2>(), data.get<0>(), data.get<1>());
}
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<const char *, size_t, off_t> data)
{
size_t size = data.get<1>();
if(fnode->write( data.get<2>(), (unsigned char *)data.get<0>(), 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
int _do_setxattr(EncFS_Context *, const string &cyName,
tuple<const char *, const char *, size_t, int> data)
{
int res = ::setxattr( cyName.c_str(), data.get<0>(), data.get<1>(),
data.get<2>(), data.get<3>() );
return (res == -1) ? -errno : res;
}
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) );
}
int _do_getxattr(EncFS_Context *, const string &cyName,
tuple<const char *, void *, size_t> data)
{
int res = ::getxattr( cyName.c_str(), data.get<0>(),
data.get<1>(), data.get<2>());
return (res == -1) ? -errno : res;
}
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) );
}
int _do_listxattr(EncFS_Context *, const string &cyName,
tuple<char *, size_t> data)
{
int res = ::listxattr( cyName.c_str(), data.get<0>(), data.get<1>() );
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) );
}
int _do_removexattr(EncFS_Context *, const string &cyName, const char *name)
{
int res = ::removexattr( cyName.c_str(), name );
return (res == -1) ? -errno : res;
}
int encfs_removexattr( const char *path, const char *name )
{
return withCipherPath( "removexattr", path, _do_removexattr, name );
}
#endif // HAVE_XATTR
int _do_lock(FileNode *fnode,
tuple<int, struct flock *, struct fuse_file_info *> data)
{
int cmd = data.get<0>();
struct flock *lock = data.get<1>();
struct fuse_file_info *fi = data.get<2>();
int fh = fnode->open( O_RDONLY );
if(fh >= 0)
{
return ulockmgr_op(fh, cmd, lock, &fi->lock_owner,
sizeof(fi->lock_owner));
} else
{
rInfo("open failed in lock of %s", fnode->cipherName());
return fh;
}
}
#ifdef HAVE_ULOCKMGR_H
int encfs_lock( const char *path, struct fuse_file_info *fi, int cmd,
struct flock *lock)
{
return withFileNode( "lock", path, fi,
_do_lock, make_tuple(cmd, lock, fi) );
}
#endif

104
encfs/encfs.h Normal file
View File

@ -0,0 +1,104 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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.
*
*/
#ifndef _encfs_incl_
#define _encfs_incl_
#include "config.h"
#include <fuse.h>
#include <unistd.h>
#if defined(HAVE_SYS_XATTR_H) | defined(HAVE_ATTR_XATTR_H)
#define HAVE_XATTR
#endif
#ifndef linux
#include <errno.h>
static __inline int setfsuid(uid_t uid)
{
uid_t olduid = geteuid();
seteuid(uid);
if (errno != EINVAL)
errno = 0;
return olduid;
}
static __inline int setfsgid(gid_t gid)
{
gid_t oldgid = getegid();
setegid(gid);
if (errno != EINVAL)
errno = 0;
return oldgid;
}
#endif
int encfs_getattr(const char *path, struct stat *stbuf);
int encfs_fgetattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi);
int encfs_readlink(const char *path, char *buf, size_t size);
int encfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler);
int encfs_mknod(const char *path, mode_t mode, dev_t rdev);
int encfs_mkdir(const char *path, mode_t mode);
int encfs_unlink(const char *path);
int encfs_rmdir(const char *path);
int encfs_symlink(const char *from, const char *to);
int encfs_rename(const char *from, const char *to);
int encfs_link(const char *from, const char *to);
int encfs_chmod(const char *path, mode_t mode);
int encfs_chown(const char *path, uid_t uid, gid_t gid);
int encfs_truncate(const char *path, off_t size);
int encfs_ftruncate(const char *path, off_t size,
struct fuse_file_info *fi);
int encfs_utime(const char *path, struct utimbuf *buf);
int encfs_open(const char *path, struct fuse_file_info *info);
int encfs_release(const char *path, struct fuse_file_info *info);
int encfs_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *info);
int encfs_write(const char *path, const char *buf, size_t size, off_t offset,
struct fuse_file_info *info);
int encfs_statfs(const char *, struct statvfs *fst);
int encfs_flush(const char *, struct fuse_file_info *info);
int encfs_fsync(const char *path, int flags, struct fuse_file_info *info);
#ifdef HAVE_XATTR
int encfs_setxattr( const char *path, const char *name, const char *value,
size_t size, int flags );
int encfs_getxattr( const char *path, const char *name, char *value,
size_t size );
int encfs_listxattr( const char *path, char *list, size_t size );
int encfs_removexattr( const char *path, const char *name );
#endif
#ifdef HAVE_ULOCKMGR_H
struct flock;
int encfs_lock( const char *path, struct fuse_file_info *fi, int cmd,
struct flock *lock );
#endif
int encfs_utimens( const char *path, const struct timespec ts[2] );
#endif

464
encfs/encfs.pod Normal file
View File

@ -0,0 +1,464 @@
=cut
Copyright (c) 2003-2004, Valient Gough <vgough@pobox.com>
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 2 of the License, or (at your option) any later
version.
=pod
=head1 NAME
encfs - mounts or creates an encrypted virtual filesystem
=head1 SYNOPSIS
B<encfs> [B<--version>] [B<-s>] [B<-f>] [B<-v>|B<--verbose>]
[B<-i MINUTES>|B<--idle=MINUTES>] [B<--extpass=program>]
[B<-S>|B<--stdinpass>] [B<--anykey>] [B<--forcedecode>]
[B<-d>|B<--fuse-debug>] [B<--public>] [B<--no-default-flags>]
I<rootdir> I<mountPoint>
[B<--> [I<Fuse Mount Options>]]
=head1 DESCRIPTION
B<encfs> creates a virtual encrypted filesystem which stores encrypted data in
the I<rootdir> directory and makes the unencrypted data visible at the
I<mountPoint> directory. The user must supply a password which is used to
(indirectly) encrypt both filenames and file contents.
If B<encfs> is unable to find a supported filesystem at the specified
I<rootdir>, then the user will be asked if they wish to create a new encrypted
filesystem at the specified location. Options will be presented to the user
allowing some control over the algorithms to use. As B<EncFS> matures, there
may be an increasing number of choices.
=head1 OPTIONS
=over 4
=item B<-i>, B<--idle=MINUTES>
Enable automatic unmount of the filesystem after a period of inactivity. The
period is specified in minutes, so the shortest timeout period that can be
requested is one minute. B<EncFS> will not automatically unmount if there are
files open within the filesystem, even if they are open in read-only mode.
However simply having files open does not count as activity.
=item B<-f>
The B<-f> (I<foreground>) option causes B<encfs> to run in the foreground.
Normally B<encfs> spawns off as a daemon and runs in the background, returning
control to the spawning shell. With the B<-f> option, it will run in the
foreground and any warning/debug log messages will be displayed on standard
error. In the default (background) mode, all log messages are logged via
syslog.
=item B<-v>, B<--verbose>
Causes B<encfs> to enable logging of various debug channels within B<encfs>.
Normally these logging messages are disabled and have no effect. It is
recommended that you run in foreground (B<-f>) mode when running with verbose
enabled.
=item B<-s>
The B<-s> (I<single threaded>) option causes B<encfs> to run in single threaded
mode. By default, B<encfs> runs in multi-threaded mode. This option is used
during B<encfs> development in order to simplify debugging and allow it to run
under memory checking tools..
=item B<-d>, B<--fuse-debug>
Enables debugging within the B<FUSE> library. This should only be used if you
suspect a problem within B<FUSE> itself (not B<encfs>), as it generates a lot
of low-level data and is not likely to be very helpful in general problem
tracking. Try I<verbose> mode (B<-v>) first, which gives a higher level view
of what is happening within B<encfs>.
=item B<--forcedecode>
This option only has an effect on filesystems which use MAC block headers. By
default, if a block is decoded and the stored MAC doesn't match what is
calculated, then an IO error is returned to the application and the block is
not returned. However, by specifying B<--forcedecode>, only an error will be
logged and the data will still be returned to the application. This may be
useful for attempting to read corrupted files.
=item B<--public>
Attempt to make encfs behave as a typical multi-user filesystem. By default,
all FUSE based filesystems are visible only to the user who mounted them. No
other users (including root) can view the filesystem contents. The B<--public>
option does two things. It adds the FUSE flags "allow_other" and
"default_permission" when mounting the filesystem, which tells FUSE to allow
other users to access the filesystem, and to use the ownership permissions
provided by the filesystem. Secondly, the B<--public> flag changes how encfs's
node creation functions work - as they will try and set ownership of new nodes
based on the caller identification.
B<Warning>: In order for this to work, encfs must be run as root -- otherwise
it will not have the ability to change ownership of files. I recommend that
you instead investigate if the fuse allow_other option can be used to do what
you want before considering the use of B<--public>.
=item B<-->
The B<--> option tells B<encfs> to send any remaining arguments directly to
B<FUSE>. In turn, B<FUSE> passes the arguments to B<fusermount>. See
the B<fusermount> help page for information on available commands.
=item B<--no-default-flags>
B<Encfs> 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>.
The following command lines produce the same result:
encfs raw crypt
encfs --no-default-flags raw crypt -- -o use_ino,default_permissions
=item B<--extpass=program>
Specify an external program to use for getting the user password. When the
external program is spawned, the environment variable "RootDir" will be set to
contain the path to the root directory. The program should print the password
to standard output.
B<EncFS> takes everything returned from the program to be the password, except
for a trailing newline (\n) which will be removed.
For example, specifying B<--extpass>=I</usr/lib/ssh/ssh-askpass> will cause
B<EncFS> to use ssh's password prompt program.
=item B<-S>, B<--stdinpass>
Read password from standard input, without prompting. This may be useful for
scripting encfs mounts.
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.
=item B<--anykey>
Turn off key validation checking. This allows B<EncFS> to be used with
secondary passwords. This could be used to store a separate set of files in an
encrypted filesystem. B<EncFS> ignores files which do not decode properly, so
files created with separate passwords will only be visible when the filesystem
is mounted with their associated password.
Note that if the primary password is changed (using B<encfsctl>), the other
passwords will not be usable unless the primary password is set back to what it
was, as the other passwords rely on an invalid decoding of the volume key,
which will not remain the same if the primary password is changed.
B<Warning>: Use this option at your own risk.
=back
=head1 EXAMPLES
Create a new encrypted filesystem. Store the raw (encrypted) data in
"~/.crypt" , and make the unencrypted data visible in "~/crypt". Both
directories are in the home directory in this example. This example shows the
full output of encfs as it asks the user if they wish to create the filesystem:
% encfs ~/.crypt ~/crypt
Directory "/home/me/.crypt" does not exist, create (y,n)?y
Directory "/home/me/crypt" does not exist, create (y,n)?y
Creating new encrypted volume.
Please choose from one of the following options:
enter "x" for expert configuration mode,
enter "p" for pre-configured paranoia mode,
anything else, or an empty line will select standard mode.
?>
Standard configuration selected.
Using cipher Blowfish, key size 160, block size 512
New Password: <password entered here>
Verify: <password entered here>
The filesystem is now mounted and visible in I<~/crypt>. If files are created
there, they can be seen in encrypted form in I<~/.crypt>. To unmount the
filesystem, use I<fusermount> with the B<-u> (unmount) option:
% fusermount -u ~/crypt
Another example. To mount the same filesystem, but have fusermount name the
mount point '/dev/foo' (as shown in I<df> and other tools which read
/etc/mtab), and also request kernel-level caching of file data (which are both
special arguments to fusermount):
% encfs ~/.crypt ~/crypt -- -n /dev/foo -c
Or, if you find strange behavior under some particular program when working in
an encrypted filesystem, it may be helpful to run in verbose mode while
reproducing the problem and send along the output with the problem report:
% encfs -v -f ~/.crypt ~/crypt 2> encfs-report.txt
In order to avoid leaking sensitive information through the debugging channels,
all warnings and debug messages (as output in verbose mode) contain only
encrypted filenames. You can use the I<encfsctl> program's I<decode> function
to decode filenames if desired.
=head1 CAVEATS
B<EncFS> 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
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<EncFS> (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<EncFS> will limit you to approximately
3*(N-2)/4. For example if the host filesystem limits to 256 characters, then
B<EncFS> will be limited to 190 character filenames. This is because encrypted
filenames are always longer then plaintext filenames.
=head1 FILESYSTEM OPTIONS
When B<EncFS> is given a root directory which does not contain an existing
B<EncFS> filesystem, it will give the option to create one. Note that options
can only be set at filesystem creation time. There is no support for modifying
a filesystem's options in-place.
If you want to upgrade a filesystem to use newer features, then you need to
create a new filesystem and mount both the old filesystem and new filesystem at
the same time and copy the old to the new.
Multiple instances of encfs can be run at the same time, including different
versions of encfs, as long as they are compatible with the current FUSE module
on your system.
A choice is provided for two pre-configured settings ('standard' and
'paranoia'), along with an expert configuration mode.
I<Standard> mode uses the following settings:
Cipher: Blowfish
Key Size: 160 bits
Filesystem Block Size: 512 bytes
Filename Encoding: Block encoding with IV chaining
Unique initialization vector file headers
I<Paranoia> mode uses the following settings:
Cipher: AES
Key Size: 256 bits
Filesystem Block Size: 512 bytes
Filename Encoding: Block encoding with IV chaining
Unique initialization vector file headers
Message Authentication Code block headers
External IV Chaining
In the expert / manual configuration mode, each of the above options is
configurable. Here is a list of current options with some notes about what
they mean:
=over 4
=item I<Cipher>
Which encryption algorithm to use. The list is generated automatically based
on what supported algorithms B<EncFS> found in the encryption libraries.
When using a recent version of B<OpenSSL>, Blowfish and AES are the typical
options.
Blowfish is an 8 byte cipher - encoding 8 bytes at a time. AES is a 16 byte
cipher.
=item I<Cipher Key Size>
Many, if not all, of the supported ciphers support multiple key lengths. There
is not really much need to have enormous key lengths. Even 160 bits (the
default) is probably overkill.
=item I<Filesystem Block Size>
This is the size (in bytes) that B<EncFS> deals with at one time. Each block
gets its own initialization vector and is encoded in the cipher's
cipher-block-chaining mode. A partial block at the end of a file is encoded
using a stream mode to avoid having to store the filesize somewhere.
Having larger block sizes reduces the overhead of B<EncFS> a little, but it can
also add overhead if your programs read small parts of files. In order to read
a single byte from a file, the entire block that contains that byte must be
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.
=item I<Filename Encoding>
B<New in 1.1>. 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
encrypted data can't tell as much about the length of your filenames. It is
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<Filename Initialization Vector Chaining>
B<New in 1.1>. In previous versions of B<EncFS>, 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
for "foo". This features has almost no performance impact (for most
operations), and so is the default in all modes.
B<Note:> One significant exception is directory renames. Since the
initialization vector for filename encoding depends on the directory path, any
rename requires re-encoding every filename in the tree of the directory being
changed. If there are thousands of files, then EncFS will have to do thousands
of renames. It may also be possible that EncFS will come across a file that it
can't decode or doesn't have permission to move during the rename operation, in
which case it will attempt to undo any changes it made up to that point and the
rename will fail.
=item I<Per-File Initialization Vectors>
B<New in 1.1>. In previous versions of B<EncFS>, 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
initialization vector, so that each file is encrypted in a different way.
This option is enabled by default.
=item I<External IV Chaining>
B<New in 1.1.3>. 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
chaining code. This means that the data in a file becomes tied to the
filename. If an encrypted file is renamed outside of encfs, it will no longer
be decodable within encfs. Note that unless Block MAC headers are enabled, the
decoding error will not be detected and will result in reading random looking
data.
There is a cost associated with this. When External IV Chaining is enabled,
hard links will not be allowed within the filesystem, as there would be no way
to properly decode two different filenames pointing to the same data.
Also, renaming a file requires modifying the file header. So renames will only
be allowed when the user has write access to the file.
Because of these limits, this option is disabled by default for standard mode
(and enabled by default for paranoia mode).
=item I<Block MAC headers>
B<New to 1.1>. 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<EncFS>. B<EncFS> 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
mode.
When this is not enabled and if B<EncFS> is asked to read modified or corrupted
data, it will have no way to verify that the decoded data is what was
originally encoded.
=back
=head1 Attacks
The primary goal of B<EncFS> is to protect data off-line. That is, provide a
convenient way of storing files in a way that will frustrate any attempt to
read them if the files are later intercepted.
Some algorithms in B<EncFS> are also meant to frustrate on-line attacks where
an attacker is assumed to be able to modify the files.
The most intrusive attacks, where an attacker has complete control of the
user's machine (and can therefor modify B<EncFS>, or B<FUSE>, or the kernel
itself) are not guarded against. Do not assume that encrypted files will
protect your sensitive data if you enter your password into a compromised
computer. How you determine that the computer is safe to use is beyond the
scope of this documentation.
That said, here are some example attacks and data gathering techniques on the
filesystem contents along with the algorithms B<EncFS> supports to thwart them:
=over 4
=item B<Attack>: modifying a few bytes of an encrypted file (without knowing
what they will decode to).
B<EncFS> 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<Attack>: copying a random block of one file to a random block of another file.
Each block has its own [deterministic] initialization vector.
=item B<Attack>: 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<Attack>: copying an entire file to another file.
Can be prevented by enabling External IV Chaining mode.
=item B<Attack>: determine if two filenames are the same by looking at encrypted names.
Filename Initialization Vector chaining prevents this by giving each file a
64-bit initialization vector derived from its full path name.
=item B<Attack>: compare if two files contain the same data.
Per-File Initialization Vector support prevents this.
=back
=head1 DISCLAIMER
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. Please refer to the "COPYING" file distributed with
B<encfs> for complete details.
=head1 AUTHORS
B<EncFS> was written by B<< Valient Gough <vgough@pobox.com> >>.
=head1 SEE ALSO
encfsctl(1)

708
encfs/encfsctl.cpp Normal file
View File

@ -0,0 +1,708 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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 <rlog/rlog.h>
#include <rlog/StdioNode.h>
#include <rlog/RLogChannel.h>
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef __FreeBSD__
#include <libintl.h>
#endif
#include "i18n.h"
#ifdef HAVE_SSL
#define NO_DES
#include <openssl/ssl.h>
#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 );
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)")},
{"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, 2, cmd_decode, "(root dir) encoded-name",
// xgroup(usage)
gettext_noop(" -- decodes name and prints plaintext version")},
{"encode", 1, 2, cmd_encode, "(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:
// xgroup(diag)
cout << "\n" << autosprintf(_("Version 5 configuration; "
"created by %s (revision %i)\n"), config.creator.c_str(),
config.subVersion);
break;
}
showFSInfo( config );
return EXIT_SUCCESS;
}
static RootPtr initRootInfo(const char* crootDir)
{
string rootDir(crootDir);
RootPtr result;
if(checkDir( rootDir ))
{
shared_ptr<EncFS_Opts> 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_decode( int argc, char **argv )
{
RootPtr rootInfo = initRootInfo(argv[1]);
if(!rootInfo)
return EXIT_FAILURE;
if( argc > 2 )
{
string name = rootInfo->root->plainPath( argv[2] );
cout << name << "\n";
} else
{
do
{
string name;
cin >> name;
if(name.empty())
break;
name = rootInfo->root->plainPath( name.c_str() );
cout << name << "\n";
} while(1);
}
return EXIT_SUCCESS;
}
static int cmd_encode( int argc, char **argv )
{
RootPtr rootInfo = initRootInfo(argv[1]);
if(!rootInfo)
return EXIT_FAILURE;
if( argc > 2 )
{
string name = rootInfo->root->cipherPath( argv[2] );
cout << name << "\n";
} else
{
do
{
string name;
cin >> name;
if(name.empty())
break;
name = rootInfo->root->cipherPath( name.c_str() );
cout << name << "\n";
} while(1);
}
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<FileNode> 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<typename T>
int processContents( const shared_ptr<EncFS_Root> &rootInfo,
const char *path, T &op )
{
int errCode = 0;
shared_ptr<FileNode> node = rootInfo->root->openNode( path, "encfsctl",
O_RDONLY, &errCode );
if(!node)
{
cerr << "unable to open " << path << endl;
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; i<blocks; ++i)
{
int bytes = node->read(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 copyContents(const shared_ptr<EncFS_Root> &rootInfo,
const char* encfsName, const char* targetName)
{
shared_ptr<FileNode> node =
rootInfo->root->lookupNode( encfsName, "encfsctl" );
if(!node)
{
cerr << "unable to open " << encfsName << endl;
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 << endl;
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 int traverseDirs(const shared_ptr<EncFS_Root> &rootInfo,
string volumeDir, string destDir)
{
// Lookup directory node so we can create a destination directory
// with the same permissions
{
struct stat st;
shared_ptr<FileNode> 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;
if(isDirectory(cpath.c_str()))
traverseDirs(rootInfo, (plainPath + '/').c_str(),
destName + '/');
else
{
int r = copyContents(rootInfo, plainPath.c_str(),
destName.c_str());
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<EncFS_Root> &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(
ngettext("Found %i invalid file.", "Found %i invalid files.",
filesFound), filesFound) << "\n";
return EXIT_SUCCESS;
}
static int do_chpasswd( bool useStdin, 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> cipher = Cipher::New(
config.cipherIface, config.keySize );
if(!cipher)
{
cout << autosprintf(_("Unable to find specified cipher \"%s\"\n"),
config.cipherIface.name().c_str());
return EXIT_FAILURE;
}
// ask for existing password
cout << _("Enter current Encfs password\n");
CipherKey userKey = getUserKey( cipher, useStdin );
if(!userKey)
return EXIT_FAILURE;
rAssert( (int)config.keyData.length() == cipher->encodedKeySize() );
// 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(
(unsigned char*)config.keyData.data(), userKey );
if(!volumeKey)
{
cout << _("Invalid password\n");
return EXIT_FAILURE;
}
// Now, get New user key..
userKey.reset();
cout << _("Enter new Encfs password\n");
if( useStdin )
userKey = getUserKey( cipher, true );
else
userKey = getNewUserKey( cipher );
// 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();
config.keyData.assign( (char*)keyBuf, encodedKeySize );
delete[] keyBuf;
if(saveConfig( cfgType, 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");
}
}
volumeKey.reset();
return result;
}
static int chpasswd( int argc, char **argv )
{
return do_chpasswd( false, argc, argv );
}
static int chpasswdAutomaticly( int argc, char **argv )
{
return do_chpasswd( true, 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]) << endl;
} else
{
if((argc-2 < commands[offset].minOptions) ||
(argc-2 > commands[offset].maxOptions))
{
cerr << autosprintf(
_("Incorrect number of arguments for command \"%s\""),
argv[1]) << endl;
} else
return (*commands[offset].func)( argc-1, argv+1 );
}
}
return EXIT_FAILURE;
}

99
encfs/encfsctl.pod Normal file
View File

@ -0,0 +1,99 @@
=cut
Copyright (c) 2003-2004, Valient Gough <vgough@pobox.com>
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 2 of the License, or (at your option) any later
version.
=pod
=head1 NAME
encfsctl - administrative tool for working with EncFS filesystems
=head1 SYNOPSIS
B<encfsctl> [I<command> I<command_args>]
B<encfsctl> I<rootdir>
B<encfsctl> info I<rootdir>
B<encfsctl> passwd I<rootdir>
B<encfsctl> showcruft I<rootdir>
B<encfsctl> decode I<rootdir> [encoded name]
=head1 DESCRIPTION
B<encfsctl> is an administrative tool for working with EncFS filesystems. It
is capable of changing the user supplied password, displaying basic information
about an encrypted volume, and other related operations.
=head1 COMMANDS
=over 4
=item B<info>
Display basic information about the filesystem. Takes a single argument,
I<rootdir>, which is the root directory of the encrypted filesystem. The
filesystem need not be mounted. B<Info> is also the default command if only a
root directory is provided on the command line.
=item B<passwd>
Allows changing the password of the encrypted filesystem. The user will be
prompted for the existing password and the new password.
=item B<showcruft>
Recursively search through the entire volume and display all files which are
not decodable (only checks filename encoding, not block MAC headers). This
might be useful for cleanup in case you've made use of features which create
files which are not decodable under the primary key.
=item B<decode>
Allows you to specify an encoded name on the command line, and displayed a
decoded version. This is mostly useful for debugging, as debug messages always
display encrypted filenames (to avoid leaking sensitive data through the debug
channels). So this command provides a way to decode the filenames.
If no name is specified on the command line, then a list of filenames will be
read from stdin and decoded.
=back
=head1 EXAMPLES
Show information about an encrypted filesystem:
% encfsctl info ~/.crypt
Version 5 configuration; created by EncFS 1.1 (revision 20040504)
Filesystem cipher: "ssl/aes" , version 2:1:1
Filename encoding: "nameio/block" , version 3:0:1
Key Size: 192 bits
Block Size: 512 bytes
Each file contains 8 byte header with unique IV data.
Filesname encoded using IV chaining mode.
=head1 DISCLAIMER
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. Please refer to the "COPYING" file distributed with
B<encfs> for complete details.
=head1 AUTHORS
EncFS was written by Valient Gough <vgough@pobox.com>.
=head1 SEE ALSO
encfs(1)

67
encfs/encfssh Normal file
View File

@ -0,0 +1,67 @@
#!/bin/sh
# This script mounts an encfs filesystem, starts a shell in the mounted
# directory, and then unmounts the filesystem when the shell exits.
# This is an equivalent of the cfssh utility for cfs.
# Contributed by David Rosenstrauch.
canonicalize() {
cd "$1"
pwd
}
if [ -z "$1" -o "$1" = "-h" ]; then
echo Usage: encfssh encrypted_directory [unencrypted-directory [-p]]
echo " -p mount the unencrypted directory as public"
exit 1
fi
enc_dir=$1
unenc_dir_given=false
mount_public=false
if [ ! -z "$2" ]; then
unenc_dir_given=true
unenc_dir=$2
for arg in "$@" ; do
if [ "$arg" = "-p" ]; then
mount_public=true
fi
done
else
unenc_dir=.$RANDOM.$RANDOM
fi
if [ ! -d "$enc_dir" ]; then
mkdir $enc_dir
fi
if [ ! -d "$unenc_dir" ]; then
mkdir $unenc_dir
fi
enc_dir=$(canonicalize "$enc_dir")
unenc_dir=$(canonicalize "$unenc_dir")
options=""
if $unenc_dir_given; then
if $mount_public; then
options="-- -o allow_other"
fi
fi
# Attach the directory and change into it
if encfs $enc_dir $unenc_dir $options; then :; else
echo "encfs failed"
rmdir $unenc_dir
exit 1
fi
if ! $unenc_dir_given; then
chmod 700 $unenc_dir
fi
echo "Directory is $unenc_dir"
orig_dir=$(pwd)
cd $unenc_dir
# Set the shell up
exec /bin/sh -c "$SHELL ; cd $orig_dir ; fusermount -u $unenc_dir ; if ! $unenc_dir_given; then rmdir $unenc_dir; fi"

41
encfs/i18n.h Normal file
View File

@ -0,0 +1,41 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2004, 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.
*/
#ifndef _i18n_incl_
#define _i18n_incl_
#if defined(LOCALEDIR)
# include "gettext.h"
// make shortcut for gettext
# define _(STR) gettext (STR)
# include "autosprintf.h"
using gnu::autosprintf;
#else
# define gettext(STR) (STR)
# define gettext_noop(STR) (STR)
# define _(STR) (STR)
# define N_(STR) (STR)
#endif
#endif

745
encfs/main.cpp Normal file
View File

@ -0,0 +1,745 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2003-2004, 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 "autosprintf.h"
#include <iostream>
#include <string>
#include <sstream>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <rlog/rlog.h>
#include <rlog/Error.h>
#include <rlog/RLogChannel.h>
#include <rlog/SyslogNode.h>
#include <rlog/StdioNode.h>
#include "ConfigReader.h"
#include "Interface.h"
#include "MemoryPool.h"
#include "FileUtils.h"
#include "DirNode.h"
#include "Context.h"
#include "openssl.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);
# define fuse_unmount fuse_unmount_compat22
#include <locale.h>
#include "i18n.h"
#ifndef MAX
inline static int MAX(int a, int b)
{
return (a > b) ? a : b;
}
#endif
using namespace std;
using namespace rlog;
using namespace rel;
using namespace gnu;
using boost::shared_ptr;
using boost::scoped_ptr;
// Maximum number of arguments that we're going to pass on to fuse. Doesn't
// affect how many arguments we can handle, just how many we can pass on..
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;
shared_ptr<EncFS_Opts> 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->reverseEncryption) ss << "(reverseEncryption) ";
if(opts->mountOnDemand) ss << "(mountOnDemand) ";
for(int i=0; i<fuseArgc; ++i)
ss << fuseArgv[i] << ' ';
return ss.str();
}
EncFS_Args()
: opts( new EncFS_Opts() )
{
}
};
static int oldStderr = STDERR_FILENO;
static
void usage(const char *name)
{
// xgroup(usage)
cerr << autosprintf( _("Build: encfs version %s"), VERSION )
<< "\n\n"
// xgroup(usage)
<< autosprintf(_("Usage: %s [options] rootDir mountPoint [-- [FUSE Mount Options]]"), name) << "\n\n"
// xgroup(usage)
<< _("Common Options:\n"
" -H\t\t\t" "show optional FUSE Mount Options\n"
" -s\t\t\t" "disable multithreaded operation\n"
" -f\t\t\t" "run in foreground (don't spawn daemon).\n"
"\t\t\tError messages will be sent to stderr\n"
"\t\t\tinstead of syslog.\n")
// xgroup(usage)
<< _(" -v, --verbose\t\t" "verbose: output encfs debug messages\n"
" -i, --idle=MINUTES\t""Auto unmount after period of inactivity\n"
" --anykey\t\t" "Do not verify correct key is being used\n"
" --forcedecode\t\t" "decode data even if an error is detected\n"
"\t\t\t(for filesystems using MAC block headers)\n")
<< _(" --public\t\t" "act as a typical multi-user filesystem\n"
"\t\t\t(encfs must be run as root)\n")
<< _(" --reverse\t\t" "reverse encryption\n")
// xgroup(usage)
<< _(" --extpass=program\tUse external program for password prompt\n"
"\n"
"Example, to mount at ~/crypt with raw storage in ~/.crypt :\n"
" encfs ~/.crypt ~/crypt\n"
"\n")
// xgroup(usage)
<< _("For more information, see the man page encfs(1)") << "\n"
<< endl;
}
static
void FuseUsage()
{
// xgroup(usage)
cerr << _("encfs [options] rootDir mountPoint -- [FUSE Mount Options]\n"
"valid FUSE Mount Options follow:\n") << endl;
int argc = 2;
const char *argv[] = {"...", "-h"};
fuse_main( argc, const_cast<char**>(argv), (fuse_operations*)NULL, NULL);
}
#define PUSHARG(ARG) \
rAssert(out->fuseArgc < MaxFuseArgs); \
out->fuseArgv[out->fuseArgc++] = ARG
static
string slashTerminate( const string &src )
{
string result = src;
if( result[ result.length()-1 ] != '/' )
result.append( "/" );
return result;
}
static
bool processArgs(int argc, char *argv[], const shared_ptr<EncFS_Args> &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->reverseEncryption = false;
bool useDefaultFlags = true;
// pass executable name through
out->fuseArgv[0] = lastPathElement(argv[0]);
++out->fuseArgc;
// 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", 1, 0, 'S'}, // read password from stdin
{"verbose", 0, 0, 'v'}, // verbose mode
{"version", 0, 0, 'V'}, //version
{"reverse", 0, 0, 'r'}, // reverse encryption
{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, "HsSfvdmi:o:",
long_options, &option_index);
if(res == -1)
break;
switch( res )
{
case 's':
out->isThreaded = false;
break;
case 'S':
out->opts->useStdin = 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;
}
}
if(!out->isThreaded)
PUSHARG("-s");
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
rWarning(_("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)
{
rAssert(out->fuseArgc < MaxFuseArgs);
out->fuseArgv[out->fuseArgc++] = argv[optind];
++optind;
}
}
// 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 )
{
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->rootDir.c_str() ,0700))
{
rWarning(_("Unable to locate root directory, aborting."));
return false;
}
if(!isDirectory( out->mountPoint.c_str() ) &&
!userAllowMkdir( 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;
}
static void * idleMonitor(void *);
void *encfs_init(fuse_conn_info *conn)
{
EncFS_Context *ctx = (EncFS_Context*)fuse_get_context()->private_data;
// 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)
{
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);
}
}
if(ctx->args->isDaemon && oldStderr >= 0)
{
rInfo("Closing stderr");
close(oldStderr);
oldStderr = -1;
}
return (void*)ctx;
}
void encfs_destroy( void *_ctx )
{
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");
}
}
int main(int argc, char *argv[])
{
// initialize the logging library
RLogInit( argc, argv );
#ifdef LOCALEDIR
setlocale( LC_ALL, "" );
bindtextdomain( PACKAGE, LOCALEDIR );
textdomain( PACKAGE );
#endif
// log to stderr by default..
scoped_ptr<StdioNode> slog( new StdioNode( STDERR_FILENO ) );
scoped_ptr<SyslogNode> logNode;
// show error and warning output
slog->subscribeTo( GetGlobalChannel("error") );
slog->subscribeTo( GetGlobalChannel("warning") );
// anything that comes from the user should be considered tainted until
// we've processed it and only allowed through what we support.
shared_ptr<EncFS_Args> encfsArgs( new EncFS_Args );
for(int i=0; i<MaxFuseArgs; ++i)
encfsArgs->fuseArgv[i] = NULL; // libfuse expects null args..
if(argc == 1 || !processArgs(argc, argv, encfsArgs))
{
usage(argv[0]);
return EXIT_FAILURE;
}
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));
encfs_oper.getattr = encfs_getattr;
encfs_oper.readlink = encfs_readlink;
encfs_oper.getdir = encfs_getdir;
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;
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;
#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;
#ifdef HAVE_ULOCKMGR_H
encfs_oper.lock = encfs_lock;
#endif // HAVE_ULOCKMGR_H
encfs_oper.utimens = encfs_utimens;
//encfs_oper.bmap = encfs_bmap;
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;
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)
{
// 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;
// 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<char**>(encfsArgs->fuseArgv),
&encfs_oper, (void*)ctx);
time( &endTime );
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"));
}
}
// cleanup so that we can check for leaked resources..
rootInfo.reset();
ctx->setRoot( shared_ptr<DirNode>() );
MemoryPool::destroyAll();
openssl_shutdown( encfsArgs->isThreaded );
return returnCode;
}
/*
Idle monitoring thread. This is only used when idle monitoring is enabled.
It will cause the filesystem to be automatically unmounted (causing us to
commit suicide) if the filesystem stays idle too long. Idle time is only
checked if there are no open files, as I don't want to risk problems by
having the filesystem unmounted from underneath open files!
*/
const int ActivityCheckInterval = 10;
static bool unmountFS(EncFS_Context *ctx);
static
void * idleMonitor(void *_arg)
{
EncFS_Context *ctx = (EncFS_Context*)_arg;
shared_ptr<EncFS_Args> arg = ctx->args;
const int timeoutCycles = 60 * arg->idleTimeout / ActivityCheckInterval;
int idleCycles = 0;
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 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( &currentTime, 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 );
rDebug("Idle monitoring thread exiting");
return 0;
}
static bool unmountFS(EncFS_Context *ctx)
{
shared_ptr<EncFS_Args> arg = ctx->args;
if( arg->opts->mountOnDemand )
{
rDebug("Detaching filesystem %s due to inactivity",
arg->mountPoint.c_str());
ctx->setRoot( shared_ptr<DirNode>() );
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;
}
}

109
encfs/openssl.cpp Normal file
View File

@ -0,0 +1,109 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2007, Valient Gough
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "openssl.h"
#include <pthread.h>
#include <rlog/rlog.h>
#define NO_DES
#include <openssl/ssl.h>
#include <openssl/rand.h>
#ifndef OPENSSL_NO_ENGINE
#include <openssl/engine.h>
#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<CRYPTO_num_locks(); ++i)
pthread_mutex_init( crypto_locks+i, 0 );
}
if(mode & CRYPTO_LOCK)
{
pthread_mutex_lock( crypto_locks + n );
} else
{
pthread_mutex_unlock( crypto_locks + n );
}
}
void pthreads_locking_cleanup()
{
if(crypto_locks)
{
for(int i=0; i<CRYPTO_num_locks(); ++i)
pthread_mutex_destroy( crypto_locks+i );
delete[] crypto_locks;
crypto_locks = NULL;
}
}
void openssl_init(bool threaded)
{
// initialize the SSL library
SSL_load_error_strings();
SSL_library_init();
unsigned int randSeed = 0;
RAND_bytes( (unsigned char*)&randSeed, sizeof(randSeed) );
srand( randSeed );
#ifndef OPENSSL_NO_ENGINE
/* Load all bundled ENGINEs into memory and make them visible */
ENGINE_load_builtin_engines();
/* Register all of them for every algorithm they collectively implement */
ENGINE_register_all_complete();
#endif // NO_ENGINE
if(threaded)
{
// provide locking functions to OpenSSL since we'll be running with
// threads accessing openssl in parallel.
CRYPTO_set_id_callback( pthreads_thread_id );
CRYPTO_set_locking_callback( pthreads_locking_callback );
}
}
void openssl_shutdown(bool threaded)
{
#ifndef OPENSSL_NO_ENGINE
ENGINE_cleanup();
#endif
if(threaded)
pthreads_locking_cleanup();
}

29
encfs/openssl.h Normal file
View File

@ -0,0 +1,29 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* Copyright (c) 2007, Valient Gough
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _openssl_incl_
#define _openssl_incl_
void openssl_init(bool isThreaded);
void openssl_shutdown(bool isThreaded);
#endif

195
encfs/readpassphrase.cpp Normal file
View File

@ -0,0 +1,195 @@
/* $OpenBSD: readpassphrase.c,v 1.12 2001/12/15 05:41:00 millert Exp $ */
/*
* Copyright (c) 2000 Todd C. Miller <Todd.Miller@courtesan.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
*/
#if defined(LIBC_SCCS) && !defined(lint)
static const char rcsid[] = "$OpenBSD: readpassphrase.c,v 1.12 2001/12/15 05:41:00 millert Exp $";
#endif /* LIBC_SCCS and not lint */
//#include "includes.h"
#ifndef HAVE_READPASSPHRASE
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <paths.h>
#include <string.h>
#include <ctype.h>
#include <termios.h>
#include <readpassphrase.h>
#ifdef TCSASOFT
# define _T_FLUSH (TCSAFLUSH|TCSASOFT)
#else
# define _T_FLUSH (TCSAFLUSH)
#endif
/* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */
#if !defined(_POSIX_VDISABLE) && defined(VDISABLE)
# define _POSIX_VDISABLE VDISABLE
#endif
static volatile sig_atomic_t signo;
static void handler(int);
char *
readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
{
ssize_t nr;
int input, output, save_errno;
char ch, *p, *end;
struct termios term, oterm;
struct sigaction sa, saveint, savehup, savequit, saveterm;
struct sigaction savetstp, savettin, savettou;
/* I suppose we could alloc on demand in this case (XXX). */
if (bufsiz == 0) {
errno = EINVAL;
return(NULL);
}
restart:
/*
* Read and write to /dev/tty if available. If not, read from
* stdin and write to stderr unless a tty is required.
*/
if ((input = output = open(_PATH_TTY, O_RDWR)) == -1) {
if (flags & RPP_REQUIRE_TTY) {
errno = ENOTTY;
return(NULL);
}
input = STDIN_FILENO;
output = STDERR_FILENO;
}
/*
* Catch signals that would otherwise cause the user to end
* up with echo turned off in the shell. Don't worry about
* things like SIGALRM and SIGPIPE for now.
*/
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0; /* don't restart system calls */
sa.sa_handler = handler;
(void)sigaction(SIGINT, &sa, &saveint);
(void)sigaction(SIGHUP, &sa, &savehup);
(void)sigaction(SIGQUIT, &sa, &savequit);
(void)sigaction(SIGTERM, &sa, &saveterm);
(void)sigaction(SIGTSTP, &sa, &savetstp);
(void)sigaction(SIGTTIN, &sa, &savettin);
(void)sigaction(SIGTTOU, &sa, &savettou);
/* Turn off echo if possible. */
if (tcgetattr(input, &oterm) == 0) {
memcpy(&term, &oterm, sizeof(term));
if (!(flags & RPP_ECHO_ON))
term.c_lflag &= ~(ECHO | ECHONL);
#ifdef VSTATUS
if (term.c_cc[VSTATUS] != _POSIX_VDISABLE)
term.c_cc[VSTATUS] = _POSIX_VDISABLE;
#endif
(void)tcsetattr(input, _T_FLUSH, &term);
} else {
memset(&term, 0, sizeof(term));
memset(&oterm, 0, sizeof(oterm));
}
(void)write(output, prompt, strlen(prompt));
end = buf + bufsiz - 1;
for (p = buf; (nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r';) {
if (p < end) {
if ((flags & RPP_SEVENBIT))
ch &= 0x7f;
if (isalpha(ch)) {
if ((flags & RPP_FORCELOWER))
ch = tolower(ch);
if ((flags & RPP_FORCEUPPER))
ch = toupper(ch);
}
*p++ = ch;
}
}
*p = '\0';
save_errno = errno;
if (!(term.c_lflag & ECHO))
(void)write(output, "\n", 1);
/* Restore old terminal settings and signals. */
if (memcmp(&term, &oterm, sizeof(term)) != 0)
(void)tcsetattr(input, _T_FLUSH, &oterm);
(void)sigaction(SIGINT, &saveint, NULL);
(void)sigaction(SIGHUP, &savehup, NULL);
(void)sigaction(SIGQUIT, &savequit, NULL);
(void)sigaction(SIGTERM, &saveterm, NULL);
(void)sigaction(SIGTSTP, &savetstp, NULL);
(void)sigaction(SIGTTIN, &savettin, NULL);
(void)sigaction(SIGTTOU, &savettou, NULL);
if (input != STDIN_FILENO)
(void)close(input);
/*
* If we were interrupted by a signal, resend it to ourselves
* now that we have restored the signal handlers.
*/
if (signo) {
kill(getpid(), signo);
switch (signo) {
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
signo = 0;
goto restart;
}
}
errno = save_errno;
return(nr == -1 ? NULL : buf);
}
#endif /* HAVE_READPASSPHRASE */
#if 0
char *
getpass(const char *prompt)
{
static char buf[_PASSWORD_LEN + 1];
return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF));
}
#endif
static void handler(int s)
{
signo = s;
}

52
encfs/readpassphrase.h Normal file
View File

@ -0,0 +1,52 @@
/* $OpenBSD: readpassphrase.h,v 1.1 2000/11/21 00:48:38 millert Exp $ */
/*
* Copyright (c) 2000 Todd C. Miller <Todd.Miller@courtesan.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
*/
#ifndef _READPASSPHRASE_H_
#define _READPASSPHRASE_H_
//#include "includes.h"
#include <sys/types.h>
#ifndef HAVE_READPASSPHRASE
#define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */
#define RPP_ECHO_ON 0x01 /* Leave echo on. */
#define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */
#define RPP_FORCELOWER 0x04 /* Force input to lower case. */
#define RPP_FORCEUPPER 0x08 /* Force input to upper case. */
#define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */
#ifdef __cplusplus
extern "C"
#endif
char *readpassphrase(const char *prompt, char *buf, size_t bufSize, int flags);
#endif /* HAVE_READPASSPHRASE */
#endif /* !_READPASSPHRASE_H_ */

543
encfs/test.cpp Normal file
View File

@ -0,0 +1,543 @@
/*****************************************************************************
* Author: Valient Gough <vgough@pobox.com>
*
*****************************************************************************
* 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 <iostream>
#include <stdlib.h>
#include "Cipher.h"
#include "DirNode.h"
#include "MemoryPool.h"
#include "Interface.h"
#include "FileUtils.h"
#include "ConfigReader.h"
#include "StreamNameIO.h"
#include "BlockNameIO.h"
#include "NullNameIO.h"
#include <rlog/rlog.h>
#include <rlog/Error.h>
#include <rlog/StdioNode.h>
#include <rlog/RLogChannel.h>
#ifdef HAVE_SSL
#define NO_DES
#include <openssl/ssl.h>
#ifndef OPENSSL_NO_ENGINE
#include <openssl/engine.h>
#endif
#endif
using namespace std;
using namespace rel;
using namespace rlog;
using boost::shared_ptr;
const int FSBlockSize = 256;
static
int checkErrorPropogation( const shared_ptr<Cipher> &cipher,
int size, int byteToChange, const CipherKey &key )
{
MemBlock orig = MemoryPool::allocate(size);
MemBlock data = MemoryPool::allocate(size);
for(int i=0; i<size; ++i)
{
unsigned char tmp = rand();
orig.data[i] = tmp;
data.data[i] = tmp;
}
if(size != FSBlockSize)
cipher->streamEncode( 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<size; ++i)
{
if( data.data[i] != orig.data[i] )
++numByteErrors;
}
MemoryPool::release( data );
MemoryPool::release( orig );
return numByteErrors;
}
const char TEST_ROOTDIR[] = "/foo";
static
bool testNameCoding( DirNode &dirNode, bool verbose )
{
// encrypt a name
const char *name[] = {
"1234567",
"12345678",
"123456789",
"123456789ABCDEF",
"123456789ABCDEF0",
"123456789ABCDEF01",
"test-name",
"test-name2",
"test",
"../test",
"/foo/bar/blah",
"test-name.21",
"test-name.22",
"test-name.o",
"1.test",
"2.test",
"a/b/c/d",
"a/c/d/e",
"b/c/d/e",
"b/a/c/d",
NULL
};
const char **orig = name;
while(*orig)
{
if(verbose)
cerr << " coding name \"" << *orig << "\"";
string encName = dirNode.relativeCipherPath( *orig );
if(verbose)
cerr << " -> \"" << 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++;
}
return true;
}
bool runTests(const shared_ptr<Cipher> &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;
}
delete[] keyBuf;
}
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.cipherIface = cipher->interface();
cfg.keySize = cipher->keySize();
cfg.blockSize = FSBlockSize;
cfg.keyData.assign( (char*)keyBuf, encodedKeySize );
// save config
ConfigReader cfgRdrOut;
cfgRdrOut["cipher"] << cfg.cipherIface;
cfgRdrOut["keySize"] << cfg.keySize;
cfgRdrOut["blockSize"] << cfg.blockSize;
cfgRdrOut["keyData"] << cfg.keyData;
// save to string data..
ConfigVar storage = cfgRdrOut.toVar();
// read back in and check everything..
ConfigReader cfgRdrIn;
cfgRdrIn.loadFromVar( storage );
EncFSConfig cfg2;
cfgRdrIn["cipher"] >> cfg2.cipherIface;
cfgRdrIn["keySize"] >> cfg2.keySize;
cfgRdrIn["blockSize"] >> cfg2.blockSize;
cfgRdrIn["keyData"] >> cfg2.keyData;
// check..
rAssert( cfg.cipherIface.implements(cfg2.cipherIface) );
rAssert( cfg.keySize == cfg2.keySize );
rAssert( cfg.blockSize == cfg2.blockSize );
rAssert( cfg.keyData == cfg2.keyData );
// try decoding key..
CipherKey key2 = cipher->readKey( (unsigned char*)cfg2.keyData.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;
}
delete[] keyBuf;
}
shared_ptr<DirNode::Config> dnConfig( new DirNode::Config );
dnConfig->cipher = cipher;
dnConfig->key = key;
dnConfig->blockSize = FSBlockSize;
if(verbose)
cerr << "Testing name encode/decode (stream coding w/ IV chaining)\n";
{
dnConfig->inactivityTimer = false;
dnConfig->blockMACBytes = 0;
dnConfig->blockMACRandBytes = 0;
dnConfig->uniqueIV = false;
dnConfig->nameCoding = shared_ptr<NameIO>( new StreamNameIO(
StreamNameIO::CurrentInterface(), cipher, key ) );
dnConfig->nameCoding->setChainedNameIV( true );
DirNode dirNode( NULL, TEST_ROOTDIR, dnConfig );
if(!testNameCoding( dirNode, verbose ))
return false;
}
if(verbose)
cerr << "Testing name encode/decode (block coding w/ IV chaining)\n";
{
dnConfig->inactivityTimer = false;
dnConfig->blockMACBytes = 0;
dnConfig->blockMACRandBytes = 0;
dnConfig->uniqueIV = false;
dnConfig->nameCoding = shared_ptr<NameIO>( new BlockNameIO(
BlockNameIO::CurrentInterface(), cipher, key,
cipher->cipherBlockSize() ) );
dnConfig->nameCoding->setChainedNameIV( true );
DirNode dirNode( NULL, TEST_ROOTDIR, dnConfig );
if(!testNameCoding( dirNode, verbose ))
return false;
}
if(!verbose)
{
{
// test stream mode, this time without IV chaining
dnConfig->inactivityTimer = false;
dnConfig->blockMACBytes = 0;
dnConfig->blockMACRandBytes = 0;
dnConfig->uniqueIV = false;
dnConfig->nameCoding =
shared_ptr<NameIO>( new StreamNameIO(
StreamNameIO::CurrentInterface(), cipher, key ) );
dnConfig->nameCoding->setChainedNameIV( false );
DirNode dirNode( NULL, TEST_ROOTDIR, dnConfig );
if(!testNameCoding( dirNode, verbose ))
return false;
}
{
// test block mode, this time without IV chaining
dnConfig->inactivityTimer = false;
dnConfig->blockMACBytes = 0;
dnConfig->blockMACRandBytes = 0;
dnConfig->uniqueIV = false;
dnConfig->nameCoding = shared_ptr<NameIO>( new BlockNameIO(
BlockNameIO::CurrentInterface(), cipher, key,
cipher->cipherBlockSize() ) );
dnConfig->nameCoding->setChainedNameIV( false );
DirNode dirNode( NULL, TEST_ROOTDIR, dnConfig );
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<FSBlockSize-1; ++i)
{
int numErrors = checkErrorPropogation( cipher,
FSBlockSize-1, i, key );
if(numErrors < minChanges)
{
minChanges = numErrors;
minAt = i;
}
if(numErrors > 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<FSBlockSize; ++i)
{
int numErrors = checkErrorPropogation( cipher,
FSBlockSize, i, key );
if(numErrors < minChanges)
{
minChanges = numErrors;
minAt = i;
}
if(numErrors > 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<Cipher::CipherAlgorithm> algorithms =
Cipher::GetAlgorithmList();
std::list<Cipher::CipherAlgorithm>::const_iterator it;
cerr << "Supported Crypto interfaces:\n";
for(it = algorithms.begin(); it != algorithms.end(); ++it)
{
cerr << it->name
<< " ( " << it->iface.name() << " "
<< it->iface.current() << ":"
<< it->iface.revision() << ":"
<< 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 = 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 = 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;
}

45
encfs/testextpass Normal file
View File

@ -0,0 +1,45 @@
#!/usr/bin/perl -w
# Sample password prompt program. Demonstration of how to interface with
# encfs's --extpass option. Note that encfs's extpass interface was chosen to
# work with existing password prompt programs, like ssh-askpass.
#
# The external password program is expected to somehow prompt for the password
# and return the password on stdout. Encfs also provides some extra
# information which is stored in environment variables.
#
# Quick Start: The only necessary part of this script for encfs operation is
# the final part which prints the password to stdout.
#
# Encfs records some environment variables with useful information:
# "encfs_root" holds a path to the raw encrypted data.
# "encfs_stdout" holds a file descriptor number which will display data to
# standard output (because standard output is used for the password).
# "encfs_stderr" holds a file descriptor number which for standard error..
use strict;
use IO::Handle;
# find out where we can send data to display to the user (assuming encfs was
# started in a terminal!)
my $realOut = $ENV{"encfs_stdout"} || fileno(STDOUT);
#my $realErr = $ENV{"encfs_stderr"} || fileno(STDERR);
#my $rootDir = $ENV{"encfs_root"} || "[unknown]";
#system("xmessage realOut=$realOut, err=$realErr");
# for grins, show all encfs related environment variables to encfs's standard
# output
my $io = new IO::Handle;
if($io->fdopen($realOut, "w"))
{
while (my ($key,$value) = each %ENV)
{
$io->print("$key=$value\n") if($key=~/encfs/);
}
}
$io->close();
## XXX XXX - This is the only part necessary for encfs to be happy.
# the functional part -- print the encfs password to stdout
print "test\n";

6402
ltmain.sh Normal file

File diff suppressed because it is too large Load Diff

10
makedist.sh Normal file
View File

@ -0,0 +1,10 @@
#!/bin/sh
echo Creating autoconf scripts...
sh ./reconfig.sh
echo Configuring...
./configure
sh ./makedist2.sh

27
makedist2.sh.in Normal file
View File

@ -0,0 +1,27 @@
#!/bin/sh
# create distribution file
make dist
# create tar archive and signature
tarArchive=@PACKAGE@-@VERSION@-@RELEASE@.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

6
reconfig.sh Normal file
View File

@ -0,0 +1,6 @@
#!/bin/sh
aclocal
autoheader
automake -a
autoconf

3
subdirs Normal file
View File

@ -0,0 +1,3 @@
admin
autom4te.cache
encfs