mirror of
https://github.com/vgough/encfs.git
synced 2024-11-25 01:13:12 +01:00
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:
parent
b676408ce1
commit
4fc836f032
8
AUTHORS
Normal file
8
AUTHORS
Normal 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
674
COPYING
Normal 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>.
|
23
INSTALL
Normal file
23
INSTALL
Normal 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
17
Makefile.am
Normal 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
2
Makefile.common
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
KDE_OPTIONS = qtonly
|
||||||
|
|
8
Makefile.dist
Normal file
8
Makefile.dist
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
default: all
|
||||||
|
|
||||||
|
all:
|
||||||
|
aclocal
|
||||||
|
autoheader
|
||||||
|
automake
|
||||||
|
autoconf
|
||||||
|
|
132
README
Normal file
132
README
Normal 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
71
README-NLS
Normal 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
7
TRANSLATORS
Normal 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
3
acinclude.m4
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
m4_include([acx_pthread.m4])
|
||||||
|
|
199
acx_pthread.m4
Normal file
199
acx_pthread.m4
Normal 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
187
configure.ac
Normal 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
32
dk2ChangeLog
Normal 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
207
encfs.spec.in
Normal 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
431
encfs/BlockFileIO.cpp
Normal 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
69
encfs/BlockFileIO.h
Normal 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
213
encfs/BlockNameIO.cpp
Normal 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
65
encfs/BlockNameIO.h
Normal 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
208
encfs/Cipher.cpp
Normal 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
151
encfs/Cipher.h
Normal 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
424
encfs/CipherFileIO.cpp
Normal 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
85
encfs/CipherFileIO.h
Normal 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
30
encfs/CipherKey.cpp
Normal 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
36
encfs/CipherKey.h
Normal 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
159
encfs/ConfigReader.cpp
Normal 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
63
encfs/ConfigReader.h
Normal 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
247
encfs/ConfigVar.cpp
Normal 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
80
encfs/ConfigVar.h
Normal 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
182
encfs/Context.cpp
Normal 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
106
encfs/Context.h
Normal 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
808
encfs/DirNode.cpp
Normal 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
210
encfs/DirNode.h
Normal 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
38
encfs/FileIO.cpp
Normal 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
84
encfs/FileIO.h
Normal 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
296
encfs/FileNode.cpp
Normal 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
104
encfs/FileNode.h
Normal 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
1438
encfs/FileUtils.cpp
Normal file
File diff suppressed because it is too large
Load Diff
161
encfs/FileUtils.h
Normal file
161
encfs/FileUtils.h
Normal 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
226
encfs/Interface.cpp
Normal 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
84
encfs/Interface.h
Normal 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 ¤t();
|
||||||
|
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
270
encfs/MACFileIO.cpp
Normal 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
69
encfs/MACFileIO.h
Normal 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
138
encfs/Makefile.am
Normal 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
151
encfs/MemoryPool.cpp
Normal 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
54
encfs/MemoryPool.h
Normal 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
62
encfs/Mutex.h
Normal 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
348
encfs/NameIO.cpp
Normal 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
136
encfs/NameIO.h
Normal 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
177
encfs/NullCipher.cpp
Normal 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
80
encfs/NullCipher.h
Normal 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
86
encfs/NullNameIO.cpp
Normal 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
49
encfs/NullNameIO.h
Normal 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
110
encfs/Range.h
Normal 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
322
encfs/RawFileIO.cpp
Normal 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
61
encfs/RawFileIO.h
Normal 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
816
encfs/SSL_Cipher.cpp
Normal 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
141
encfs/SSL_Cipher.h
Normal 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
205
encfs/StreamNameIO.cpp
Normal 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
57
encfs/StreamNameIO.h
Normal 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
164
encfs/base64.cpp
Normal 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
55
encfs/base64.h
Normal 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
4
encfs/docs/Makefile.am
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
####### kdevelop will overwrite this part!!! (begin)##########
|
||||||
|
|
||||||
|
|
||||||
|
####### kdevelop will overwrite this part!!! (end)############
|
4
encfs/docs/en/Makefile.am
Normal file
4
encfs/docs/en/Makefile.am
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
####### kdevelop will overwrite this part!!! (begin)##########
|
||||||
|
|
||||||
|
|
||||||
|
####### kdevelop will overwrite this part!!! (end)############
|
765
encfs/encfs.cpp
Normal file
765
encfs/encfs.cpp
Normal 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
104
encfs/encfs.h
Normal 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
464
encfs/encfs.pod
Normal 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
708
encfs/encfsctl.cpp
Normal 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
99
encfs/encfsctl.pod
Normal 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
67
encfs/encfssh
Normal 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
41
encfs/i18n.h
Normal 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
745
encfs/main.cpp
Normal 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( ¤tTime, 0 );
|
||||||
|
struct timespec wakeupTime;
|
||||||
|
wakeupTime.tv_sec = currentTime.tv_sec + ActivityCheckInterval;
|
||||||
|
wakeupTime.tv_nsec = currentTime.tv_usec * 1000;
|
||||||
|
pthread_cond_timedwait( &ctx->wakeupCond,
|
||||||
|
&ctx->wakeupMutex, &wakeupTime );
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock( &ctx->wakeupMutex );
|
||||||
|
|
||||||
|
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
109
encfs/openssl.cpp
Normal 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
29
encfs/openssl.h
Normal 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
195
encfs/readpassphrase.cpp
Normal 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
52
encfs/readpassphrase.h
Normal 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
543
encfs/test.cpp
Normal 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
45
encfs/testextpass
Normal 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";
|
||||||
|
|
10
makedist.sh
Normal file
10
makedist.sh
Normal 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
27
makedist2.sh.in
Normal 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
6
reconfig.sh
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
aclocal
|
||||||
|
autoheader
|
||||||
|
automake -a
|
||||||
|
autoconf
|
||||||
|
|
Loading…
Reference in New Issue
Block a user