mirror of
https://github.com/vgough/encfs.git
synced 2024-11-21 15:33:16 +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