Branched r1161 trunk for 0.3.5 release

This commit is contained in:
frisco 2009-06-25 02:11:30 +00:00
parent d0ed91ffa8
commit 1d1139428b
802 changed files with 135155 additions and 0 deletions

340
staging-v0.3.5/LICENSE Normal file
View file

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) 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
this service 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 make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. 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.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
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
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the 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 a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE 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.
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
convey 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 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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision 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, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This 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 Library General
Public License instead of this License.

View file

@ -0,0 +1,15 @@
include README*
include LICENSE
include changelog
include copyright
graft man
graft scripts
graft doc
graft conf-examples
graft event-scripts
graft www-data
graft templates
graft lang
graft plugins
graft intl
prune package.exclude

199
staging-v0.3.5/README Normal file
View file

@ -0,0 +1,199 @@
********************************************
* CryptoBox v0.3.xx *
********************************************
$Id$
This file describes the webserver CryptoBox.
The CryptoBox enables you to control the plaintext or encrypted harddisks of
your server via a webinterface.
Read on if you want to install the CryptoBox-server package on your computer.
For more information, see the website:
http://cryptobox.org
Table of contents:
1) Requirements
2) Installation
3) Setup
4) Usage
5) Data access
6) Development
7) Acknowledgements
8) Licence
--------------------------------------------
1) Requirements
- Linux 2.6
- super (to selectively gain root privileges)
- Python 2.4
- some python packages:
clearsilver 0.10 for python
python-configobj 4.x
cherrypy 2.x
2) Installation
For Debian, Ubuntu and other derivates you should use the debian package:
see http://systemausfall.org/toolforge/debian/
Please follow the /usr/share/doc/cryptobox-server/README.Debian for
any special steps regarding Debian.
There are currently no official rpm or other packages of the CryptoBox.
Use the source installation on non-deb based distributions.
For source installation follow these steps:
Get the source:
http://cryptobox.org/download
Extract tarball and change to the new directory:
tar xzf cryptobox-0.?.?.tar.gz
Install the program:
python setup.by install
Fulfil the requirements:
read more in the user documentation
The installed pyhton modules can be found in your local python installation directory.
The default location should be:
/usr/lib/python2.4/site-packages/cryptobox/
The data files are (by default) installed to:
/usr/share/cryptobox-server/
As some actions of the cryptobox require root privileges, you have to add the
following line to /etc/super.tab:
CryptoBoxRootActions /usr/bin/CryptoBoxRootActions cryptobox
The script /usr/bin/CryptoBoxRootActions is used to execute all actions
requiring root privileges. Please check it to make sure, that your system will
not get compromised.
3) Setup
a) Start at bootup
Set NO_START in /etc/default/cryptobox-server to "0".
The CryptoBox webserver will get started by its runlevel control script
after bootup.
b) Define managed devices
You may restrict which blockdevices should be accessible to the CryptoBox.
Simply set [Main]->AllowedDevices in /etc/cryptobox-server/cryptobox.conf
to a comma separated list of device prefixes: e.g. /dev/sd gives access to
all SCSI devices, while /dev/hda3 restricts it to this single partition.
The user executing the webserver (by default: 'cryptobox') must have write
access to these devices. Usually the cryptobox user is member of the 'disk'
group. This gives control over most devices.
Be careful with this setting, as you may expose important data to public
read and write access.
c) Listening port and interface
By default, the CryptoBox webserver listens to tcp port 8080 on all network
interfaces. You can change this setting in /etc/default/cryptobox-server.
Also take a look at your firewall settings.
d) Disable plugins
The CryptoBox contains a lot of plugins. As some of them could expose
unwanted features to your users, you should carefully select which plugins
to disable.
Quite likely candidates for disabling are:
- shutdown: poweroff or reboot the computer
- network: change IP, gateway or dns settings of the server
- partition: partition blockdevices
- volume_format_fs: format a disk/partition (plaintext/encrypted)
Take a look at /usr/share/cryptobox-server/plugins for the list of
other plugins.
The setting [Main]->DisabledPlugins in /etc/cryptobox-server/cryptobox.conf
is a comma separated list of plugin names. Capitalization is important!
e) Separate configuration partition
The CryptoBox webserver requires a writeable directory for proper
operation. If your root filesystem is not writeable (e.g. booting from a
cdrom, read-only mounted flash memory, ...) you may use a seperated
partition to store runtime settings. The CryptoBox will automatically
creates it, when you use partition one of your disks with its interface.
The setting [Main]->UseConfigPartition (see
/etc/cryptobox-server/cryptobox.conf) defines, whether you want to use a
separate partition (value "1") or if you want to store your runtime
settings in the root filesystem (typically below
/var/cache/cryptobox-server).
f) Samba/WebDAV/NFS/??? integration (aka. event script handling)
The CryptoBox allows you to add event handling scripts for most of the
interesting events: bootup/shutdown of the webserver and mount/umount
of single volumes.
If you want to automatically publish your mounted volumes with samba
or similar fileservers, then you should take a closer look at the
example scripts for samba and apache-webdav in
/usr/share/doc/cryptobox-server/event-scripts.
You may also just publish the mount directory of the CryptoBox. This
will expose all mounted volumes very easily. Review the configuration
file for the setting [Locations]->MountParentDir.
g) Take a close look at the configuration file to check all other options
before you start the CryptoBox webserver.
4) Usage
Use your favourite web browser to go to http://localhost:8080 and browse the
webinterface of the CryptoBox.
Some parts of the interface are restricted to administrative access. The
default access combination is the user 'admin' and the password 'admin'. Please
change this setting immediately.
The plugin 'user_manager' allows you to add users and to change passwords.
The plugin 'plugin_manager' lets you configure, which plugins require
administrative authentication.
5) Data access
Before you can access your plaintext or encrypted data on a volume, you have to
open it. To accomplish this, you have to go to the webinterface, select the
appropriate volume and click on "Open volume" in the "Activation" tab.
If you are running the CryptoBox locally, then you can access all open volumes
below the 'MountDir' as specified in the configuration file
(/etc/cryptobox-server/cryptobox.conf). The default mount location is
/var/cache/cryptobox/mnt.
If the CryptoBox package is running on a networking server, then you have to
configure your favourite fileserver (e.g. samba, webdav, nfs, ftp, ...) to
publish the subdirectories of the 'MountDir' (see above).
If you want to customize the publishing of volumes, then you may use the
event script feature fo the CryptoBox. See event-scripts/README for details.
6) Development
bug reports: please use our issue tracker
https://systemausfall.org/trac/cryptobox/newticket
email:
info@cryptobox.org
The CryptoBox project is mainly driven by sense.lab (http://senselab.org).
7) Acknowledgements
Besides the core development team, these people helped a lot:
Clavdia Horvat, Tadej Brce & Dušan Rebolj - Slovenian translation
rike - French translation
Fabrizio Tarizzo - Italian translation
kinneko - Japanese translation
Andrzej S. Kaznowski - Polish translation
Fadrique - Spanish translation
Michiel van Dijk - Dutch translation
Raimar - the blender dragon
Gilles Accad - French translation
ASpr - Russian translation
We also want to thank the numerous developers of the Free Software, the
CryptoBox depends on and that was used in development.
8) License
The code is licensed under the GPL v2.0 or above.
The documentation and all graphics are licenced under "Creative Commons Attribution
Share-Alike 2.5" (http://creativecommons.org/licenses/by-sa/2.5/).
See the file 'copyright' for details.

View file

@ -0,0 +1,47 @@
Integration of apach2 as a (Web)DAV server into the CryptoBox
This file describes how to expose the volumes that are managed by the CryptoBox
through WebDAV shares.
Apache2 including the dav_fs module is the most common server for the WebDAV
filesystem. The following description will focus on this server.
First you have to install apache2 and the dav_fs module.
Use your favourite package manager to install them.
(Note for debian: the dav_fs module is part of the apache2-common package.
Just activate the module via 'a2enmod dav_fs'.)
There are two different ways to do use dav shares:
A) one share for all volumes together
Just create a file with the following lines to your /etc/apache2/conf.d directory:
Alias "/cryptobox" "/var/cache/cryptobox-server/mnt"
<Location "/cryptobox">
Dav filesystem
</Location>
Reload the new apache2 configuration by calling:
invoke-rc.d apache2 reload
B) one share for each volume
Copy the example event script
/usr/share/doc/cryptobox-server/event-script/apache2_dav to
/etc/cryptobox-server/events.d/apache2_dav. This event handler will add and remove
shares whenever a volume is mounted or unmounted via the CryptoBox webinterface.
Copy the file /usr/share/doc/cryptobox-server/conf-examples/apache2_dav.conf to
/etc/apache2/conf.d/apache2_dav.
Create a directory for the apache share config files:
mkdir -p /var/cache/cryptobox-server/apache2_dav.conf.d
Chown it to the cryptobox user:
chown cryptobox /var/cache/cryptobox-server/apache2_dav.conf.d
Reload the new apache2 configuration by calling:
invoke-rc.d apache2 reload

View file

@ -0,0 +1,66 @@
Running the CryptoBox behind a proxy
This describes how to setup the CryptoBox webserver behind a proxy webserver
(e.g.: apache or lighttpd).
-=-=-=- apache in front of the cryptobox-server (cherrypy) -=-=-=-
The following section describes how to configure an apache2 webserver to
forward requests to the cherrypy server of the CryptoBox.
1) Required modules
- proxy
- headers
Both module should be part of usual default installations of apache2.
Activate these modules. For debian you should run: a2enmod MOD_NAME
2) Configuration directives
The following example should help you to create your own proxy configuration
for apache2.
ProxyRequests Off
<Proxy *>
Order Deny,Allow
Allow from all
</Proxy>
<Location /cryptobox/>
ProxyPass http://localhost:8080/
ProxyPassReverse http://localhost:8080/
RequestHeader set CryptoBox-Location /cryptobox
# uncomment the next line for ssl-enabled virtualhosts
RequestHeader set X-SSL-Request 1
</Location>
Now you should restart apache2.
3) Testing
Now you should point your webserver to the proxy host and check if
the CryptoBox layout ist displayed properly.
-=-=-=- lighttpd in front of the cryptobox-server (cherrypy) -=-=-=-
In this section we do the same as above, but with lighttpd.
Your lighttpd config should contain something like this:
# selecting modules
server.modules = ( "mod_scgi" )
scgi.server = ( "/cryptobox" =>
(( "host" => "127.0.0.1",
"port" => 8080,
"check-local" => "disable"
))
)

View file

@ -0,0 +1,31 @@
Integration of samba into the CryptoBox
This file describes how to expose the volumes that are managed by the CryptoBox
through samba shares.
There are two different ways to do this:
A) one share for all volumes together
Just add the following lines to your /etc/samba/smb.conf:
[cryptobox]
path = /var/cache/cryptobox-server/mnt
browseable = yes
read only = no
guest ok = yes
Reload the new samba configuration by calling:
invoke-rc.d samba reload
B) one share for each volume
Copy the example event script /usr/share/doc/cryptobox-server/event-scripts/samba
to /etc/cryptobox-server/events.d/samba and make sure it is executable
by root. This event handler will add and remove shares whenever a volume is mounted
or unmounted via the CryptoBox webinterface.
Add the following line to your /etc/samba/smb.conf:
include = /var/cache/cryptobox-server/settings/misc/samba-include.conf

113
staging-v0.3.5/README.ssl Normal file
View file

@ -0,0 +1,113 @@
Encrypting the http traffic to the CryptoBox webserver with SSL
This file describes how to encrypt your connection to the CryptoBox webserver.
This is highly recommended as the encryption password for your data could be
exposed to intruders in your local network otherwise.
Below you will find detailed descriptions on how to set up an encrypted
connection to the webinterface:
- use the plugin "encrypted_webinterface"
- run the CryptoBox webserver behind an ssl-enabled webserver
- use stunnel or stunnel4 to provide an SSL socket
- use the a proxy server (e.g. pound)
- ...
At the end of this document you will find some information on how to turn off
SSL detection of the CryptoBox. You should read it, if there is no solution
for your specific setup available or if you are _very_ sure, that you do not
need encrypted http connections.
-------------------------------------------------------------------
1) using the plugin 'encrypted_webinterface'
This plugin is disabled by default. You can enable it in your
cryptobox.conf file by removing it from the 'DisabledPlugins' setting.
The plugin does the following during startup of the CryptoBox:
- create a self-signed X.509 certificate if necessary
- run stunnel4 from port 80 to 443 (https) with this certificate
Of course, this will not work, if the port 443 is already in use by
another program - in this case, you should better choose one of the
solutions described below.
Now, you need to point your browser to the URL of the CryptoBox with
'https' instead of 'http'. Or just follow the "Use encrypted
connection" link that appears, if you use plain http.
For a finer tuned certifacte follow the steps under "CryptoBox
behind stunnel".
-------------------------------------------------------------------
2) CryptoBox behind an ssl-enabled webserver
Read the documentation of your favourite webserver to learn how to enable
ssl encryption.
The CryptoBox webserver cannot detect whether the connection is encrypted
or not since it is behind the proxy webserver and does not share its
environment. Thus you have to tell the CryptoBox in the request header
whether the connection is encrypted or not.
for apache2:
1) enable the 'headers' module (for debian: "a2enmod headers")
2) add this line to your ssl-enabled virtualhost:
RequestHeader set X-SSL-Request 1
3) restart your webserver
-------------------------------------------------------------------
3) CryptoBox behind stunnel (configured manually)
You may want to tunnel the traffic between the cryptobox-server
and your browser. "stunnel" or "stunnel4" are excellent candidates for this job.
If you do not have an ssl certificate yet, then you should create
one first. On Debian: "apt-get install ssl-cert" and run the following
command (the supplied example openssl.conf file resides in the doc
directory of the cryptobox-server package):
make-ssl-cert conf-examples/openssl.conf <CERT_FILE_NAME>
In case, that you already have a certificate just run this command:
stunnel -p <CERT_FILE_NAME> -r localhost:80 -d 443
And maybe you want to add the last command to your bootup scripts.
-------------------------------------------------------------------
4) CryptoBox behind a proxy server
As there are many proxy servers around, we cannot describe all of them. As
an example, we will explain the setup of the load-balancing proxy 'pound'
(http://www.apsis.ch/pound/).
Just add the following lines to you /etc/pound/pound.cfg:
# Remove the X-SSL-Request header from incoming
# connections to prevent hackers from spoofing it
HeadRemove "X-SSL-Request"
# Add an extra header to tell the CryptoBox that
# the external connection is secure
HTTPSHeaders 0 "X-SSL-Request: 1"
This example is taken from:
http://jamesthornton.com/writing/openacs-pound.html
-------------------------------------------------------------------
5) Problems with SSL detection?
If the CryptoBox continues to complain about the unencrypted connection, even
if it runs behind an ssl-enabled webserver or behind stunnel, then you can do
one of the following things:
- disable the plugin 'encypted_webinterface' in the cryptobox.conf file
if you do not need it
- set the request header value "X-SSL-Request" to "1" (the digit 'one')
- set the environment setting "HTTPS" to a non-empty value during the
startup of the CryptoBox webserver. Maybe
/etc/default/cryptobox-server would be the right place for this.
- let the CryptoBox webserver listen to port 443

View file

@ -0,0 +1,555 @@
#!/usr/bin/env python
#
# Copyright 2007 sense.lab e.V.
#
# This file is part of the CryptoBox.
#
# The CryptoBox 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 2 of the License, or
# (at your option) any later version.
#
# The CryptoBox 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 the CryptoBox; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""module for executing some programs or scripts that need root privileges
Syntax:
check
- return exitcode zero if basic checks succeeded
program PROGRAM_NAME [ARGS]
- call the program (must be defined in "allowedProgs" below)
event EVENT_SCRIPT [ARGS]
- call an event script
plugin PLUGIN_NAME [ARGS]
- call a root_action script of a plugin
Exitcodes:
0 - execution was ok
1 - the executed program or action returned a failure exitcode
100 - improper calling or misconfiguration
of CryptoBoxRootAction (wrong arguments, wrong uid)
101 - failed to execute the given program - maybe it does not exist?
For more detailed information take a look at the manpage:
"man CryptoBoxRootActions"
"""
__revision__ = "$Id"
import os
import sys
import subprocess
import pwd
import grp
import types
allowedProgs = {
"sfdisk": "/sbin/sfdisk",
"cryptsetup": "/sbin/cryptsetup",
"mount": "/bin/mount",
"umount": "/bin/umount",
"blkid": "/sbin/blkid",
"pvdisplay": "/sbin/pvdisplay",
}
## this line is necessary for running unittests or playing around with a local
## svn working copy - otherwise the security checks would be too strict
OVERRIDE_FILECHECK = False
DEV_TYPES = { "pipe":1, "char":2, "dir":4, "block":6, "file":8, "link":10, "socket":12}
EVENT_MARKER = '_event_scripts_'
## use this string as device name if you want to mount a ramdisk
MAGIC_TMPFS = "_tmpfs_"
def checkIfFileIsSafe(fname):
"""check if the file and its parents are only writeable for root"""
## the override setting may be turned off temporarily to allow unittests
if OVERRIDE_FILECHECK:
return True
## if the calling user id is 0 (root), then we do not have to check this,
## as root would be allowed to do this anyway
## this eases testing with a not-installed working copy in a uml environment
if getCallingUserInfo()[1] == 0:
return True
props = os.stat(fname)
## check if it is owned by non-root
if props.st_uid != 0: return False
## check group-write permission if gid is not zero
if (props.st_gid != 0) and (props.st_mode % 32 / 16 > 0): return False
## check if it is world-writeable
if props.st_mode % 4 / 2 > 0: return False
## are we at root-level (directory-wise)? If yes, then we are ok ...
if fname == os.path.sep: return True
## check if the parent directory is ok - recursively :)
return checkIfFileIsSafe(os.path.dirname(os.path.abspath(fname)))
def checkIfPluginIsValid(plugin):
import imp
try:
x = imp.load_source("cbox_plugin", plugin)
except (SyntaxError, IOError):
return False
try:
if getattr(x, "PLUGIN_TYPE") == "cryptobox":
return True
else:
return False
except AttributeError:
return False
def checkIfEventScriptIsValid(plugin):
event_dir = os.path.dirname(plugin)
if os.path.exists(os.path.join(event_dir, EVENT_MARKER)):
return True
else:
return False
def call_plugin(args):
"""check if the plugin may be called - and do it finally ..."""
plugin = os.path.abspath(args[0])
del args[0]
## check existence and if it is executable
if not os.access(plugin, os.X_OK):
raise Exception, "could not find executable plugin (%s)" % plugin
## check if the plugin (and its parents) are only writeable for root
## this can be overridden by OVERRIDE_FILECHECK
if not checkIfFileIsSafe(plugin):
raise Exception, "the plugin (%s) is not safe - check its " % plugin \
+ "(and its parents') permissions"
## check if the plugin is a python program, that is marked as a cryptobox plugin
if not checkIfPluginIsValid(plugin):
raise Exception, "the plugin (%s) is not a correctly marked python script" % plugin
args.insert(0, plugin)
proc = subprocess.Popen(
shell = False,
args = args)
proc.wait()
return proc.returncode == 0
def call_event(args):
"""check if the event script may be called - and do it finally ..."""
event = os.path.abspath(args[0])
del args[0]
## check existence and if it is executable
if not os.access(event, os.X_OK):
raise Exception, "could not find executable event script (%s)" % event
## check if the script is valid (the marker file must be in the same directory)
if not checkIfEventScriptIsValid(event):
raise Exception, "the event script (%s) does not reside in" % event \
+ "a directory with the marker file (%s) - this " % EVENT_MARKER \
+ "is not allowed due to abuse prevention"
## check if the event (and its parents) are only writeable for root
if not checkIfFileIsSafe(event):
raise Exception, "the event (%s) is not safe - check its " % event \
+ "(and its parents') permissions"
args.insert(0, event)
proc = subprocess.Popen(
shell = False,
args = args)
proc.wait()
return proc.returncode == 0
def isWriteable(path, force_dev_type=None):
"""check if the calling user (not root!) has write access to the device/file
the real (not the effective) user id is used for the check
additionally the permissions of the default groups of the real uid are checked
it is sufficient, if the device/dir is owned by us
this check works nicely together with "super", as it changes (by default) only
the effective uid (not the real uid)
"""
## first check, if the device/file exists
if not os.path.exists(path):
sys.stderr.write("%s does not exist!\n" % path)
return False
## check the type of the path - if necessary
if (not force_dev_type is None) and \
(force_dev_type != os.stat(path).st_mode % 65536 / 4096):
sys.stderr.write("%s does not have the numeric type '%d'!\n" \
% (path, force_dev_type))
return False
## retrieve the information for the real user id
(trustUserName, trustUID, groupsOfTrustUser) = getCallingUserInfo()
## are we called by the root user? this would be ok
if trustUID == 0:
return True
## is the path owned by us?
if os.stat(path)[4] == trustUID:
return True
## set the default groups of the caller for the check (restore them later)
savedGroups = os.getgroups()
os.setgroups(groupsOfTrustUser)
## check permissions
result = os.access(path, os.W_OK) and os.access(path, os.R_OK)
## reset the groups of this process
os.setgroups(savedGroups)
return result
def run_cryptsetup(args):
"""execute cryptsetup as root
@args: list of arguments - they will be treated accordingly to the first element
of this list (the action)"""
if not args: raise "WrongArguments", "no action for cryptsetup supplied"
if type(args) != types.ListType:
raise "WrongArguments", "invalid arguments supplied: %s" % (args, )
try:
action = args[0]
del args[0]
device = None
cmd_args = []
if action == "luksFormat":
device = args[0]; del args[0]
cmd_args.append(action)
cmd_args.append(device)
elif action == "luksUUID":
device = args[0]; del args[0]
cmd_args.append(action)
cmd_args.append(device)
elif action == "luksOpen":
if len(args) < 2: raise "WrongArguments", "missing arguments"
device = args[0]; del args[0]
destination = args[0]; del args[0]
cmd_args.append(action)
cmd_args.append(device)
cmd_args.append(destination)
elif action == "luksClose":
if len(args) < 1: raise "WrongArguments", "missing arguments"
destination = args[0]; del args[0]
# maybe add a check for the mapped device's permissions?
# dmsetup deps self.device
cmd_args.append(action)
cmd_args.append(destination)
elif action == "luksAddKey":
device = args[0]; del args[0]
cmd_args.append(action)
cmd_args.append(device)
elif action == "luksDelKey":
if len(args) < 2: raise "WrongArguments", "missing arguments"
device = args[0]; del args[0]
slot = args[0]; del args[0]
cmd_args.append(action)
cmd_args.append(device)
cmd_args.append(slot)
elif action == "isLuks":
device = args[0]; del args[0]
cmd_args.append(action)
cmd_args.append(device)
else: raise "WrongArguments", "invalid action supplied: %s" % (action, )
# check if a device was defined - and check it
if (not device is None) and (not isWriteable(device, DEV_TYPES["block"])):
raise "WrongArguments", "%s is not a writeable block device" % (device, )
cs_args = [allowedProgs["cryptsetup"]]
cs_args.extend(args)
cs_args.extend(cmd_args)
except (TypeError, IndexError):
raise "WrongArguments", "invalid arguments supplied: %s" % (args, )
# execute cryptsetup with the given parameters
proc = subprocess.Popen(
shell = False,
args = cs_args)
proc.wait()
## chown the devmapper block device to the cryptobox user
calling_user = getCallingUserInfo()
if (proc.returncode == 0) and (action == "luksOpen"):
os.chown(os.path.join(os.path.sep, "dev", "mapper", destination),
calling_user[1], calling_user[2][0])
return proc.returncode == 0
def getFSType(device):
"""get the filesystem type of a device"""
proc = subprocess.Popen(
shell = False,
stdout = subprocess.PIPE,
args = [ allowedProgs["blkid"],
"-s", "TYPE",
"-o", "value",
"-c", os.devnull,
"-w", os.devnull,
device])
(stdout, stderr) = proc.communicate()
if proc.returncode != 0:
return None
return stdout.strip()
def run_mount(args):
"""execute mount
"""
if not args: raise "WrongArguments", "no destination for mount supplied"
if type(args) != types.ListType:
raise "WrongArguments", "invalid arguments supplied: %s" % (args, )
try:
device = args[0]
del args[0]
destination = args[0]
del args[0]
## shall we mount a ramdisk?
is_tmpfs = (device == MAGIC_TMPFS)
# check permissions for the device
if (not is_tmpfs) and (not isWriteable(device, DEV_TYPES["block"])):
raise "WrongArguments", "%s is not a writeable block device" % (device, )
## check permissions for the mountpoint
if not isWriteable(destination, DEV_TYPES["dir"]):
raise "WrongArguments", "the mountpoint (%s) is not writeable" \
% (destination, )
# check for additional (not allowed) arguments
if len(args) != 0:
raise "WrongArguments", "too many arguments for 'mount': %s" % (args, )
except TypeError:
raise "WrongArguments", "invalid arguments supplied: %s" % (args, )
# execute mount with the given parameters
# first overwrite the real uid, as 'mount' wants this to be zero (root)
savedUID = os.getuid()
os.setuid(os.geteuid())
## we have to change the permissions of the mounted directory - otherwise it will
## not be writeable for the cryptobox user
## for 'vfat' we have to do this during mount
## for ext2/3 we have to do it afterward
## first: get the user/group of the target
(trustUserName, trustUID, groupsOfTrustUser) = getUserInfo(savedUID)
trustGID = groupsOfTrustUser[0]
if is_tmpfs:
fsType = "tmpfs"
else:
fsType = getFSType(device)
## define arguments
if fsType == "vfat":
## add the "uid/gid" arguments to the mount call
mount_args = [ allowedProgs["mount"],
"-o", "uid=%d,gid=%d,umask=0000" % (trustUID, trustGID),
device,
destination ]
## use fuse (ntfs-3g) for ntfs
elif fsType == "ntfs":
mount_args = [ allowedProgs["mount"],
"-t", "ntfs-3g",
device,
destination ]
elif is_tmpfs:
mount_args = [ allowedProgs["mount"],
"-t", "tmpfs",
"cryptobox-tmpfs", destination ]
else:
## all other filesystem types will be handled after mount
mount_args = [ allowedProgs["mount"], device, destination ]
# execute mount
proc = subprocess.Popen(
shell = False,
args = mount_args)
proc.wait()
## return in case of an error
if proc.returncode != 0:
return False
## for vfat: we are done
if fsType == "vfat": return True
## for all other filesystem types: chown the mount directory
try:
os.chown(destination, trustUID, groupsOfTrustUser[0])
except OSError, errMsg:
sys.stderr.write("could not chown the mount destination (%s) " % destination \
+ "to the specified user (%d/%d): " % (trustUID, groupsOfTrustUser[0]) \
+ "%s/n" % str(errMsg))
sys.stderr.write("UID: %d\n" % (os.geteuid(),))
return False
## BEWARE: it would be nice, if we could restore the previous uid (not euid) but
## this would also override the euid (see 'man 2 setuid') - any ideas?
return True
def run_umount(args):
"""execute mount
"""
if not args: raise "WrongArguments", "no mountpoint for umount supplied"
if type(args) != types.ListType:
raise "WrongArguments", "invalid arguments supplied"
try:
destination = args[0]
del args[0]
# check permissions for the destination
if not isWriteable(os.path.dirname(destination), DEV_TYPES["dir"]):
raise "WrongArguments", "the parent of the mountpoint " \
+ "(%s) is not writeable" % (destination, )
if len(args) != 0: raise "WrongArguments", "umount does not allow arguments"
except TypeError:
raise "WrongArguments", "invalid arguments supplied"
# execute umount with the given parameters
# first overwrite the real uid, as 'umount' wants this to be zero (root)
savedUID = os.getuid()
os.setuid(os.geteuid())
# execute umount (with the parameter '-l' - lazy umount)
proc = subprocess.Popen(
shell = False,
args = [allowedProgs["umount"], "-l", destination])
proc.wait()
# restore previous real uid
os.setuid(savedUID)
return proc.returncode == 0
def run_pvdisplay(args):
"""execute pvdisplay to check for physical LVM devices
"""
if len(args) > 0:
raise "WrongArguments", "no arguments may be supplied for 'pvdisplay'"
## call pvdisplay with the parameter "--colon"
proc = subprocess.Popen(
shell = False,
args = [ allowedProgs["pvdisplay"], "--colon" ])
proc.wait()
return proc.returncode == 0
def getCallingUserInfo():
"""return information about the user that was calling this program via "super"
@user: (uid or name)
@return: tuple of (name, uid, (groups))
"""
## are we called via 'super'?
if ("SUPERCMD" in os.environ) and ("ORIG_USER" in os.environ):
## return the user that was calling super
return getUserInfo(os.environ["ORIG_USER"])
else:
## return the current user
return getUserInfo(os.getuid())
def getUserInfo(user):
"""return information about the specified user
@user: (uid or name)
@return: tuple of (name, uid, (groups))
"""
if (user is None) or (user == ""):
raise "KeyError", "no user supplied"
## if a KeyError is raised again in the following lines, then the supplied
## user was invalid
if type(user) is int:
# 'user' is a uid
userinfo = pwd.getpwuid(user)
elif type(user) is str:
# 'user' is a name
userinfo = pwd.getpwnam(user)
u_groups = [one_group.gr_gid
for one_group in grp.getgrall()
if userinfo.pw_name in one_group.gr_mem]
if not userinfo.pw_gid in u_groups:
## put in front of the list
u_groups.insert(0,userinfo.pw_gid)
return (userinfo.pw_name, userinfo.pw_uid, u_groups)
# **************** main **********************
# prevent import
if __name__ == "__main__":
## do we have root privileges (effective uid is zero)?
if os.geteuid() != 0:
sys.stderr.write("the effective uid is not zero - you should use " \
+ "'super' to call this script (%s)" % sys.argv[0])
sys.exit(100)
## remove program name
args = sys.argv[1:]
# do not allow to use root permissions (real uid may not be zero)
#if os.getuid() == 0:
# sys.stderr.write("the uid of the caller is zero (root) - this is not allowed\n")
# sys.exit(100)
## check if there were arguments
if (len(args) == 0):
sys.stderr.write("No arguments supplied\n")
sys.exit(100)
## did the user call the "check" action?
if (len(args) == 1) and (args[0].lower() == "check"):
# exit silently
sys.exit(0)
## all of the following actions require at least two arguments
if len(args) < 2:
sys.stderr.write("No program/plugin/event specified for execution\n")
sys.exit(100)
## call a plugin root_action script
if args[0].lower() == "plugin":
del args[0]
try:
isOK = call_plugin(args)
except Exception, errMsg:
sys.stderr.write("Execution of plugin '%s' failed: %s\n" \
% (args[0], errMsg))
sys.exit(100)
if isOK:
sys.exit(0)
else:
sys.exit(1)
## call an event script
if args[0].lower() == "event":
del args[0]
try:
isOK = call_event(args)
except Exception, errMsg:
sys.stderr.write("Execution of event '%s' failed: %s\n" \
% (args[0], errMsg))
sys.exit(100)
if isOK:
sys.exit(0)
else:
sys.exit(1)
## call one of the allowed programs
if args[0].lower() == "program":
del args[0]
progRequest = args[0]
del args[0]
if not progRequest in allowedProgs.keys():
sys.stderr.write("Invalid program requested: %s\n" % progRequest)
sys.exit(100)
if progRequest == "cryptsetup": runner = run_cryptsetup
elif progRequest == "mount": runner = run_mount
elif progRequest == "umount": runner = run_umount
elif progRequest == "pvdisplay": runner = run_pvdisplay
else:
sys.stderr.write("The interface for this program (%s) is " \
+ "not yet implemented!\n" % progRequest)
sys.exit(100)
try:
if runner(args):
sys.exit(0)
else:
sys.exit(1)
except OSError, errstr:
sys.stderr.write("Execution failed: %s\n" % errstr)
sys.exit(101)
except "WrongArguments", errstr:
sys.stderr.write("Invalid arguments: %s\n" % errstr)
sys.exit(100)

View file

@ -0,0 +1,401 @@
#!/usr/bin/env python
#
# The daemon script to run the CryptoBox webserver.
#
# run the script with "--help" to see all possible paramters
#
#
# Copyright 2006 sense.lab e.V.
#
# This file is part of the CryptoBox.
#
# The CryptoBox 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 2 of the License, or
# (at your option) any later version.
#
# The CryptoBox 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 the CryptoBox; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__revision__ = "$Id"
REMOVE_ENV_SETTINGS = [ "LANG", "LC", "LC_ALL", "LC_COLLATE", "LC_CTYPE",
"LC_MESSAGES", "LC_NUMERIC", "BASH_ENV", "SHELLOPTS" ]
import os, sys
import signal, atexit
import cryptobox.web.sites
from cryptobox.core.exceptions import *
from optparse import OptionParser
## check python version
(ver_major, ver_minor, ver_sub, ver_desc, ver_subsub) = sys.version_info
if (ver_major < 2) or ((ver_major == 2) and (ver_minor < 4)):
sys.stderr.write("You need a python version >= 2.4\n")
sys.stderr.write("Current version is: %s\n" % sys.version)
sys.exit(1)
## check cherrypy dependency
try:
import cherrypy
except:
sys.stderr.write("Could not import the cherrypy module!\n")
sys.stderr.write("Try 'apt-get install python-cherrypy'.\n")
sys.exit(1)
## check clearsilver dependency
try:
import neo_cgi, neo_util
except:
sys.stderr.write("Could not import the clearsilver module!\n")
sys.stderr.write("Try 'apt-get install python-clearsilver'.\n")
sys.exit(1)
## check configobj dependency
try:
import configobj, validate
except:
sys.stderr.write("Could not import the configobj or validate module!\n")
sys.stderr.write("Try 'apt-get install python-configobj'.\n")
sys.exit(1)
SERVER_ENVIRONMENT = "production"
class CryptoBoxWebserver:
'''this class starts the cherrypy webserver and serves the single sites'''
def __init__(self, opts):
"""Configure cherrypy and check the location of the configuration file
"""
self.opts = opts
## check conffile
if not os.access(opts.conffile, os.R_OK) or not os.path.isfile(opts.conffile):
sys.stderr.write("Error: could not read configuration file (%s)\n" % opts.conffile)
sys.exit(1)
## store the absolute path as we will chdir later (for daemons)
self.conffile = os.path.realpath(opts.conffile)
## expose static content and set options
## beware:
cherrypy.config.update({
"global": {
"server.socket_port" : int(opts.port),
"server.socket_host" : opts.host,
"server.log_to_screen" : not opts.background and opts.verbose,
"server.log_tracebacks" : opts.verbose,
"server.log_request_headers": opts.verbose,
"server.environment": SERVER_ENVIRONMENT,
"server.log_file" : opts.logfile },
"/cryptobox-misc": {
"staticFilter.on" : True,
"staticFilter.dir": os.path.realpath(opts.datadir)},
"/favicon.ico": {
"staticFilter.on" : True,
"staticFilter.file": os.path.realpath(os.path.join(opts.datadir, 'favicon.ico'))}
})
def bootup_cryptobox(self):
## initialize site class
try:
cherrypy.root = cryptobox.web.sites.WebInterfaceSites(self.conffile)
self.website = cherrypy.root
except (CBConfigError,CBEnvironmentError), err_msg:
sys.stderr.write("Error: the CryptoBox is misconfigured - please fix it!\n")
raise
def get_user_info(self):
"""Retrieve the uid, gid and additional groups of the given user
"""
import pwd, grp
user_entry = pwd.getpwuid(self.opts.user)
## get the new uid and gid
pw_name, pw_uid, pw_gid = user_entry[0], user_entry[2], user_entry[3]
## change the owner of the webserver log file
try:
os.chown(self.opts.logfile, pw_uid, pw_gid)
except OSError:
## fail silently
pass
## calculate additional groups of the given user
additional_groups = [ entry[2]
for entry in grp.getgrall()
if pw_name in entry[3] ] + [ pw_gid ]
return (pw_uid, pw_gid, additional_groups)
def change_groups(self):
"""Change the groups of the current process to the ones of the given user
we have to do this before we call cherrypy.server.start(), as it somehow
remembers the current setting for any thread it will create later
"""
if self.opts.user is None:
return
(pw_uid, pw_gid, additional_groups) = self.get_user_info()
try:
os.setgroups(additional_groups)
except OSError, err_msg:
sys.stderr.write("Failed to change the groups: %s\n" % err_msg)
def drop_privileges_permanently(self):
"""Drop all privileges of the current process and acquire the privileges of the
given user instead.
"""
if self.opts.user is None:
return
(pw_uid, pw_gid, additional_groups) = self.get_user_info()
try:
## setgroups happened before (see 'change_groups')
os.setregid(pw_gid, pw_gid)
os.setreuid(pw_uid, pw_uid)
except OSError, err_msg:
sys.stderr.write("Failed to drop privileges permanently: %s\n" % err_msg)
def start(self):
try:
## first: change the groups (cherrypy.server.start stores the
## current setting for creating new threads later)
self.change_groups()
cherrypy.server.start(initOnly=True)
self.drop_privileges_permanently()
## this must be done with dropped privileges - otherwise there is
## at least a problem with 'blkid' - see bug #139
self.bootup_cryptobox()
cherrypy.server.wait_for_http_ready()
except cherrypy._cperror.NotReady, err_msg:
sys.stderr.write("Failed to start CryptoBox: %s\n" % err_msg)
sys.exit(1)
except Exception, err_msg:
if err_msg == "(98, 'Address already in use')":
sys.stderr.write("Failed to start CryptoBox: %s\n" % err_msg)
sys.exit(1)
else:
raise
def fork_to_background():
## this is just copy'n'pasted from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731
## check the original for exhaustive comments
try:
pid = os.fork()
except OSError, err_msg:
sys.stderr.write("Error: failed to fork cryptobox daemon process!\n")
sys.stderr.write("%s\n" % err_msg)
sys.exit(1)
if pid == 0: # the first child
os.setsid()
try:
pid = os.fork()
except OSError, err_msg:
sys.stderr.write("Error: failed to fork second cryptobox daemon process!\n")
sys.stderr.write("%s\n" % err_msg)
sys.exit(1)
if pid == 0: # the second child
## we do not change the directory - otherwise there seems to be a race condition with the python interpreter loading this script file
#os.chdir(os.path.sep)
os.umask(0)
else:
os._exit(0)
else:
os._exit(0)
def close_open_files():
"""this is only necessary if we want to go into background
we will only close stdin, stdout and stderr
"""
import resource # Resource usage information.
## use the following lines to close all open files (including the log file)
# maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
# if (maxfd == resource.RLIM_INFINITY):
# maxfd = 1024
maxfd = 2
for fd in range(0, maxfd):
try:
## close all except for stderr - we will redirect it later
if fd != 2:
os.close(fd)
except OSError: # ERROR, fd wasn't open to begin with (ignored)
pass
os.open(os.devnull, os.O_RDWR) # standard input (0)
os.dup2(0, 1) # standard output (1)
def write_pid_file(pid_file, pid=None):
"""write the process ID of the cryptonas daemon to a file
call this function with the second parameter (e.g. pid=0) to check, if the
given location is writeable
@param pid_file: the path of the pid file to be written
@type pid_file: string
@param pid: use a specific PID instead of the PID of the current process
@type pid: int
"""
if pid is None:
## use the PID of the current process
pid = os.getpid()
if os.path.exists(pid_file):
sys.stderr.write(
"Warning: pid file (%s) already exists - overwriting ...\n" % pid_file)
try:
pidf = open(pid_file,"w")
pidf.write(str(pid))
pidf.close()
except (IOError, OSError), err_msg:
sys.stderr.write(
"Warning: failed to write pid file (%s): %s\n" % (pid_file, err_msg))
## it is just a warning - no need to break
def parseOptions():
import cryptobox
import pwd
version = "%prog" + cryptobox.__version__
parser = OptionParser(version=version)
parser.set_defaults(conffile="/etc/cryptobox-server/cryptobox.conf",
pidfile="/var/run/cryptobox-server/webserver.pid",
background=False,
datadir="/usr/share/cryptobox-server/www-data",
logfile="/var/log/cryptobox-server/webserver.log",
port="8080",
host="",
verbose=True,
profile_file=False,
user=None)
parser.add_option("-c", "--config", dest="conffile",
help="read configuration from FILE", metavar="FILE")
parser.add_option("","--pidfile", dest="pidfile",
help="write process id to FILE", metavar="FILE")
parser.add_option("-B","", dest="background", action="store_true",
help="run webserver in background (as daemon)")
parser.add_option("-q","", dest="verbose", action="store_false",
help="output only errors")
parser.add_option("","--datadir", dest="datadir", metavar="DIR",
help="set data directory to DIR")
parser.add_option("-p","--port", dest="port", metavar="PORT",
help="listen on PORT")
parser.add_option("-l","--logfile", dest="logfile", metavar="FILE",
help="write webserver log to FILE")
parser.add_option("","--host", dest="host", metavar="HOST",
help="attach to HOST")
parser.add_option("-u","--user", dest="user", metavar="USER",
help="change to USER after starting the webserver")
parser.add_option("","--profile", dest="profile_file", metavar="PROFILE_FILE",
help="enable profiling and store results in PROFILE_FILE")
(options, args) = parser.parse_args()
## we do not expect any remaining arguments
if len(args) != 0:
parser.error("unknown argument: %s" % str(args[0]))
if not ((not os.path.exists(options.logfile) \
and os.access(os.path.dirname(options.logfile), os.W_OK)) \
or os.access(options.logfile, os.W_OK)):
parser.error("could not write to logfile (%s)" % options.logfile)
if not os.path.isdir(options.datadir) or not os.access(options.datadir,os.X_OK):
parser.error("could not access the data directory (%s)" % options.datadir)
try:
if (int(options.port) < 0) or (int(options.port) > 65535):
parser.error("invalid port number: %s" % str(options.port))
except ValueError:
parser.error("invalid port specified (%s) - it must be a number" % (options.port))
if options.user:
try:
try:
## check for the user given as uid
uid = pwd.getpwuid(int(options.user))[2]
except ValueError:
## check for the user given as name
uid = pwd.getpwnam(options.user)[2]
except KeyError:
## invalid user specified
parser.error("invalid user specified (%s)" % options.user)
## we will use the uid
options.user = uid
if options.profile_file:
options.profile_file = os.path.abspath(options.profile_file)
try:
import profile
except ImportError:
parser.error("profiling requires the python module 'profile' - debian users should run 'apt-get install python-profiler'")
return options
def clean_environment(settings_list):
"""Remove some environment settings with side effects (e.g. LANG)
Useful, as some plugins depend on the output of other commands - localized
output would be quite ugly for them ...
"""
for one_setting in settings_list:
os.unsetenv(one_setting)
if __name__ == "__main__":
## process arguments
options = parseOptions()
## set umask to 022 (aka 755) - octal value
os.umask(022)
## initialize the webserver class
cbw = CryptoBoxWebserver(options)
## remove some environment settings
clean_environment(REMOVE_ENV_SETTINGS)
## fork to background before cbw.start() - otherwise we lose the socket
if options.background:
fork_to_background()
## define the default exit handler
def exit_handler(signum, sigframe):
if hasattr(cbw, "website"):
## are we already up?
cbw.website.cbox.log.info("Shutting down ...")
cbw.website.cleanup()
cherrypy.server.stop()
try:
os.remove(options.pidfile)
except OSError:
pass
os._exit(0)
## the signal handler gets called by a kill signal (usually in background mode)
signal.signal(signal.SIGTERM, exit_handler)
## this exit handler gets called by KeyboardInterrupt and similar ones (foreground)
atexit.register(exit_handler, None, None)
## start the webserver
try:
if options.profile_file:
import profile
profile.run('cbw.start()', options.profile_file)
else:
cbw.start()
except CBError, err_msg:
sys.stderr.write("Failed to start the CryptoBox webserver!\n")
sys.stderr.write("%s\n" % str(err_msg))
sys.stderr.write("Check the log file for details.\n")
cherrypy.server.stop()
sys.exit(1)
## test if we can write to the PID file
## this _must_ be done before forking, since a potential error would be
## silent (due to the closed files - e.g. STDERR)
write_pid_file(options.pidfile, 0)
## redirect stderr to the webserver's logfile
if options.background:
## replace stdin and stdout by /dev/null
close_open_files()
## replace stderr by the webserver logfile
os.close(2)
os.open(options.logfile, os.O_WRONLY | os.O_APPEND)
## startup went fine - fork is done - now we may write the pid file
write_pid_file(options.pidfile)
## this will never exit - one of the above exit handlers will get triggered
cherrypy.server.block()