First public release of the version 2.0 rewrite. This is the
first release of ShellInABox that supports an AJAX interface instead of the original Java applet. git-svn-id: https://shellinabox.googlecode.com/svn/trunk@2 0da03de8-d603-11dd-86c2-0f8696b7b6f9
This commit is contained in:
parent
ed80ce65c7
commit
aab20f5ed0
66 changed files with 54468 additions and 0 deletions
1
AUTHORS
Normal file
1
AUTHORS
Normal file
|
@ -0,0 +1 @@
|
|||
markus@shellinabox.com
|
39
COPYING
Normal file
39
COPYING
Normal file
|
@ -0,0 +1,39 @@
|
|||
Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
In addition to these license terms, the author grants the following
|
||||
additional rights:
|
||||
|
||||
If you modify this program, or any covered work, by linking or
|
||||
combining it with the OpenSSL project's OpenSSL library (or a
|
||||
modified version of that library), containing parts covered by the
|
||||
terms of the OpenSSL or SSLeay licenses, the author
|
||||
grants you additional permission to convey the resulting work.
|
||||
Corresponding Source for a non-source form of such a combination
|
||||
shall include the source code for the parts of OpenSSL used as well
|
||||
as that of the covered work.
|
||||
|
||||
You may at your option choose to remove this additional permission from
|
||||
the work, or from any part of it.
|
||||
|
||||
It is possible to build this program in a way that it loads OpenSSL
|
||||
libraries at run-time. If doing so, the following notices are required
|
||||
by the OpenSSL and SSLeay licenses:
|
||||
|
||||
This product includes software developed by the OpenSSL Project
|
||||
for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
|
||||
This product includes cryptographic software written by Eric Young
|
||||
(eay@cryptsoft.com)
|
6
ChangeLog
Normal file
6
ChangeLog
Normal file
|
@ -0,0 +1,6 @@
|
|||
2008-12-27 Markus Gutschke <markus@shellinabox.com>
|
||||
|
||||
* First public release of the version 2.0 rewrite. This is the
|
||||
first release of ShellInABox that supports an AJAX interface
|
||||
instead of the original Java applet.
|
||||
|
339
GPL-2
Normal file
339
GPL-2
Normal file
|
@ -0,0 +1,339 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, 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 Lesser 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 Street, 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 Lesser General
|
||||
Public License instead of this License.
|
237
INSTALL
Normal file
237
INSTALL
Normal file
|
@ -0,0 +1,237 @@
|
|||
Installation Instructions
|
||||
*************************
|
||||
|
||||
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
|
||||
2006, 2007 Free Software Foundation, Inc.
|
||||
|
||||
This file is free documentation; the Free Software Foundation gives
|
||||
unlimited permission to copy, distribute and modify it.
|
||||
|
||||
Basic Installation
|
||||
==================
|
||||
|
||||
Briefly, the shell commands `./configure; make; make install' should
|
||||
configure, build, and install this package. The following
|
||||
more-detailed instructions are generic; see the `README' file for
|
||||
instructions specific to this package.
|
||||
|
||||
The `configure' shell script attempts to guess correct values for
|
||||
various system-dependent variables used during compilation. It uses
|
||||
those values to create a `Makefile' in each directory of the package.
|
||||
It may also create one or more `.h' files containing system-dependent
|
||||
definitions. Finally, it creates a shell script `config.status' that
|
||||
you can run in the future to recreate the current configuration, and a
|
||||
file `config.log' containing compiler output (useful mainly for
|
||||
debugging `configure').
|
||||
|
||||
It can also use an optional file (typically called `config.cache'
|
||||
and enabled with `--cache-file=config.cache' or simply `-C') that saves
|
||||
the results of its tests to speed up reconfiguring. Caching is
|
||||
disabled by default to prevent problems with accidental use of stale
|
||||
cache files.
|
||||
|
||||
If you need to do unusual things to compile the package, please try
|
||||
to figure out how `configure' could check whether to do them, and mail
|
||||
diffs or instructions to the address given in the `README' so they can
|
||||
be considered for the next release. If you are using the cache, and at
|
||||
some point `config.cache' contains results you don't want to keep, you
|
||||
may remove or edit it.
|
||||
|
||||
The file `configure.ac' (or `configure.in') is used to create
|
||||
`configure' by a program called `autoconf'. You need `configure.ac' if
|
||||
you want to change it or regenerate `configure' using a newer version
|
||||
of `autoconf'.
|
||||
|
||||
The simplest way to compile this package is:
|
||||
|
||||
1. `cd' to the directory containing the package's source code and type
|
||||
`./configure' to configure the package for your system.
|
||||
|
||||
Running `configure' might take a while. While running, it prints
|
||||
some messages telling which features it is checking for.
|
||||
|
||||
2. Type `make' to compile the package.
|
||||
|
||||
3. Optionally, type `make check' to run any self-tests that come with
|
||||
the package.
|
||||
|
||||
4. Type `make install' to install the programs and any data files and
|
||||
documentation.
|
||||
|
||||
5. You can remove the program binaries and object files from the
|
||||
source code directory by typing `make clean'. To also remove the
|
||||
files that `configure' created (so you can compile the package for
|
||||
a different kind of computer), type `make distclean'. There is
|
||||
also a `make maintainer-clean' target, but that is intended mainly
|
||||
for the package's developers. If you use it, you may have to get
|
||||
all sorts of other programs in order to regenerate files that came
|
||||
with the distribution.
|
||||
|
||||
6. Often, you can also type `make uninstall' to remove the installed
|
||||
files again.
|
||||
|
||||
Compilers and Options
|
||||
=====================
|
||||
|
||||
Some systems require unusual options for compilation or linking that the
|
||||
`configure' script does not know about. Run `./configure --help' for
|
||||
details on some of the pertinent environment variables.
|
||||
|
||||
You can give `configure' initial values for configuration parameters
|
||||
by setting variables in the command line or in the environment. Here
|
||||
is an example:
|
||||
|
||||
./configure CC=c99 CFLAGS=-g LIBS=-lposix
|
||||
|
||||
*Note Defining Variables::, for more details.
|
||||
|
||||
Compiling For Multiple Architectures
|
||||
====================================
|
||||
|
||||
You can compile the package for more than one kind of computer at the
|
||||
same time, by placing the object files for each architecture in their
|
||||
own directory. To do this, you can use GNU `make'. `cd' to the
|
||||
directory where you want the object files and executables to go and run
|
||||
the `configure' script. `configure' automatically checks for the
|
||||
source code in the directory that `configure' is in and in `..'.
|
||||
|
||||
With a non-GNU `make', it is safer to compile the package for one
|
||||
architecture at a time in the source code directory. After you have
|
||||
installed the package for one architecture, use `make distclean' before
|
||||
reconfiguring for another architecture.
|
||||
|
||||
Installation Names
|
||||
==================
|
||||
|
||||
By default, `make install' installs the package's commands under
|
||||
`/usr/local/bin', include files under `/usr/local/include', etc. You
|
||||
can specify an installation prefix other than `/usr/local' by giving
|
||||
`configure' the option `--prefix=PREFIX'.
|
||||
|
||||
You can specify separate installation prefixes for
|
||||
architecture-specific files and architecture-independent files. If you
|
||||
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
|
||||
PREFIX as the prefix for installing programs and libraries.
|
||||
Documentation and other data files still use the regular prefix.
|
||||
|
||||
In addition, if you use an unusual directory layout you can give
|
||||
options like `--bindir=DIR' to specify different values for particular
|
||||
kinds of files. Run `configure --help' for a list of the directories
|
||||
you can set and what kinds of files go in them.
|
||||
|
||||
If the package supports it, you can cause programs to be installed
|
||||
with an extra prefix or suffix on their names by giving `configure' the
|
||||
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
||||
|
||||
Optional Features
|
||||
=================
|
||||
|
||||
Some packages pay attention to `--enable-FEATURE' options to
|
||||
`configure', where FEATURE indicates an optional part of the package.
|
||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
||||
is something like `gnu-as' or `x' (for the X Window System). The
|
||||
`README' should mention any `--enable-' and `--with-' options that the
|
||||
package recognizes.
|
||||
|
||||
For packages that use the X Window System, `configure' can usually
|
||||
find the X include and library files automatically, but if it doesn't,
|
||||
you can use the `configure' options `--x-includes=DIR' and
|
||||
`--x-libraries=DIR' to specify their locations.
|
||||
|
||||
Specifying the System Type
|
||||
==========================
|
||||
|
||||
There may be some features `configure' cannot figure out automatically,
|
||||
but needs to determine by the type of machine the package will run on.
|
||||
Usually, assuming the package is built to be run on the _same_
|
||||
architectures, `configure' can figure that out, but if it prints a
|
||||
message saying it cannot guess the machine type, give it the
|
||||
`--build=TYPE' option. TYPE can either be a short name for the system
|
||||
type, such as `sun4', or a canonical name which has the form:
|
||||
|
||||
CPU-COMPANY-SYSTEM
|
||||
|
||||
where SYSTEM can have one of these forms:
|
||||
|
||||
OS KERNEL-OS
|
||||
|
||||
See the file `config.sub' for the possible values of each field. If
|
||||
`config.sub' isn't included in this package, then this package doesn't
|
||||
need to know the machine type.
|
||||
|
||||
If you are _building_ compiler tools for cross-compiling, you should
|
||||
use the option `--target=TYPE' to select the type of system they will
|
||||
produce code for.
|
||||
|
||||
If you want to _use_ a cross compiler, that generates code for a
|
||||
platform different from the build platform, you should specify the
|
||||
"host" platform (i.e., that on which the generated programs will
|
||||
eventually be run) with `--host=TYPE'.
|
||||
|
||||
Sharing Defaults
|
||||
================
|
||||
|
||||
If you want to set default values for `configure' scripts to share, you
|
||||
can create a site shell script called `config.site' that gives default
|
||||
values for variables like `CC', `cache_file', and `prefix'.
|
||||
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
||||
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||
`CONFIG_SITE' environment variable to the location of the site script.
|
||||
A warning: not all `configure' scripts look for a site script.
|
||||
|
||||
Defining Variables
|
||||
==================
|
||||
|
||||
Variables not defined in a site shell script can be set in the
|
||||
environment passed to `configure'. However, some packages may run
|
||||
configure again during the build, and the customized values of these
|
||||
variables may be lost. In order to avoid this problem, you should set
|
||||
them in the `configure' command line, using `VAR=value'. For example:
|
||||
|
||||
./configure CC=/usr/local2/bin/gcc
|
||||
|
||||
causes the specified `gcc' to be used as the C compiler (unless it is
|
||||
overridden in the site shell script).
|
||||
|
||||
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
|
||||
an Autoconf bug. Until the bug is fixed you can use this workaround:
|
||||
|
||||
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
|
||||
|
||||
`configure' Invocation
|
||||
======================
|
||||
|
||||
`configure' recognizes the following options to control how it operates.
|
||||
|
||||
`--help'
|
||||
`-h'
|
||||
Print a summary of the options to `configure', and exit.
|
||||
|
||||
`--version'
|
||||
`-V'
|
||||
Print the version of Autoconf used to generate the `configure'
|
||||
script, and exit.
|
||||
|
||||
`--cache-file=FILE'
|
||||
Enable the cache: use and save the results of the tests in FILE,
|
||||
traditionally `config.cache'. FILE defaults to `/dev/null' to
|
||||
disable caching.
|
||||
|
||||
`--config-cache'
|
||||
`-C'
|
||||
Alias for `--cache-file=config.cache'.
|
||||
|
||||
`--quiet'
|
||||
`--silent'
|
||||
`-q'
|
||||
Do not print messages saying which checks are being made. To
|
||||
suppress all normal output, redirect it to `/dev/null' (any error
|
||||
messages will still be shown).
|
||||
|
||||
`--srcdir=DIR'
|
||||
Look for the package's source code in directory DIR. Usually
|
||||
`configure' can determine that directory automatically.
|
||||
|
||||
`configure' also accepts some other, not widely useful, options. Run
|
||||
`configure --help' for more details.
|
||||
|
211
Makefile.am
Normal file
211
Makefile.am
Normal file
|
@ -0,0 +1,211 @@
|
|||
AM_CPPFLAGS =
|
||||
AM_CFLAGS = -g -std=gnu99 -Wall -Werror -Os
|
||||
AM_LDFLAGS = -g
|
||||
|
||||
noinst_LTLIBRARIES = libhttp.la \
|
||||
liblogging.la
|
||||
bin_PROGRAMS = shellinaboxd
|
||||
man_MANS = shellinaboxd.1
|
||||
noinst_HEADERS = libhttp/http.h
|
||||
dist_doc_DATA = AUTHORS \
|
||||
COPYING \
|
||||
GPL-2 \
|
||||
ChangeLog \
|
||||
INSTALL \
|
||||
NEWS \
|
||||
README \
|
||||
TODO
|
||||
EXTRA_DIST = shellinabox/shellinaboxd.man.in \
|
||||
debian/README \
|
||||
debian/changelog \
|
||||
debian/compat \
|
||||
debian/control \
|
||||
debian/copyright \
|
||||
debian/docs \
|
||||
debian/rules \
|
||||
debian/shellinabox.default \
|
||||
debian/shellinabox.dirs \
|
||||
debian/shellinabox.init \
|
||||
debian/shellinabox.install \
|
||||
debian/shellinabox.postinst \
|
||||
debian/shellinabox.postrm
|
||||
LIBLOGGING_INCLUDES = logging/logging.h
|
||||
liblogging_la_SOURCES= logging/logging.c \
|
||||
$(LIBLOGGING_INCLUDES)
|
||||
liblogging_la_LDFLAGS= -version 1:0:0
|
||||
|
||||
LIBHTTP_INCLUDES = libhttp/hashmap.h \
|
||||
libhttp/trie.h \
|
||||
libhttp/httpconnection.h \
|
||||
libhttp/server.h \
|
||||
libhttp/ssl.h \
|
||||
libhttp/url.h
|
||||
libhttp_la_SOURCES = libhttp/hashmap.c \
|
||||
libhttp/trie.c \
|
||||
libhttp/httpconnection.c \
|
||||
libhttp/server.c \
|
||||
libhttp/ssl.c \
|
||||
libhttp/url.c \
|
||||
$(LIBHTTP_INCLUDES) \
|
||||
libhttp/libhttp.sym
|
||||
libhttp_la_LDFLAGS = -export-symbols $(top_srcdir)/libhttp/libhttp.sym \
|
||||
-version 1:0:0 -ldl
|
||||
|
||||
shellinaboxd_SOURCES = shellinabox/shellinaboxd.c \
|
||||
shellinabox/externalfile.c \
|
||||
shellinabox/externalfile.h \
|
||||
shellinabox/launcher.c \
|
||||
shellinabox/launcher.h \
|
||||
shellinabox/privileges.c \
|
||||
shellinabox/privileges.h \
|
||||
shellinabox/service.c \
|
||||
shellinabox/service.h \
|
||||
shellinabox/session.c \
|
||||
shellinabox/session.h \
|
||||
shellinabox/root_page.html \
|
||||
shellinabox/vt100.js \
|
||||
shellinabox/shell_in_a_box.js \
|
||||
shellinabox/styles.css \
|
||||
shellinabox/favicon.ico \
|
||||
shellinabox/beep.wav
|
||||
shellinaboxd_LDADD = liblogging.la \
|
||||
libhttp.la
|
||||
shellinaboxd_LDFLAGS = -static -ldl
|
||||
|
||||
libtool: $(LIBTOOL_DEPS)
|
||||
$(SHELL) ./config.status --recheck
|
||||
|
||||
shellinaboxd.1: shellinabox/shellinaboxd.man.in
|
||||
@echo preprocess "$<" '>'"$@"
|
||||
@if echo " $(DEFS)" | grep HAVE_OPENSSL_BIO_H | \
|
||||
grep HAVE_OPENSSL_ERR_H | \
|
||||
grep -q HAVE_OPENSSL_SSL_H; then \
|
||||
sed -e '/^#ifdef *HAVE_OPENSSL$$/d;/^#endif$$/d' "$<" >"$@"; \
|
||||
else \
|
||||
sed -e '/^#ifdef *HAVE_OPENSSL$$/,/^#endif$$/d' "$<" >"$@"; \
|
||||
fi
|
||||
@man -Tps "./$@" >`echo "$@"|sed -e 's/\.[^.]*$$/.ps/'` || true
|
||||
|
||||
clean-local:
|
||||
-rm -rf shellinaboxd.1 \
|
||||
shellinaboxd.ps
|
||||
-rm -rf debian/shellinabox \
|
||||
debian/shellinabox*.debhelper* \
|
||||
debian/shellinabox.substvars \
|
||||
debian/tmp
|
||||
|
||||
.css.o:
|
||||
@$(ECHO) objcopy "$<" "$@"
|
||||
@objcopy \
|
||||
-I binary `echo "$(build_cpu)" | \
|
||||
grep -q '^i[0-9]86$$' && \
|
||||
echo ' -O elf32-i386 -B i386' || \
|
||||
echo ' -O elf64-x86-64 -B x86-64'` \
|
||||
`echo "$<" | sed -e ' \
|
||||
s/\(.*\/\)\([^.]*\)\([.].*\)/\1\2\3=\2 /; \
|
||||
t0; s/\([^.]*\)\([.].*\)/\1\2=\1 /; t0; s/.*/&=& /;:0; \
|
||||
s/$$/aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ/; \
|
||||
:1; \
|
||||
s/\(=[^_]*\)_\([a-z]\)\([^ ]* .*\2\)\(.\)/\1\4\3\4/; \
|
||||
t1; \
|
||||
s/.\{53\}$$//; \
|
||||
s/[/.]/_/g; \
|
||||
s/^/--redefine-sym _binary_/; \
|
||||
s/\([^=]*\)\(=[^ ]*\)/& \1_end\2End/; \
|
||||
s/\([^=]*\)\(=[^ ]*\)/& \1_start\2Start/; \
|
||||
s/[^ ]*\([^=]*\)=[^ ]*/-N\1_size/'` \
|
||||
"$<" "$@"
|
||||
|
||||
.html.o:
|
||||
@$(ECHO) objcopy "$<" "$@"
|
||||
@objcopy \
|
||||
-I binary `echo "$(build_cpu)" | \
|
||||
grep -q '^i[0-9]86$$' && \
|
||||
echo ' -O elf32-i386 -B i386' || \
|
||||
echo ' -O elf64-x86-64 -B x86-64'` \
|
||||
`echo "$<" | sed -e ' \
|
||||
s/\(.*\/\)\([^.]*\)\([.].*\)/\1\2\3=\2 /; \
|
||||
t0; s/\([^.]*\)\([.].*\)/\1\2=\1 /; t0; s/.*/&=& /;:0; \
|
||||
s/$$/aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ/; \
|
||||
:1; \
|
||||
s/\(=[^_]*\)_\([a-z]\)\([^ ]* .*\2\)\(.\)/\1\4\3\4/; \
|
||||
t1; \
|
||||
s/.\{53\}$$//; \
|
||||
s/[/.]/_/g; \
|
||||
s/^/--redefine-sym _binary_/; \
|
||||
s/\([^=]*\)\(=[^ ]*\)/& \1_end\2End/; \
|
||||
s/\([^=]*\)\(=[^ ]*\)/& \1_start\2Start/; \
|
||||
s/[^ ]*\([^=]*\)=[^ ]*/-N\1_size/'` \
|
||||
"$<" "$@"
|
||||
|
||||
.ico.o:
|
||||
@$(ECHO) objcopy "$<" "$@"
|
||||
@objcopy \
|
||||
-I binary `echo "$(build_cpu)" | \
|
||||
grep -q '^i[0-9]86$$' && \
|
||||
echo ' -O elf32-i386 -B i386' || \
|
||||
echo ' -O elf64-x86-64 -B x86-64'` \
|
||||
`echo "$<" | sed -e ' \
|
||||
s/\(.*\/\)\([^.]*\)\([.].*\)/\1\2\3=\2 /; \
|
||||
t0; s/\([^.]*\)\([.].*\)/\1\2=\1 /; t0; s/.*/&=& /;:0; \
|
||||
s/$$/aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ/; \
|
||||
:1; \
|
||||
s/\(=[^_]*\)_\([a-z]\)\([^ ]* .*\2\)\(.\)/\1\4\3\4/; \
|
||||
t1; \
|
||||
s/.\{53\}$$//; \
|
||||
s/[/.]/_/g; \
|
||||
s/^/--redefine-sym _binary_/; \
|
||||
s/\([^=]*\)\(=[^ ]*\)/& \1_end\2End/; \
|
||||
s/\([^=]*\)\(=[^ ]*\)/& \1_start\2Start/; \
|
||||
s/[^ ]*\([^=]*\)=[^ ]*/-N\1_size/'` \
|
||||
"$<" "$@"
|
||||
|
||||
.js.o:
|
||||
@$(ECHO) preprocess "$<" \| objcopy "$@"
|
||||
@trap 'rm -f "$@.pre"' EXIT INT TERM QUIT; \
|
||||
sed -e "`sed -e 's/^#define *\([^ ]*\) *\(.*\)/\/^[^#]\/s\/\1\/\2 \\\\\/* \1 *\\\\\/\/g/; \
|
||||
t;d' "$<"` \
|
||||
;s/^#/\/\/ #/ \
|
||||
;s/VERSION/\"@VERSION@\"/g" "$<" >"$@.pre" && \
|
||||
objcopy \
|
||||
-I binary `echo $(build_cpu) | \
|
||||
grep -q '^i[0-9]86$$' && \
|
||||
echo ' -O elf32-i386 -B i386' || \
|
||||
echo ' -O elf64-x86-64 -B x86-64'` \
|
||||
`echo "$@" | sed -e ' \
|
||||
s/\(.*\/\)\([^.]*\)\([.].*\)/\1\2\3=\2 /; \
|
||||
t0; s/\([^.]*\)\([.].*\)/\1\2=\1 /; t0; s/.*/&=& /;:0; \
|
||||
s/$$/aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ/; \
|
||||
:1; \
|
||||
s/\(=[^_]*\)_\([a-z]\)\([^ ]* .*\2\)\(.\)/\1\4\3\4/; \
|
||||
t1; \
|
||||
s/.\{53\}$$//; \
|
||||
s/[/.]/_/g; \
|
||||
s/^/--redefine-sym _binary_/; \
|
||||
s/\([^=]*\)\(=[^ ]*\)/& \1_pre_end\2End/; \
|
||||
s/\([^=]*\)\(=[^ ]*\)/& \1_pre_start\2Start/; \
|
||||
s/[^ ]*\([^=]*\)=[^ ]*/-N\1_pre_size/'` \
|
||||
"$@.pre" "$@"
|
||||
|
||||
.wav.o:
|
||||
@$(ECHO) objcopy "$<" "$@"
|
||||
@objcopy \
|
||||
-I binary `echo "$(build_cpu)" | \
|
||||
grep -q '^i[0-9]86$$' && \
|
||||
echo ' -O elf32-i386 -B i386' || \
|
||||
echo ' -O elf64-x86-64 -B x86-64'` \
|
||||
`echo "$<" | sed -e ' \
|
||||
s/\(.*\/\)\([^.]*\)\([.].*\)/\1\2\3=\2 /; \
|
||||
t0; s/\([^.]*\)\([.].*\)/\1\2=\1 /; t0; s/.*/&=& /;:0; \
|
||||
s/$$/aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ/; \
|
||||
:1; \
|
||||
s/\(=[^_]*\)_\([a-z]\)\([^ ]* .*\2\)\(.\)/\1\4\3\4/; \
|
||||
t1; \
|
||||
s/.\{53\}$$//; \
|
||||
s/[/.]/_/g; \
|
||||
s/^/--redefine-sym _binary_/; \
|
||||
s/\([^=]*\)\(=[^ ]*\)/& \1_end\2End/; \
|
||||
s/\([^=]*\)\(=[^ ]*\)/& \1_start\2Start/; \
|
||||
s/[^ ]*\([^=]*\)=[^ ]*/-N\1_size/'` \
|
||||
"$<" "$@"
|
||||
|
1100
Makefile.in
Normal file
1100
Makefile.in
Normal file
File diff suppressed because it is too large
Load diff
3
NEWS
Normal file
3
NEWS
Normal file
|
@ -0,0 +1,3 @@
|
|||
Version 2.0 of ShellInABox is a complete rewrite. While previous versions
|
||||
required the Java plugin, since version 2.0 JavaScript enabled browsers are
|
||||
supported without the need for additional plugins.
|
3
README
Normal file
3
README
Normal file
|
@ -0,0 +1,3 @@
|
|||
Build the package according to the information in INSTALL, then refer to
|
||||
to the shellinaboxd.1 manual page, or the shellinaboxd.ps PostScript file
|
||||
for detailed documentation.
|
4
TODO
Normal file
4
TODO
Normal file
|
@ -0,0 +1,4 @@
|
|||
- Check if there is any way that we could fall back on gnutls if openssl is
|
||||
unavailable
|
||||
- Package for distributions other than Debian
|
||||
|
7520
aclocal.m4
vendored
Normal file
7520
aclocal.m4
vendored
Normal file
File diff suppressed because it is too large
Load diff
1526
config.guess
vendored
Normal file
1526
config.guess
vendored
Normal file
File diff suppressed because it is too large
Load diff
1658
config.sub
vendored
Normal file
1658
config.sub
vendored
Normal file
File diff suppressed because it is too large
Load diff
13
configure.ac
Normal file
13
configure.ac
Normal file
|
@ -0,0 +1,13 @@
|
|||
AC_PREREQ(2.57)
|
||||
AC_INIT(shellinabox, 2.0, markus@shellinabox.com)
|
||||
AM_INIT_AUTOMAKE
|
||||
AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_LIBTOOL
|
||||
AC_SUBST(LIBTOOL_DEPS)
|
||||
AC_C_CONST
|
||||
AC_PROG_GCC_TRADITIONAL
|
||||
AC_CHECK_HEADERS([openssl/bio.h openssl/err.h openssl/ssl.h pthread.h \
|
||||
security/pam_appl.h security/pam_misc.h])
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_OUTPUT
|
7
debian/README
vendored
Normal file
7
debian/README
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
The list of files here isn't complete. For a step-by-step guide on
|
||||
how to set this package up correctly, check out
|
||||
http://www.debian.org/doc/maint-guide/
|
||||
|
||||
Most of the files that are in this directory are boilerplate.
|
||||
However, you may need to change the list of binary-arch dependencies
|
||||
in 'rules'.
|
5
debian/changelog
vendored
Normal file
5
debian/changelog
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
shellinabox (2.0-1) unstable; urgency=low
|
||||
|
||||
* Initial release.
|
||||
|
||||
-- Markus Gutschke <markus@shellinabox.com> Sat, 27 Dec 2008 09:15:32 -0800
|
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
4
|
15
debian/control
vendored
Normal file
15
debian/control
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
Source: shellinabox
|
||||
Section: web
|
||||
Priority: optional
|
||||
Maintainer: Markus Gutschke <markus@shellinabox.com>
|
||||
Build-Depends: debhelper (>= 4.0.0), binutils, libssl-dev, libpam0g-dev
|
||||
Standards-Version: 3.6.1
|
||||
|
||||
Package: shellinabox
|
||||
Section: web
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, adduser
|
||||
Description: publish command line shell through AJAX interface
|
||||
Shellinabox can export arbitrary command line programs to any JavaScript
|
||||
enabled web browser. By default, it prompts for username and password
|
||||
and then exports a SSL/TLS encrypted login shell.
|
52
debian/copyright
vendored
Normal file
52
debian/copyright
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
This package was debianized by Markus Gutschke <markus@shellinabox.com> on
|
||||
27 December 2008.
|
||||
|
||||
It was downloaded from http://shellinabox.com/
|
||||
|
||||
Upstream Author: markus@shellinabox.com
|
||||
|
||||
Copyright (c) 2008, Markus Gutschke
|
||||
All rights reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
See /usr/share/common-licenses/GPL-2 on your Debian system.
|
||||
|
||||
In addition to these license terms, the author grants the following
|
||||
additional rights:
|
||||
|
||||
If you modify this program, or any covered work, by linking or
|
||||
combining it with the OpenSSL project's OpenSSL library (or a
|
||||
modified version of that library), containing parts covered by the
|
||||
terms of the OpenSSL or SSLeay licenses, the author
|
||||
grants you additional permission to convey the resulting work.
|
||||
Corresponding Source for a non-source form of such a combination
|
||||
shall include the source code for the parts of OpenSSL used as well
|
||||
as that of the covered work.
|
||||
|
||||
You may at your option choose to remove this additional permission from
|
||||
the work, or from any part of it.
|
||||
|
||||
It is possible to build this program in a way that it loads OpenSSL
|
||||
libraries at run-time. If doing so, the following notices are required
|
||||
by the OpenSSL and SSLeay licenses:
|
||||
|
||||
This product includes software developed by the OpenSSL Project
|
||||
for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
|
||||
This product includes cryptographic software written by Eric Young
|
||||
(eay@cryptsoft.com)
|
||||
|
||||
The most up-to-date version of this program is always available from
|
||||
http://shellinabox.com
|
7
debian/docs
vendored
Normal file
7
debian/docs
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
AUTHORS
|
||||
COPYING
|
||||
ChangeLog
|
||||
INSTALL
|
||||
NEWS
|
||||
README
|
||||
TODO
|
114
debian/rules
vendored
Executable file
114
debian/rules
vendored
Executable file
|
@ -0,0 +1,114 @@
|
|||
#!/usr/bin/make -f
|
||||
# -*- makefile -*-
|
||||
# Sample debian/rules that uses debhelper.
|
||||
# This file was originally written by Joey Hess and Craig Small.
|
||||
# As a special exception, when this file is copied by dh-make into a
|
||||
# dh-make output file, you may use that output file without restriction.
|
||||
# This special exception was added by Craig Small in version 0.37 of dh-make.
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
|
||||
# These are used for cross-compiling and for saving the configure script
|
||||
# from having to guess our platform (since we know it already)
|
||||
DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
|
||||
DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
|
||||
|
||||
|
||||
CFLAGS = -Wall -g
|
||||
|
||||
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
|
||||
CFLAGS += -O0
|
||||
else
|
||||
CFLAGS += -O2
|
||||
endif
|
||||
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
|
||||
INSTALL_PROGRAM += -s
|
||||
endif
|
||||
|
||||
# shared library versions, option 1
|
||||
#version=2.0.5
|
||||
#major=2
|
||||
# option 2, assuming the library is created as src/.libs/libfoo.so.2.0.5 or so
|
||||
version=`ls src/.libs/lib*.so.* | \
|
||||
awk '{if (match($$0,/[0-9]+\.[0-9]+\.[0-9]+$$/)) print substr($$0,RSTART)}'`
|
||||
major=`ls src/.libs/lib*.so.* | \
|
||||
awk '{if (match($$0,/\.so\.[0-9]+$$/)) print substr($$0,RSTART+4)}'`
|
||||
|
||||
config.status: configure
|
||||
dh_testdir
|
||||
@# Add here commands to configure the package.
|
||||
CFLAGS="$(CFLAGS)" ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info
|
||||
|
||||
|
||||
build: build-stamp
|
||||
build-stamp: config.status
|
||||
dh_testdir
|
||||
@# Add here commands to compile the package.
|
||||
$(MAKE)
|
||||
|
||||
touch build-stamp
|
||||
|
||||
clean:
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
rm -f build-stamp
|
||||
@# Add here commands to clean up after the build process.
|
||||
-$(MAKE) distclean
|
||||
ifneq "$(wildcard /usr/share/misc/config.sub)" ""
|
||||
cp -f /usr/share/misc/config.sub config.sub
|
||||
endif
|
||||
ifneq "$(wildcard /usr/share/misc/config.guess)" ""
|
||||
cp -f /usr/share/misc/config.guess config.guess
|
||||
endif
|
||||
|
||||
|
||||
dh_clean
|
||||
|
||||
install: build
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_clean -k
|
||||
dh_installdirs
|
||||
@# Add here commands to install the package into debian/tmp
|
||||
$(MAKE) install DESTDIR=$(CURDIR)/debian/tmp
|
||||
|
||||
|
||||
# Build architecture-independent files here.
|
||||
binary-indep: build install
|
||||
# We have nothing to do by default.
|
||||
|
||||
# Build architecture-dependent files here.
|
||||
binary-arch: build install
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_installchangelogs ChangeLog
|
||||
dh_installdocs --exclude COPYING --exclude INSTALL --exclude ChangeLog
|
||||
dh_installexamples
|
||||
dh_install --sourcedir=debian/tmp
|
||||
# dh_installmenu
|
||||
# dh_installdebconf
|
||||
# dh_installlogrotate
|
||||
# dh_installemacsen
|
||||
# dh_installpam
|
||||
# dh_installmime
|
||||
dh_installinit
|
||||
# dh_installcron
|
||||
# dh_installinfo
|
||||
dh_installman
|
||||
dh_link
|
||||
dh_strip
|
||||
dh_compress
|
||||
dh_fixperms
|
||||
# dh_perl
|
||||
# dh_python
|
||||
dh_makeshlibs
|
||||
dh_installdeb
|
||||
dh_shlibdeps
|
||||
dh_gencontrol
|
||||
dh_md5sums
|
||||
dh_builddeb
|
||||
|
||||
binary: binary-indep binary-arch
|
||||
.PHONY: build clean binary-indep binary-arch binary install
|
8
debian/shellinabox.default
vendored
Normal file
8
debian/shellinabox.default
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Should shellinaboxd start automatically
|
||||
SHELLINABOX_DAEMON_START=1
|
||||
|
||||
# TCP port that shellinboxd's webserver listens on
|
||||
SHELLINABOX_PORT=4200
|
||||
|
||||
# Any optional arguments (e.g. extra service definitions)
|
||||
SHELLINABOX_ARGS=
|
2
debian/shellinabox.dirs
vendored
Normal file
2
debian/shellinabox.dirs
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
usr/bin
|
||||
usr/share/man/man1
|
106
debian/shellinabox.init
vendored
Executable file
106
debian/shellinabox.init
vendored
Executable file
|
@ -0,0 +1,106 @@
|
|||
#!/bin/sh
|
||||
### BEGIN INIT INFO
|
||||
# Provides: shellinabox
|
||||
# Required-Start: $network $remote_fs
|
||||
# Required-Stop: $network $remote_fs
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Shell In A Box Daemon
|
||||
# Description: Daemon for publishing a login shell at
|
||||
# http://localhost:4200
|
||||
### END INIT INFO
|
||||
|
||||
PATH=/sbin:/bin:/usr/sbin:/usr/bin
|
||||
DESC="Shell In A Box Daemon"
|
||||
NAME="shellinabox"
|
||||
DAEMON="/usr/bin/shellinaboxd"
|
||||
PIDFILE="/var/run/shellinaboxd.pid"
|
||||
SCRIPTNAME=/etc/init.d/$NAME
|
||||
|
||||
# Gracefully exit if the package has been removed.
|
||||
test -x $DAEMON || exit 0
|
||||
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
# Include shellinabox defaults if available.
|
||||
test -f /etc/default/shellinabox && . /etc/default/shellinabox
|
||||
|
||||
#
|
||||
# Function that starts the daemon/service.
|
||||
#
|
||||
d_start() {
|
||||
if [ -z "$SHELLINABOX_DAEMON_START" -o \
|
||||
"$SHELLINABOX_DAEMON_START" = "0" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
start-stop-daemon --start --oknodo --quiet --pidfile "$PIDFILE" \
|
||||
--exec "$DAEMON" -- -q --background="$PIDFILE" \
|
||||
-c /var/lib/shellinabox -p ${SHELLINABOX_PORT:-4200} \
|
||||
-u shellinabox -g shellinabox ${SHELLINABOX_ARGS}
|
||||
}
|
||||
|
||||
#
|
||||
# Function that stops the daemon/service.
|
||||
#
|
||||
d_stop() {
|
||||
start-stop-daemon --stop --oknodo --pidfile "$PIDFILE"
|
||||
rm -f "$PIDFILE"
|
||||
}
|
||||
|
||||
#
|
||||
# Function that reloads the config file for the daemon/service.
|
||||
#
|
||||
d_reload() {
|
||||
# Only reload if there are no active sessions running
|
||||
[ -r "$PIDFILE" ] &&
|
||||
[ `ps o pid= --ppid "\`cat "$PIDFILE"\`\`ps o pid= --ppid \
|
||||
\\\`cat "$PIDFILE"\\\`|
|
||||
xargs -r -n 1 printf ',%s'\`" |
|
||||
wc -l` -gt 1 ] &&
|
||||
return 1
|
||||
|
||||
d_stop
|
||||
d_start
|
||||
}
|
||||
|
||||
#
|
||||
# Function that check the status of the daemon/service.
|
||||
#
|
||||
d_status() {
|
||||
[ -r "$PIDFILE" && kill -0 `cat "$PIDFILE"` ] &&
|
||||
echo "$DESC is running" || echo "$DESC is not running"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
log_daemon_msg "Starting $DESC" "$NAME"
|
||||
d_start
|
||||
log_end_msg $?
|
||||
;;
|
||||
stop)
|
||||
log_daemon_msg "Stopping $DESC" "$NAME"
|
||||
d_stop
|
||||
log_end_msg $?
|
||||
;;
|
||||
reload)
|
||||
log_daemon_msg "Reloading services for $DESC" "$NAME"
|
||||
d_reload
|
||||
log_end_msg $?
|
||||
;;
|
||||
restart|force-reload)
|
||||
log_daemon_msg "Restarting $DESC" "$NAME"
|
||||
d_stop
|
||||
d_start
|
||||
log_end_msg $?
|
||||
;;
|
||||
status)
|
||||
d_status
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload|reload}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
4
debian/shellinabox.install
vendored
Normal file
4
debian/shellinabox.install
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
usr/bin/*
|
||||
usr/share/man/man1/*
|
||||
debian/tmp/usr/bin/*
|
||||
debian/tmp/usr/share/man/man1/*
|
42
debian/shellinabox.postinst
vendored
Executable file
42
debian/shellinabox.postinst
vendored
Executable file
|
@ -0,0 +1,42 @@
|
|||
#!/bin/sh
|
||||
# postinst script for shellinabox
|
||||
|
||||
set -e
|
||||
|
||||
# summary of how this script can be called:
|
||||
# * <postinst> `configure' <most-recently-configured-version>
|
||||
# * <old-postinst> `abort-upgrade' <new version>
|
||||
# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
|
||||
# <new-version>
|
||||
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
|
||||
# <failed-install-package> <version> `removing'
|
||||
# <conflicting-package> <version>
|
||||
# for details, see http://www.debian.org/doc/debian-policy/ or
|
||||
# the debian-policy package
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
if ! getent passwd shellinabox >/dev/null; then
|
||||
adduser --disabled-password --quiet --system \
|
||||
--home /var/lib/shellinabox --gecos "Shell In A Box" \
|
||||
--group shellinabox
|
||||
fi
|
||||
;;
|
||||
abort-upgrade|abort-remove|abort-deconfigure)
|
||||
;;
|
||||
*)
|
||||
echo "postinst called with unknown argument \`$1'" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Automatically added by dh_installinit
|
||||
if [ -x "/etc/init.d/shellinabox" ]; then
|
||||
update-rc.d shellinabox start 30 2 3 4 5 . stop 01 0 1 6 . >/dev/null
|
||||
if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then
|
||||
invoke-rc.d shellinabox start || exit $?
|
||||
else
|
||||
/etc/init.d/shellinabox start || exit $?
|
||||
fi
|
||||
fi
|
||||
# End automatically added section
|
15
debian/shellinabox.postrm
vendored
Executable file
15
debian/shellinabox.postrm
vendored
Executable file
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
# Automatically added by dh_installinit
|
||||
if [ "$1" = "purge" ] ; then
|
||||
update-rc.d shellinabox remove >/dev/null || exit $?
|
||||
fi
|
||||
# End automatically added section
|
||||
|
||||
|
||||
if [ "$1" = "purge" ] ; then
|
||||
deluser --quiet --system --remove-home shellinabox > /dev/null || true
|
||||
delgroup --quiet --system shellinabox > /dev/null || true
|
||||
fi
|
589
depcomp
Normal file
589
depcomp
Normal file
|
@ -0,0 +1,589 @@
|
|||
#! /bin/sh
|
||||
# depcomp - compile a program generating dependencies as side-effects
|
||||
|
||||
scriptversion=2007-03-29.01
|
||||
|
||||
# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007 Free Software
|
||||
# Foundation, Inc.
|
||||
|
||||
# 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, 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 Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
echo "$0: No command. Try \`$0 --help' for more information." 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<\EOF
|
||||
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
|
||||
|
||||
Run PROGRAMS ARGS to compile a file, generating dependencies
|
||||
as side-effects.
|
||||
|
||||
Environment variables:
|
||||
depmode Dependency tracking mode.
|
||||
source Source file read by `PROGRAMS ARGS'.
|
||||
object Object file output by `PROGRAMS ARGS'.
|
||||
DEPDIR directory where to store dependencies.
|
||||
depfile Dependency file to output.
|
||||
tmpdepfile Temporary file to use when outputing dependencies.
|
||||
libtool Whether libtool is used (yes/no).
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v | --v*)
|
||||
echo "depcomp $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
esac
|
||||
|
||||
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
|
||||
echo "depcomp: Variables source, object and depmode must be set" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
|
||||
depfile=${depfile-`echo "$object" |
|
||||
sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
|
||||
tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
|
||||
|
||||
rm -f "$tmpdepfile"
|
||||
|
||||
# Some modes work just like other modes, but use different flags. We
|
||||
# parameterize here, but still list the modes in the big case below,
|
||||
# to make depend.m4 easier to write. Note that we *cannot* use a case
|
||||
# here, because this file can only contain one case statement.
|
||||
if test "$depmode" = hp; then
|
||||
# HP compiler uses -M and no extra arg.
|
||||
gccflag=-M
|
||||
depmode=gcc
|
||||
fi
|
||||
|
||||
if test "$depmode" = dashXmstdout; then
|
||||
# This is just like dashmstdout with a different argument.
|
||||
dashmflag=-xM
|
||||
depmode=dashmstdout
|
||||
fi
|
||||
|
||||
case "$depmode" in
|
||||
gcc3)
|
||||
## gcc 3 implements dependency tracking that does exactly what
|
||||
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
|
||||
## it if -MD -MP comes after the -MF stuff. Hmm.
|
||||
## Unfortunately, FreeBSD c89 acceptance of flags depends upon
|
||||
## the command line argument order; so add the flags where they
|
||||
## appear in depend2.am. Note that the slowdown incurred here
|
||||
## affects only configure: in makefiles, %FASTDEP% shortcuts this.
|
||||
for arg
|
||||
do
|
||||
case $arg in
|
||||
-c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
|
||||
*) set fnord "$@" "$arg" ;;
|
||||
esac
|
||||
shift # fnord
|
||||
shift # $arg
|
||||
done
|
||||
"$@"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
mv "$tmpdepfile" "$depfile"
|
||||
;;
|
||||
|
||||
gcc)
|
||||
## There are various ways to get dependency output from gcc. Here's
|
||||
## why we pick this rather obscure method:
|
||||
## - Don't want to use -MD because we'd like the dependencies to end
|
||||
## up in a subdir. Having to rename by hand is ugly.
|
||||
## (We might end up doing this anyway to support other compilers.)
|
||||
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
|
||||
## -MM, not -M (despite what the docs say).
|
||||
## - Using -M directly means running the compiler twice (even worse
|
||||
## than renaming).
|
||||
if test -z "$gccflag"; then
|
||||
gccflag=-MD,
|
||||
fi
|
||||
"$@" -Wp,"$gccflag$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
|
||||
## The second -e expression handles DOS-style file names with drive letters.
|
||||
sed -e 's/^[^:]*: / /' \
|
||||
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
|
||||
## This next piece of magic avoids the `deleted header file' problem.
|
||||
## The problem is that when a header file which appears in a .P file
|
||||
## is deleted, the dependency causes make to die (because there is
|
||||
## typically no way to rebuild the header). We avoid this by adding
|
||||
## dummy dependencies for each header file. Too bad gcc doesn't do
|
||||
## this for us directly.
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" |
|
||||
## Some versions of gcc put a space before the `:'. On the theory
|
||||
## that the space means something, we add a space to the output as
|
||||
## well.
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
hp)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
sgi)
|
||||
if test "$libtool" = yes; then
|
||||
"$@" "-Wp,-MDupdate,$tmpdepfile"
|
||||
else
|
||||
"$@" -MDupdate "$tmpdepfile"
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
|
||||
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
|
||||
echo "$object : \\" > "$depfile"
|
||||
|
||||
# Clip off the initial element (the dependent). Don't try to be
|
||||
# clever and replace this with sed code, as IRIX sed won't handle
|
||||
# lines with more than a fixed number of characters (4096 in
|
||||
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
|
||||
# the IRIX cc adds comments like `#:fec' to the end of the
|
||||
# dependency line.
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
|
||||
tr '
|
||||
' ' ' >> $depfile
|
||||
echo >> $depfile
|
||||
|
||||
# The second pass generates a dummy entry for each header file.
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
|
||||
>> $depfile
|
||||
else
|
||||
# The sourcefile does not contain any dependencies, so just
|
||||
# store a dummy comment line, to avoid errors with the Makefile
|
||||
# "include basename.Plo" scheme.
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
aix)
|
||||
# The C for AIX Compiler uses -M and outputs the dependencies
|
||||
# in a .u file. In older versions, this file always lives in the
|
||||
# current directory. Also, the AIX compiler puts `$object:' at the
|
||||
# start of each line; $object doesn't have directory information.
|
||||
# Version 6 uses the directory in both cases.
|
||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
||||
test "x$dir" = "x$object" && dir=
|
||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
||||
if test "$libtool" = yes; then
|
||||
tmpdepfile1=$dir$base.u
|
||||
tmpdepfile2=$base.u
|
||||
tmpdepfile3=$dir.libs/$base.u
|
||||
"$@" -Wc,-M
|
||||
else
|
||||
tmpdepfile1=$dir$base.u
|
||||
tmpdepfile2=$dir$base.u
|
||||
tmpdepfile3=$dir$base.u
|
||||
"$@" -M
|
||||
fi
|
||||
stat=$?
|
||||
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
# Each line is of the form `foo.o: dependent.h'.
|
||||
# Do two passes, one to just change these to
|
||||
# `$object: dependent.h' and one to simply `dependent.h:'.
|
||||
sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
|
||||
# That's a tab and a space in the [].
|
||||
sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
# The sourcefile does not contain any dependencies, so just
|
||||
# store a dummy comment line, to avoid errors with the Makefile
|
||||
# "include basename.Plo" scheme.
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
icc)
|
||||
# Intel's C compiler understands `-MD -MF file'. However on
|
||||
# icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
|
||||
# ICC 7.0 will fill foo.d with something like
|
||||
# foo.o: sub/foo.c
|
||||
# foo.o: sub/foo.h
|
||||
# which is wrong. We want:
|
||||
# sub/foo.o: sub/foo.c
|
||||
# sub/foo.o: sub/foo.h
|
||||
# sub/foo.c:
|
||||
# sub/foo.h:
|
||||
# ICC 7.1 will output
|
||||
# foo.o: sub/foo.c sub/foo.h
|
||||
# and will wrap long lines using \ :
|
||||
# foo.o: sub/foo.c ... \
|
||||
# sub/foo.h ... \
|
||||
# ...
|
||||
|
||||
"$@" -MD -MF "$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
# Each line is of the form `foo.o: dependent.h',
|
||||
# or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
|
||||
# Do two passes, one to just change these to
|
||||
# `$object: dependent.h' and one to simply `dependent.h:'.
|
||||
sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
|
||||
# Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
# correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
|
||||
sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
hp2)
|
||||
# The "hp" stanza above does not work with aCC (C++) and HP's ia64
|
||||
# compilers, which have integrated preprocessors. The correct option
|
||||
# to use with these is +Maked; it writes dependencies to a file named
|
||||
# 'foo.d', which lands next to the object file, wherever that
|
||||
# happens to be.
|
||||
# Much of this is similar to the tru64 case; see comments there.
|
||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
||||
test "x$dir" = "x$object" && dir=
|
||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
||||
if test "$libtool" = yes; then
|
||||
tmpdepfile1=$dir$base.d
|
||||
tmpdepfile2=$dir.libs/$base.d
|
||||
"$@" -Wc,+Maked
|
||||
else
|
||||
tmpdepfile1=$dir$base.d
|
||||
tmpdepfile2=$dir$base.d
|
||||
"$@" +Maked
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
|
||||
# Add `dependent.h:' lines.
|
||||
sed -ne '2,${; s/^ *//; s/ \\*$//; s/$/:/; p;}' "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile" "$tmpdepfile2"
|
||||
;;
|
||||
|
||||
tru64)
|
||||
# The Tru64 compiler uses -MD to generate dependencies as a side
|
||||
# effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
|
||||
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
|
||||
# dependencies in `foo.d' instead, so we check for that too.
|
||||
# Subdirectories are respected.
|
||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
||||
test "x$dir" = "x$object" && dir=
|
||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
||||
|
||||
if test "$libtool" = yes; then
|
||||
# With Tru64 cc, shared objects can also be used to make a
|
||||
# static library. This mechanism is used in libtool 1.4 series to
|
||||
# handle both shared and static libraries in a single compilation.
|
||||
# With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
|
||||
#
|
||||
# With libtool 1.5 this exception was removed, and libtool now
|
||||
# generates 2 separate objects for the 2 libraries. These two
|
||||
# compilations output dependencies in $dir.libs/$base.o.d and
|
||||
# in $dir$base.o.d. We have to check for both files, because
|
||||
# one of the two compilations can be disabled. We should prefer
|
||||
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
|
||||
# automatically cleaned when .libs/ is deleted, while ignoring
|
||||
# the former would cause a distcleancheck panic.
|
||||
tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
|
||||
tmpdepfile2=$dir$base.o.d # libtool 1.5
|
||||
tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
|
||||
tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
|
||||
"$@" -Wc,-MD
|
||||
else
|
||||
tmpdepfile1=$dir$base.o.d
|
||||
tmpdepfile2=$dir$base.d
|
||||
tmpdepfile3=$dir$base.d
|
||||
tmpdepfile4=$dir$base.d
|
||||
"$@" -MD
|
||||
fi
|
||||
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
|
||||
# That's a tab and a space in the [].
|
||||
sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
#nosideeffect)
|
||||
# This comment above is used by automake to tell side-effect
|
||||
# dependency tracking mechanisms from slower ones.
|
||||
|
||||
dashmstdout)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout, regardless of -o.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test $1 != '--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
# Remove `-o $object'.
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case $arg in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift # fnord
|
||||
shift # $arg
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
test -z "$dashmflag" && dashmflag=-M
|
||||
# Require at least two characters before searching for `:'
|
||||
# in the target name. This is to cope with DOS-style filenames:
|
||||
# a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
|
||||
"$@" $dashmflag |
|
||||
sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
cat < "$tmpdepfile" > "$depfile"
|
||||
tr ' ' '
|
||||
' < "$tmpdepfile" | \
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
dashXmstdout)
|
||||
# This case only exists to satisfy depend.m4. It is never actually
|
||||
# run, as this mode is specially recognized in the preamble.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
makedepend)
|
||||
"$@" || exit $?
|
||||
# Remove any Libtool call
|
||||
if test "$libtool" = yes; then
|
||||
while test $1 != '--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
# X makedepend
|
||||
shift
|
||||
cleared=no
|
||||
for arg in "$@"; do
|
||||
case $cleared in
|
||||
no)
|
||||
set ""; shift
|
||||
cleared=yes ;;
|
||||
esac
|
||||
case "$arg" in
|
||||
-D*|-I*)
|
||||
set fnord "$@" "$arg"; shift ;;
|
||||
# Strip any option that makedepend may not understand. Remove
|
||||
# the object too, otherwise makedepend will parse it as a source file.
|
||||
-*|$object)
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"; shift ;;
|
||||
esac
|
||||
done
|
||||
obj_suffix="`echo $object | sed 's/^.*\././'`"
|
||||
touch "$tmpdepfile"
|
||||
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
|
||||
rm -f "$depfile"
|
||||
cat < "$tmpdepfile" > "$depfile"
|
||||
sed '1,2d' "$tmpdepfile" | tr ' ' '
|
||||
' | \
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile" "$tmpdepfile".bak
|
||||
;;
|
||||
|
||||
cpp)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test $1 != '--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
# Remove `-o $object'.
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case $arg in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift # fnord
|
||||
shift # $arg
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
"$@" -E |
|
||||
sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
|
||||
sed '$ s: \\$::' > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
cat < "$tmpdepfile" >> "$depfile"
|
||||
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvisualcpp)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout, regardless of -o,
|
||||
# because we must use -o when running libtool.
|
||||
"$@" || exit $?
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case "$arg" in
|
||||
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
|
||||
set fnord "$@"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
"$@" -E |
|
||||
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
. "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile"
|
||||
echo " " >> "$depfile"
|
||||
. "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
none)
|
||||
exec "$@"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown depmode $depmode" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-end: "$"
|
||||
# End:
|
519
install-sh
Normal file
519
install-sh
Normal file
|
@ -0,0 +1,519 @@
|
|||
#!/bin/sh
|
||||
# install - install a program, script, or datafile
|
||||
|
||||
scriptversion=2006-12-25.00
|
||||
|
||||
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||
# following copyright and license.
|
||||
#
|
||||
# Copyright (C) 1994 X Consortium
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
||||
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Except as contained in this notice, the name of the X Consortium shall not
|
||||
# be used in advertising or otherwise to promote the sale, use or other deal-
|
||||
# ings in this Software without prior written authorization from the X Consor-
|
||||
# tium.
|
||||
#
|
||||
#
|
||||
# FSF changes to this file are in the public domain.
|
||||
#
|
||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||
# `make' implicit rules from creating a file called install from it
|
||||
# when there is no Makefile.
|
||||
#
|
||||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch.
|
||||
|
||||
nl='
|
||||
'
|
||||
IFS=" "" $nl"
|
||||
|
||||
# set DOITPROG to echo to test this script
|
||||
|
||||
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||
doit=${DOITPROG-}
|
||||
if test -z "$doit"; then
|
||||
doit_exec=exec
|
||||
else
|
||||
doit_exec=$doit
|
||||
fi
|
||||
|
||||
# Put in absolute file names if you don't have them in your path;
|
||||
# or use environment vars.
|
||||
|
||||
chgrpprog=${CHGRPPROG-chgrp}
|
||||
chmodprog=${CHMODPROG-chmod}
|
||||
chownprog=${CHOWNPROG-chown}
|
||||
cmpprog=${CMPPROG-cmp}
|
||||
cpprog=${CPPROG-cp}
|
||||
mkdirprog=${MKDIRPROG-mkdir}
|
||||
mvprog=${MVPROG-mv}
|
||||
rmprog=${RMPROG-rm}
|
||||
stripprog=${STRIPPROG-strip}
|
||||
|
||||
posix_glob='?'
|
||||
initialize_posix_glob='
|
||||
test "$posix_glob" != "?" || {
|
||||
if (set -f) 2>/dev/null; then
|
||||
posix_glob=
|
||||
else
|
||||
posix_glob=:
|
||||
fi
|
||||
}
|
||||
'
|
||||
|
||||
posix_mkdir=
|
||||
|
||||
# Desired mode of installed file.
|
||||
mode=0755
|
||||
|
||||
chgrpcmd=
|
||||
chmodcmd=$chmodprog
|
||||
chowncmd=
|
||||
mvcmd=$mvprog
|
||||
rmcmd="$rmprog -f"
|
||||
stripcmd=
|
||||
|
||||
src=
|
||||
dst=
|
||||
dir_arg=
|
||||
dst_arg=
|
||||
|
||||
copy_on_change=false
|
||||
no_target_directory=
|
||||
|
||||
usage="\
|
||||
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
||||
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
||||
or: $0 [OPTION]... -d DIRECTORIES...
|
||||
|
||||
In the 1st form, copy SRCFILE to DSTFILE.
|
||||
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
||||
In the 4th, create DIRECTORIES.
|
||||
|
||||
Options:
|
||||
--help display this help and exit.
|
||||
--version display version info and exit.
|
||||
|
||||
-c (ignored)
|
||||
-C install only if different (preserve the last data modification time)
|
||||
-d create directories instead of installing files.
|
||||
-g GROUP $chgrpprog installed files to GROUP.
|
||||
-m MODE $chmodprog installed files to MODE.
|
||||
-o USER $chownprog installed files to USER.
|
||||
-s $stripprog installed files.
|
||||
-t DIRECTORY install into DIRECTORY.
|
||||
-T report an error if DSTFILE is a directory.
|
||||
|
||||
Environment variables override the default commands:
|
||||
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
|
||||
RMPROG STRIPPROG
|
||||
"
|
||||
|
||||
while test $# -ne 0; do
|
||||
case $1 in
|
||||
-c) ;;
|
||||
|
||||
-C) copy_on_change=true;;
|
||||
|
||||
-d) dir_arg=true;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift;;
|
||||
|
||||
--help) echo "$usage"; exit $?;;
|
||||
|
||||
-m) mode=$2
|
||||
case $mode in
|
||||
*' '* | *' '* | *'
|
||||
'* | *'*'* | *'?'* | *'['*)
|
||||
echo "$0: invalid mode: $mode" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift;;
|
||||
|
||||
-s) stripcmd=$stripprog;;
|
||||
|
||||
-t) dst_arg=$2
|
||||
shift;;
|
||||
|
||||
-T) no_target_directory=true;;
|
||||
|
||||
--version) echo "$0 $scriptversion"; exit $?;;
|
||||
|
||||
--) shift
|
||||
break;;
|
||||
|
||||
-*) echo "$0: invalid option: $1" >&2
|
||||
exit 1;;
|
||||
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
|
||||
# When -d is used, all remaining arguments are directories to create.
|
||||
# When -t is used, the destination is already specified.
|
||||
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||
for arg
|
||||
do
|
||||
if test -n "$dst_arg"; then
|
||||
# $@ is not empty: it contains at least $arg.
|
||||
set fnord "$@" "$dst_arg"
|
||||
shift # fnord
|
||||
fi
|
||||
shift # arg
|
||||
dst_arg=$arg
|
||||
done
|
||||
fi
|
||||
|
||||
if test $# -eq 0; then
|
||||
if test -z "$dir_arg"; then
|
||||
echo "$0: no input file specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
# It's OK to call `install-sh -d' without argument.
|
||||
# This can happen when creating conditional directories.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
trap '(exit $?); exit' 1 2 13 15
|
||||
|
||||
# Set umask so as not to create temps with too-generous modes.
|
||||
# However, 'strip' requires both read and write access to temps.
|
||||
case $mode in
|
||||
# Optimize common cases.
|
||||
*644) cp_umask=133;;
|
||||
*755) cp_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw='% 200'
|
||||
fi
|
||||
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
||||
*)
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw=,u+rw
|
||||
fi
|
||||
cp_umask=$mode$u_plus_rw;;
|
||||
esac
|
||||
fi
|
||||
|
||||
for src
|
||||
do
|
||||
# Protect names starting with `-'.
|
||||
case $src in
|
||||
-*) src=./$src;;
|
||||
esac
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
dst=$src
|
||||
dstdir=$dst
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
else
|
||||
|
||||
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# if $src (and thus $dsttmp) contains '*'.
|
||||
if test ! -f "$src" && test ! -d "$src"; then
|
||||
echo "$0: $src does not exist." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$dst_arg"; then
|
||||
echo "$0: no destination specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dst=$dst_arg
|
||||
# Protect names starting with `-'.
|
||||
case $dst in
|
||||
-*) dst=./$dst;;
|
||||
esac
|
||||
|
||||
# If destination is a directory, append the input filename; won't work
|
||||
# if double slashes aren't ignored.
|
||||
if test -d "$dst"; then
|
||||
if test -n "$no_target_directory"; then
|
||||
echo "$0: $dst_arg: Is a directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
dstdir=$dst
|
||||
dst=$dstdir/`basename "$src"`
|
||||
dstdir_status=0
|
||||
else
|
||||
# Prefer dirname, but fall back on a substitute if dirname fails.
|
||||
dstdir=`
|
||||
(dirname "$dst") 2>/dev/null ||
|
||||
expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
|
||||
X"$dst" : 'X\(//\)[^/]' \| \
|
||||
X"$dst" : 'X\(//\)$' \| \
|
||||
X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
|
||||
echo X"$dst" |
|
||||
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\/\)[^/].*/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\/\)$/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\).*/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
s/.*/./; q'
|
||||
`
|
||||
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
fi
|
||||
fi
|
||||
|
||||
obsolete_mkdir_used=false
|
||||
|
||||
if test $dstdir_status != 0; then
|
||||
case $posix_mkdir in
|
||||
'')
|
||||
# Create intermediate dirs using mode 755 as modified by the umask.
|
||||
# This is like FreeBSD 'install' as of 1997-10-28.
|
||||
umask=`umask`
|
||||
case $stripcmd.$umask in
|
||||
# Optimize common cases.
|
||||
*[2367][2367]) mkdir_umask=$umask;;
|
||||
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
mkdir_umask=`expr $umask + 22 \
|
||||
- $umask % 100 % 40 + $umask % 20 \
|
||||
- $umask % 10 % 4 + $umask % 2
|
||||
`;;
|
||||
*) mkdir_umask=$umask,go-w;;
|
||||
esac
|
||||
|
||||
# With -d, create the new directory with the user-specified mode.
|
||||
# Otherwise, rely on $mkdir_umask.
|
||||
if test -n "$dir_arg"; then
|
||||
mkdir_mode=-m$mode
|
||||
else
|
||||
mkdir_mode=
|
||||
fi
|
||||
|
||||
posix_mkdir=false
|
||||
case $umask in
|
||||
*[123567][0-7][0-7])
|
||||
# POSIX mkdir -p sets u+wx bits regardless of umask, which
|
||||
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
|
||||
;;
|
||||
*)
|
||||
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
|
||||
|
||||
if (umask $mkdir_umask &&
|
||||
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
|
||||
then
|
||||
if test -z "$dir_arg" || {
|
||||
# Check for POSIX incompatibilities with -m.
|
||||
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||
# other-writeable bit of parent directory when it shouldn't.
|
||||
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||
ls_ld_tmpdir=`ls -ld "$tmpdir"`
|
||||
case $ls_ld_tmpdir in
|
||||
d????-?r-*) different_mode=700;;
|
||||
d????-?--*) different_mode=755;;
|
||||
*) false;;
|
||||
esac &&
|
||||
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
|
||||
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
|
||||
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||
}
|
||||
}
|
||||
then posix_mkdir=:
|
||||
fi
|
||||
rmdir "$tmpdir/d" "$tmpdir"
|
||||
else
|
||||
# Remove any dirs left behind by ancient mkdir implementations.
|
||||
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
|
||||
fi
|
||||
trap '' 0;;
|
||||
esac;;
|
||||
esac
|
||||
|
||||
if
|
||||
$posix_mkdir && (
|
||||
umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||
)
|
||||
then :
|
||||
else
|
||||
|
||||
# The umask is ridiculous, or mkdir does not conform to POSIX,
|
||||
# or it failed possibly due to a race condition. Create the
|
||||
# directory the slow way, step by step, checking for races as we go.
|
||||
|
||||
case $dstdir in
|
||||
/*) prefix='/';;
|
||||
-*) prefix='./';;
|
||||
*) prefix='';;
|
||||
esac
|
||||
|
||||
eval "$initialize_posix_glob"
|
||||
|
||||
oIFS=$IFS
|
||||
IFS=/
|
||||
$posix_glob set -f
|
||||
set fnord $dstdir
|
||||
shift
|
||||
$posix_glob set +f
|
||||
IFS=$oIFS
|
||||
|
||||
prefixes=
|
||||
|
||||
for d
|
||||
do
|
||||
test -z "$d" && continue
|
||||
|
||||
prefix=$prefix$d
|
||||
if test -d "$prefix"; then
|
||||
prefixes=
|
||||
else
|
||||
if $posix_mkdir; then
|
||||
(umask=$mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||
# Don't fail if two instances are running concurrently.
|
||||
test -d "$prefix" || exit 1
|
||||
else
|
||||
case $prefix in
|
||||
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||
*) qprefix=$prefix;;
|
||||
esac
|
||||
prefixes="$prefixes '$qprefix'"
|
||||
fi
|
||||
fi
|
||||
prefix=$prefix/
|
||||
done
|
||||
|
||||
if test -n "$prefixes"; then
|
||||
# Don't fail if two instances are running concurrently.
|
||||
(umask $mkdir_umask &&
|
||||
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||
test -d "$dstdir" || exit 1
|
||||
obsolete_mkdir_used=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
|
||||
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
|
||||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
|
||||
else
|
||||
|
||||
# Make a couple of temp file names in the proper directory.
|
||||
dsttmp=$dstdir/_inst.$$_
|
||||
rmtmp=$dstdir/_rm.$$_
|
||||
|
||||
# Trap to clean up those temp files at exit.
|
||||
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
|
||||
|
||||
# Copy the file name to the temp name.
|
||||
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
|
||||
|
||||
# and set any options; do chmod last to preserve setuid bits.
|
||||
#
|
||||
# If any of these fail, we abort the whole thing. If we want to
|
||||
# ignore errors from any of these, just make sure not to ignore
|
||||
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
||||
#
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
|
||||
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
|
||||
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||
|
||||
# If -C, don't bother to copy if it wouldn't change the file.
|
||||
if $copy_on_change &&
|
||||
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||
|
||||
eval "$initialize_posix_glob" &&
|
||||
$posix_glob set -f &&
|
||||
set X $old && old=:$2:$4:$5:$6 &&
|
||||
set X $new && new=:$2:$4:$5:$6 &&
|
||||
$posix_glob set +f &&
|
||||
|
||||
test "$old" = "$new" &&
|
||||
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
|
||||
then
|
||||
rm -f "$dsttmp"
|
||||
else
|
||||
# Rename the file to the real destination.
|
||||
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
|
||||
|
||||
# The rename failed, perhaps because mv can't rename something else
|
||||
# to itself, or perhaps because mv is so ancient that it does not
|
||||
# support -f.
|
||||
{
|
||||
# Now remove or move aside any old file at destination location.
|
||||
# We try this two ways since rm can't unlink itself on some
|
||||
# systems and the destination file might be busy for other
|
||||
# reasons. In this case, the final cleanup might fail but the new
|
||||
# file should still install successfully.
|
||||
{
|
||||
test ! -f "$dst" ||
|
||||
$doit $rmcmd -f "$dst" 2>/dev/null ||
|
||||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
|
||||
} ||
|
||||
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||
(exit 1); exit 1
|
||||
}
|
||||
} &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
$doit $mvcmd "$dsttmp" "$dst"
|
||||
}
|
||||
fi || exit 1
|
||||
|
||||
trap '' 0
|
||||
fi
|
||||
done
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-end: "$"
|
||||
# End:
|
241
libhttp/hashmap.c
Normal file
241
libhttp/hashmap.c
Normal file
|
@ -0,0 +1,241 @@
|
|||
// hashmap.c -- Basic implementation of a hashmap abstract data type
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libhttp/hashmap.h"
|
||||
#include "logging/logging.h"
|
||||
|
||||
struct HashMap *newHashMap(void (*destructor)(void *arg, char *key,
|
||||
char *value),
|
||||
void *arg) {
|
||||
struct HashMap *hashmap;
|
||||
check(hashmap = malloc(sizeof(struct HashMap)));
|
||||
initHashMap(hashmap, destructor, arg);
|
||||
return hashmap;
|
||||
}
|
||||
|
||||
void initHashMap(struct HashMap *hashmap,
|
||||
void (*destructor)(void *arg, char *key, char *value),
|
||||
void *arg) {
|
||||
hashmap->destructor = destructor;
|
||||
hashmap->arg = arg;
|
||||
hashmap->entries = NULL;
|
||||
hashmap->mapSize = 0;
|
||||
hashmap->numEntries = 0;
|
||||
}
|
||||
|
||||
void destroyHashMap(struct HashMap *hashmap) {
|
||||
if (hashmap) {
|
||||
for (int i = 0; i < hashmap->mapSize; i++) {
|
||||
if (hashmap->entries[i]) {
|
||||
for (int j = 0; hashmap->entries[i][j].key; j++) {
|
||||
if (hashmap->destructor) {
|
||||
hashmap->destructor(hashmap->arg,
|
||||
(char *)hashmap->entries[i][j].key,
|
||||
(char *)hashmap->entries[i][j].value);
|
||||
}
|
||||
}
|
||||
free(hashmap->entries[i]);
|
||||
}
|
||||
}
|
||||
free(hashmap->entries);
|
||||
}
|
||||
}
|
||||
|
||||
void deleteHashMap(struct HashMap *hashmap) {
|
||||
destroyHashMap(hashmap);
|
||||
free(hashmap);
|
||||
}
|
||||
|
||||
static unsigned int stringHashFunc(const char *s) {
|
||||
unsigned int h = 0;
|
||||
while (*s) {
|
||||
h = 31*h + *(unsigned char *)s++;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
const void *addToHashMap(struct HashMap *hashmap, const char *key,
|
||||
const char *value) {
|
||||
if (hashmap->numEntries + 1 > (hashmap->mapSize * 8)/10) {
|
||||
struct HashMap newMap;
|
||||
newMap.numEntries = hashmap->numEntries;
|
||||
if (hashmap->mapSize == 0) {
|
||||
newMap.mapSize = 32;
|
||||
} else if (hashmap->mapSize < 1024) {
|
||||
newMap.mapSize = 2*hashmap->mapSize;
|
||||
} else {
|
||||
newMap.mapSize = hashmap->mapSize + 1024;
|
||||
}
|
||||
check(newMap.entries = calloc(sizeof(void *), newMap.mapSize));
|
||||
for (int i = 0; i < hashmap->mapSize; i++) {
|
||||
if (!hashmap->entries[i]) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; hashmap->entries[i][j].key; j++) {
|
||||
addToHashMap(&newMap, hashmap->entries[i][j].key,
|
||||
hashmap->entries[i][j].value);
|
||||
}
|
||||
free(hashmap->entries[i]);
|
||||
}
|
||||
free(hashmap->entries);
|
||||
hashmap->entries = newMap.entries;
|
||||
hashmap->mapSize = newMap.mapSize;
|
||||
hashmap->numEntries = newMap.numEntries;
|
||||
}
|
||||
unsigned hash = stringHashFunc(key);
|
||||
int idx = hash % hashmap->mapSize;
|
||||
int i = 0;
|
||||
if (hashmap->entries[idx]) {
|
||||
for (i = 0; hashmap->entries[idx][i].key; i++) {
|
||||
if (!strcmp(hashmap->entries[idx][i].key, key)) {
|
||||
if (hashmap->destructor) {
|
||||
hashmap->destructor(hashmap->arg,
|
||||
(char *)hashmap->entries[idx][i].key,
|
||||
(char *)hashmap->entries[idx][i].value);
|
||||
}
|
||||
hashmap->entries[idx][i].key = key;
|
||||
hashmap->entries[idx][i].value = value;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
check(hashmap->entries[idx] = realloc(hashmap->entries[idx],
|
||||
(i+2)*sizeof(*hashmap->entries[idx])));
|
||||
hashmap->entries[idx][i].key = key;
|
||||
hashmap->entries[idx][i].value = value;
|
||||
memset(&hashmap->entries[idx][i+1], 0, sizeof(*hashmap->entries[idx]));
|
||||
hashmap->numEntries++;
|
||||
return value;
|
||||
}
|
||||
|
||||
void deleteFromHashMap(struct HashMap *hashmap, const char *key) {
|
||||
if (hashmap->mapSize == 0) {
|
||||
return;
|
||||
}
|
||||
unsigned hash = stringHashFunc(key);
|
||||
int idx = hash % hashmap->mapSize;
|
||||
if (!hashmap->entries[idx]) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; hashmap->entries[idx][i].key; i++) {
|
||||
if (!strcmp(hashmap->entries[idx][i].key, key)) {
|
||||
int j = i + 1;
|
||||
while (hashmap->entries[idx][j].key) {
|
||||
j++;
|
||||
}
|
||||
if (hashmap->destructor) {
|
||||
hashmap->destructor(hashmap->arg,
|
||||
(char *)hashmap->entries[idx][i].key,
|
||||
(char *)hashmap->entries[idx][i].value);
|
||||
}
|
||||
if (i != j-1) {
|
||||
memcpy(&hashmap->entries[idx][i], &hashmap->entries[idx][j-1],
|
||||
sizeof(*hashmap->entries[idx]));
|
||||
}
|
||||
memset(&hashmap->entries[idx][j-1], 0, sizeof(*hashmap->entries[idx]));
|
||||
check(--hashmap->numEntries >= 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char **getRefFromHashMap(const struct HashMap *hashmap, const char *key) {
|
||||
if (hashmap->mapSize == 0) {
|
||||
return NULL;
|
||||
}
|
||||
unsigned hash = stringHashFunc(key);
|
||||
int idx = hash % hashmap->mapSize;
|
||||
if (!hashmap->entries[idx]) {
|
||||
return NULL;
|
||||
}
|
||||
for (int i = 0; hashmap->entries[idx][i].key; i++) {
|
||||
if (!strcmp(hashmap->entries[idx][i].key, key)) {
|
||||
return (char **)&hashmap->entries[idx][i].value;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *getFromHashMap(const struct HashMap *hashmap, const char *key) {
|
||||
char **ref = getRefFromHashMap(hashmap, key);
|
||||
return ref ? *ref : NULL;
|
||||
}
|
||||
|
||||
void iterateOverHashMap(struct HashMap *hashmap,
|
||||
int (*fnc)(void *arg, const char *key, char **value),
|
||||
void *arg) {
|
||||
for (int i = 0; i < hashmap->mapSize; i++) {
|
||||
if (hashmap->entries[i]) {
|
||||
int count = 0;
|
||||
while (hashmap->entries[i][count].key) {
|
||||
count++;
|
||||
}
|
||||
for (int j = 0; j < count; j++) {
|
||||
if (!fnc(arg, hashmap->entries[i][j].key,
|
||||
(char **)&hashmap->entries[i][j].value)) {
|
||||
if (hashmap->destructor) {
|
||||
hashmap->destructor(hashmap->arg,
|
||||
(char *)hashmap->entries[i][j].key,
|
||||
(char *)hashmap->entries[i][j].value);
|
||||
}
|
||||
if (j != count-1) {
|
||||
memcpy(&hashmap->entries[i][j], &hashmap->entries[i][count-1],
|
||||
sizeof(*hashmap->entries[i]));
|
||||
}
|
||||
memset(&hashmap->entries[i][count-1], 0,
|
||||
sizeof(*hashmap->entries[i]));
|
||||
count--;
|
||||
j--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int getHashmapSize(const struct HashMap *hashmap) {
|
||||
return hashmap->numEntries;
|
||||
}
|
80
libhttp/hashmap.h
Normal file
80
libhttp/hashmap.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
// hashmap.h -- Basic implementation of a hashmap abstract data type
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#ifndef HASH_MAP__
|
||||
#define HASH_MAP__
|
||||
|
||||
#include "libhttp/http.h"
|
||||
|
||||
struct HashMap {
|
||||
void (*destructor)(void *arg, char *key, char *value);
|
||||
void *arg;
|
||||
struct {
|
||||
const char *key;
|
||||
const char *value;
|
||||
} **entries;
|
||||
int mapSize;
|
||||
int numEntries;
|
||||
};
|
||||
|
||||
struct HashMap *newHashMap(void (*destructor)(void *arg, char *key,
|
||||
char *value),
|
||||
void *arg);
|
||||
void initHashMap(struct HashMap *hashmap,
|
||||
void (*destructor)(void *arg, char *key, char *value),
|
||||
void *arg);
|
||||
void destroyHashMap(struct HashMap *hashmap);
|
||||
void deleteHashMap(struct HashMap *hashmap);
|
||||
const void *addToHashMap(struct HashMap *hashmap, const char *key,
|
||||
const char *value);
|
||||
void deleteFromHashMap(struct HashMap *hashmap, const char *key);
|
||||
char **getRefFromHashMap(const struct HashMap *hashmap, const char *key);
|
||||
const char *getFromHashMap(const struct HashMap *hashmap, const char *key);
|
||||
void iterateOverHashMap(struct HashMap *hashmap,
|
||||
int (*fnc)(void *arg, const char *key, char **value),
|
||||
void *arg);
|
||||
int getHashmapSize(const struct HashMap *hashmap);
|
||||
|
||||
#endif /* HASH_MAP__ */
|
138
libhttp/http.h
Normal file
138
libhttp/http.h
Normal file
|
@ -0,0 +1,138 @@
|
|||
// http.h -- Library for implementing embedded custom HTTP servers
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#ifndef LIB_HTTP_H__
|
||||
#define LIB_HTTP_H__
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
|
||||
#define HTTP_DONE 0
|
||||
#define HTTP_ERROR 1
|
||||
#define HTTP_READ_MORE 2
|
||||
#define HTTP_SUSPEND 3
|
||||
#define HTTP_PARTIAL_REPLY 4
|
||||
|
||||
#define NOINTR(x) ({ int i__; while ((i__ = (x)) < 0 && errno == EINTR); i__;})
|
||||
|
||||
typedef struct HashMap HashMap;
|
||||
typedef struct HttpConnection HttpConnection;
|
||||
typedef struct ServerConnection ServerConnection;
|
||||
typedef struct Server Server;
|
||||
typedef struct URL URL;
|
||||
|
||||
Server *newServer(int port);
|
||||
void deleteServer(Server *server);
|
||||
void serverRegisterHttpHandler(Server *server, const char *url,
|
||||
int (*handler)(HttpConnection *, void *,
|
||||
const char *, int), void *arg);
|
||||
void serverRegisterStreamingHttpHandler(Server *server, const char *url,
|
||||
int (*handler)(HttpConnection *, void *),
|
||||
void *arg);
|
||||
ServerConnection *serverAddConnection(Server *server, int fd,
|
||||
int (*handleConnection)(ServerConnection *,
|
||||
void *arg, short *events,
|
||||
short revents),
|
||||
void (*destroyConnection)(void *arg),
|
||||
void *arg);
|
||||
void serverDeleteConnection(Server *server, int fd);
|
||||
void serverSetTimeout(ServerConnection *connection, time_t timeout);
|
||||
time_t serverGetTimeout(ServerConnection *connection);
|
||||
ServerConnection *serverGetConnection(Server *server, ServerConnection *hint,
|
||||
int fd);
|
||||
short serverConnectionSetEvents(Server *server, ServerConnection *connection,
|
||||
short events);
|
||||
void serverExitLoop(Server *server, int exitAll);
|
||||
void serverLoop(Server *server);
|
||||
int serverSupportsSSL();
|
||||
void serverEnableSSL(Server *server, int flag);
|
||||
void serverSetCertificate(Server *server, const char *filename,
|
||||
int autoGenerateMissing);
|
||||
void serverSetNumericHosts(Server *server, int numericHosts);
|
||||
|
||||
void httpTransfer(HttpConnection *http, char *msg, int len);
|
||||
void httpTransferPartialReply(HttpConnection *http, char *msg, int len);
|
||||
void httpSetCallback(HttpConnection *http,
|
||||
int (*callback)(HttpConnection *, void *,
|
||||
const char *, int), void *arg);
|
||||
void *httpGetPrivate(HttpConnection *http);
|
||||
void *httpSetPrivate(HttpConnection *http, void *private);
|
||||
void httpSendReply(HttpConnection *http, int code,
|
||||
const char *msg, const char *fmt, ...)
|
||||
__attribute__((format(printf, 4, 5)));
|
||||
void httpExitLoop(HttpConnection *http, int exitAll);
|
||||
Server *httpGetServer(const HttpConnection *http);
|
||||
ServerConnection *httpGetServerConnection(const HttpConnection *);
|
||||
int httpGetFd(const HttpConnection *http);
|
||||
const char *httpGetPeerName(const HttpConnection *http);
|
||||
const char *httpGetMethod(const HttpConnection *http);
|
||||
const char *httpGetVersion(const HttpConnection *http);
|
||||
const HashMap *httpGetHeaders(const HttpConnection *http);
|
||||
URL *newURL(const HttpConnection *http, const char *buf, int len);
|
||||
void deleteURL(URL *url);
|
||||
const char *urlGetProtocol(URL *url);
|
||||
const char *urlGetUser(URL *url);
|
||||
const char *urlGetPassword(URL *url);
|
||||
const char *urlGetHost(URL *url);
|
||||
int urlGetPort(URL *url);
|
||||
const char *urlGetPath(URL *url);
|
||||
const char *urlGetPathInfo(URL *url);
|
||||
const char *urlGetQuery(URL *url);
|
||||
const char *urlGetAnchor(URL *url);
|
||||
const HashMap *urlGetArgs(URL *url);
|
||||
HashMap *newHashMap(void (*destructor)(void *arg, char *key, char *value),
|
||||
void *arg);
|
||||
void deleteHashMap(HashMap *hashmap);
|
||||
const void *addToHashMap(HashMap *hashmap, const char *key, const char *value);
|
||||
void deleteFromHashMap(HashMap *hashmap, const char *key);
|
||||
char **getRefFromHashMap(const HashMap *hashmap, const char *key);
|
||||
const char *getFromHashMap(const HashMap *hashmap, const char *key);
|
||||
void iterateOverHashMap(struct HashMap *hashmap,
|
||||
int (*fnc)(void *arg, const char *key, char **value),
|
||||
void *arg);
|
||||
int getHashmapSize(const HashMap *hashmap);
|
||||
|
||||
#endif /* LIB_HTTP_H__ */
|
1374
libhttp/httpconnection.c
Normal file
1374
libhttp/httpconnection.c
Normal file
File diff suppressed because it is too large
Load diff
145
libhttp/httpconnection.h
Normal file
145
libhttp/httpconnection.h
Normal file
|
@ -0,0 +1,145 @@
|
|||
// httpconnection.h -- Manage state machine for HTTP connections
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#ifndef HTTP_CONNECTION__
|
||||
#define HTTP_CONNECTION__
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "libhttp/hashmap.h"
|
||||
#include "libhttp/trie.h"
|
||||
#include "libhttp/server.h"
|
||||
#include "libhttp/http.h"
|
||||
#include "libhttp/ssl.h"
|
||||
|
||||
#define HTTP_DONE 0
|
||||
#define HTTP_ERROR 1
|
||||
#define HTTP_READ_MORE 2
|
||||
#define HTTP_SUSPEND 3
|
||||
#define HTTP_PARTIAL_REPLY 4
|
||||
|
||||
struct HttpConnection {
|
||||
struct Server *server;
|
||||
struct ServerConnection *connection;
|
||||
int fd;
|
||||
int port;
|
||||
int closed;
|
||||
int isSuspended;
|
||||
int isPartialReply;
|
||||
int done;
|
||||
enum { SNIFFING_SSL, COMMAND, HEADERS, PAYLOAD, DISCARD_PAYLOAD } state;
|
||||
char *peerName;
|
||||
int peerPort;
|
||||
char *url;
|
||||
char *method;
|
||||
char *path;
|
||||
char *matchedPath;
|
||||
char *pathInfo;
|
||||
char *query;
|
||||
char *version;
|
||||
struct HashMap header;
|
||||
int headerLength;
|
||||
char *key;
|
||||
char *partial;
|
||||
int partialLength;
|
||||
char *msg;
|
||||
int msgLength;
|
||||
int msgOffset;
|
||||
int totalWritten;
|
||||
int expecting;
|
||||
int (*callback)(struct HttpConnection *, void *,
|
||||
const char *,int);
|
||||
void *arg;
|
||||
void *private;
|
||||
int code;
|
||||
struct SSLSupport *ssl;
|
||||
SSL *sslHndl;
|
||||
int lastError;
|
||||
};
|
||||
|
||||
struct HttpHandler {
|
||||
int (*handler)(struct HttpConnection *, void *);
|
||||
int (*streamingHandler)(struct HttpConnection *, void *, const char *, int);
|
||||
void *arg, *streamingArg;
|
||||
|
||||
};
|
||||
|
||||
struct HttpConnection *newHttpConnection(struct Server *server, int fd,
|
||||
int port, struct SSLSupport *ssl,
|
||||
int numericHosts);
|
||||
void initHttpConnection(struct HttpConnection *http, struct Server *server,
|
||||
int fd, int port, struct SSLSupport *ssl,
|
||||
int numericHosts);
|
||||
void destroyHttpConnection(struct HttpConnection *http);
|
||||
void deleteHttpConnection(struct HttpConnection *http);
|
||||
void httpTransfer(struct HttpConnection *http, char *msg, int len);
|
||||
void httpTransferPartialReply(struct HttpConnection *http, char *msg, int len);
|
||||
int httpHandleConnection(struct ServerConnection *connection, void *http_,
|
||||
short *events, short revents);
|
||||
void httpSetCallback(struct HttpConnection *http,
|
||||
int (*callback)(struct HttpConnection *, void *,
|
||||
const char *, int), void *arg);
|
||||
void *httpGetPrivate(struct HttpConnection *http);
|
||||
void *httpSetPrivate(struct HttpConnection *http, void *private);
|
||||
void httpSendReply(struct HttpConnection *http, int code,
|
||||
const char *msg, const char *fmt, ...)
|
||||
__attribute__((format(printf, 4, 5)));
|
||||
void httpExitLoop(struct HttpConnection *http, int exitAll);
|
||||
struct Server *httpGetServer(const struct HttpConnection *http);
|
||||
struct ServerConnection *httpGetServerConnection(const struct HttpConnection*);
|
||||
int httpGetFd(const HttpConnection *http);
|
||||
const char *httpGetPeerName(const struct HttpConnection *http);
|
||||
const char *httpGetMethod(const struct HttpConnection *http);
|
||||
const char *httpGetProtocol(const struct HttpConnection *http);
|
||||
const char *httpGetHost(const struct HttpConnection *http);
|
||||
int httpGetPort(const struct HttpConnection *http);
|
||||
const char *httpGetPath(const struct HttpConnection *http);
|
||||
const char *httpGetPathInfo(const struct HttpConnection *http);
|
||||
const char *httpGetQuery(const struct HttpConnection *http);
|
||||
const char *httpGetURL(const struct HttpConnection *http);
|
||||
const char *httpGetVersion(const struct HttpConnection *http);
|
||||
const struct HashMap *httpGetHeaders(const struct HttpConnection *http);
|
||||
|
||||
#endif /* HTTP_CONNECTION__ */
|
49
libhttp/libhttp.sym
Normal file
49
libhttp/libhttp.sym
Normal file
|
@ -0,0 +1,49 @@
|
|||
newServer
|
||||
deleteServer
|
||||
serverRegisterHttpHandler
|
||||
serverRegisterStreamingHttpHandler
|
||||
serverAddConnection
|
||||
serverDeleteConnection
|
||||
serverSetTimeout
|
||||
serverGetTimeout
|
||||
serverGetConnection
|
||||
serverConnectionSetEvents
|
||||
serverExitLoop
|
||||
serverLoop
|
||||
serverSupportsSSL
|
||||
serverEnableSSL
|
||||
serverSetCertificate
|
||||
serverSetNumericHosts
|
||||
httpTransfer
|
||||
httpTransferPartialReply
|
||||
httpSetCallback
|
||||
httpGetPrivate
|
||||
httpSetPrivate
|
||||
httpSendReply
|
||||
httpExitLoop
|
||||
httpGetServer
|
||||
httpGetServerConnection
|
||||
httpGetPeerName
|
||||
httpGetMethod
|
||||
httpGetVersion
|
||||
httpGetHeaders
|
||||
newURL
|
||||
deleteURL
|
||||
urlGetProtocol
|
||||
urlGetUser
|
||||
urlGetPassword
|
||||
urlGetHost
|
||||
urlGetPort
|
||||
urlGetPath
|
||||
urlGetPathInfo
|
||||
urlGetQuery
|
||||
urlGetAnchor
|
||||
urlGetArgs
|
||||
newHashMap
|
||||
deleteHashMap
|
||||
addToHashMap
|
||||
deleteFromHashMap
|
||||
getRefFromHashMap
|
||||
getFromHashMap
|
||||
iterateOverHashMap
|
||||
getHashmapSize
|
470
libhttp/server.c
Normal file
470
libhttp/server.c
Normal file
|
@ -0,0 +1,470 @@
|
|||
// server.c -- Generic server that can deal with HTTP connections
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "libhttp/server.h"
|
||||
#include "libhttp/httpconnection.h"
|
||||
#include "libhttp/ssl.h"
|
||||
#include "logging/logging.h"
|
||||
|
||||
#define INITIAL_TIMEOUT (10*60)
|
||||
|
||||
// Maximum amount of payload (e.g. form values that have been POST'd) that we
|
||||
// read into memory. If the application needs any more than this, the streaming
|
||||
// API should be used, instead.
|
||||
#define MAX_PAYLOAD_LENGTH (64<<10)
|
||||
|
||||
time_t currentTime;
|
||||
|
||||
struct PayLoad {
|
||||
int (*handler)(struct HttpConnection *, void *, const char *, int);
|
||||
void *arg;
|
||||
int len;
|
||||
char *bytes;
|
||||
};
|
||||
|
||||
static int serverCollectFullPayload(struct HttpConnection *http,
|
||||
void *payload_, const char *buf, int len) {
|
||||
int rc = HTTP_READ_MORE;
|
||||
struct PayLoad *payload = (struct PayLoad *)payload_;
|
||||
if (buf && len) {
|
||||
if (payload->len + len > MAX_PAYLOAD_LENGTH) {
|
||||
httpSendReply(http, 400, "Bad Request", NULL);
|
||||
return HTTP_DONE;
|
||||
}
|
||||
check(len > 0);
|
||||
check(payload->bytes = realloc(payload->bytes, payload->len + len));
|
||||
memcpy(payload->bytes + payload->len, buf, len);
|
||||
payload->len += len;
|
||||
}
|
||||
const char *contentLength = getFromHashMap(httpGetHeaders(http),
|
||||
"content-length");
|
||||
if (!contentLength ||
|
||||
(payload->bytes &&
|
||||
((contentLength && atoi(contentLength) <= payload->len) || !buf))) {
|
||||
rc = payload->handler(http, payload->arg,
|
||||
payload->bytes ? payload->bytes : "", payload->len);
|
||||
free(payload->bytes);
|
||||
payload->bytes = NULL;
|
||||
payload->len = 0;
|
||||
}
|
||||
if (!buf) {
|
||||
if (rc == HTTP_SUSPEND || rc == HTTP_PARTIAL_REPLY) {
|
||||
// Tell the other party that the connection is getting torn down, even
|
||||
// though it requested it to be suspended.
|
||||
payload->handler(http, payload->arg, NULL, 0);
|
||||
rc = HTTP_DONE;
|
||||
}
|
||||
free(payload);
|
||||
}
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
static int serverCollectHandler(struct HttpConnection *http, void *handler_) {
|
||||
struct HttpHandler *handler = handler_;
|
||||
struct PayLoad *payload;
|
||||
check(payload = malloc(sizeof(struct PayLoad)));
|
||||
payload->handler = handler->streamingHandler;
|
||||
payload->arg = handler->streamingArg;
|
||||
payload->len = 0;
|
||||
payload->bytes = malloc(0);
|
||||
httpSetCallback(http, serverCollectFullPayload, payload);
|
||||
return HTTP_READ_MORE;
|
||||
|
||||
}
|
||||
|
||||
static void serverDestroyHandlers(void *arg, char *value) {
|
||||
free(value);
|
||||
}
|
||||
|
||||
void serverRegisterHttpHandler(struct Server *server, const char *url,
|
||||
int (*handler)(struct HttpConnection *, void *,
|
||||
const char *, int), void *arg) {
|
||||
if (!handler) {
|
||||
addToTrie(&server->handlers, url, NULL);
|
||||
} else {
|
||||
struct HttpHandler *h;
|
||||
check(h = malloc(sizeof(struct HttpHandler)));
|
||||
h->handler = serverCollectHandler;
|
||||
h->arg = h;
|
||||
h->streamingHandler = handler;
|
||||
h->streamingArg = arg;
|
||||
addToTrie(&server->handlers, url, (char *)h);
|
||||
}
|
||||
}
|
||||
|
||||
void serverRegisterStreamingHttpHandler(struct Server *server, const char *url,
|
||||
int (*handler)(struct HttpConnection *, void *),
|
||||
void *arg) {
|
||||
if (!handler) {
|
||||
addToTrie(&server->handlers, url, NULL);
|
||||
} else {
|
||||
struct HttpHandler *h;
|
||||
check(h = malloc(sizeof(struct HttpHandler)));
|
||||
h->handler = handler;
|
||||
h->streamingHandler = NULL;
|
||||
h->streamingArg = NULL;
|
||||
h->arg = arg;
|
||||
addToTrie(&server->handlers, url, (char *)h);
|
||||
}
|
||||
}
|
||||
|
||||
static int serverQuitHandler(struct HttpConnection *http, void *arg) {
|
||||
httpSendReply(http, 200, "Good Bye", NULL);
|
||||
httpExitLoop(http, 1);
|
||||
return HTTP_DONE;
|
||||
}
|
||||
|
||||
struct Server *newServer(int port) {
|
||||
struct Server *server;
|
||||
check(server = malloc(sizeof(struct Server)));
|
||||
initServer(server, port);
|
||||
return server;
|
||||
}
|
||||
|
||||
void initServer(struct Server *server, int port) {
|
||||
server->port = port;
|
||||
server->looping = 0;
|
||||
server->exitAll = 0;
|
||||
server->serverFd = -1;
|
||||
server->numericHosts = 0;
|
||||
server->pollFds = NULL;
|
||||
server->connections = NULL;
|
||||
server->numConnections = 0;
|
||||
initTrie(&server->handlers, serverDestroyHandlers, NULL);
|
||||
serverRegisterStreamingHttpHandler(server, "/quit", serverQuitHandler, NULL);
|
||||
initSSL(&server->ssl);
|
||||
}
|
||||
|
||||
void destroyServer(struct Server *server) {
|
||||
if (server) {
|
||||
if (server->serverFd >= 0) {
|
||||
info("Shutting down server");
|
||||
close(server->serverFd);
|
||||
}
|
||||
for (int i = 0; i < server->numConnections; i++) {
|
||||
server->connections[i].destroyConnection(server->connections[i].arg);
|
||||
}
|
||||
free(server->connections);
|
||||
free(server->pollFds);
|
||||
destroyTrie(&server->handlers);
|
||||
destroySSL(&server->ssl);
|
||||
}
|
||||
}
|
||||
|
||||
void deleteServer(struct Server *server) {
|
||||
destroyServer(server);
|
||||
free(server);
|
||||
}
|
||||
|
||||
struct ServerConnection *serverAddConnection(struct Server *server, int fd,
|
||||
int (*handleConnection)(struct ServerConnection *c,
|
||||
void *arg, short *events,
|
||||
short revents),
|
||||
void (*destroyConnection)(void *arg),
|
||||
void *arg) {
|
||||
check(server->connections = realloc(server->connections,
|
||||
++server->numConnections*
|
||||
sizeof(struct ServerConnection)));
|
||||
check(server->pollFds = realloc(server->pollFds,
|
||||
(server->numConnections + 1) *
|
||||
sizeof(struct pollfd)));
|
||||
server->pollFds[server->numConnections].fd = fd;
|
||||
server->pollFds[server->numConnections].events = POLLIN;
|
||||
struct ServerConnection *connection =
|
||||
server->connections + server->numConnections - 1;
|
||||
connection->deleted = 0;
|
||||
connection->timeout = 0;
|
||||
connection->handleConnection = handleConnection;
|
||||
connection->destroyConnection = destroyConnection;
|
||||
connection->arg = arg;
|
||||
return connection;
|
||||
}
|
||||
|
||||
void serverDeleteConnection(struct Server *server, int fd) {
|
||||
for (int i = 0; i < server->numConnections; i++) {
|
||||
if (fd == server->pollFds[i + 1].fd && !server->connections[i].deleted) {
|
||||
server->connections[i].deleted = 1;
|
||||
server->connections[i].destroyConnection(server->connections[i].arg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void serverSetTimeout(struct ServerConnection *connection, time_t timeout) {
|
||||
if (!currentTime) {
|
||||
currentTime = time(NULL);
|
||||
}
|
||||
connection->timeout = timeout > 0 ? timeout + currentTime : 0;
|
||||
}
|
||||
|
||||
time_t serverGetTimeout(struct ServerConnection *connection) {
|
||||
if (connection->timeout) {
|
||||
// Returns <0 if expired, 0 if not set, and >0 if still pending.
|
||||
if (!currentTime) {
|
||||
currentTime = time(NULL);
|
||||
}
|
||||
int remaining = connection->timeout - currentTime;
|
||||
if (!remaining) {
|
||||
remaining--;
|
||||
}
|
||||
return remaining;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct ServerConnection *serverGetConnection(struct Server *server,
|
||||
struct ServerConnection *hint,
|
||||
int fd) {
|
||||
if (hint &&
|
||||
server->connections <= hint &&
|
||||
server->connections + server->numConnections > hint &&
|
||||
&server->connections[hint - server->connections] == hint &&
|
||||
!hint->deleted &&
|
||||
server->pollFds[hint - server->connections + 1].fd == fd) {
|
||||
return hint;
|
||||
}
|
||||
for (int i = 0; i < server->numConnections; i++) {
|
||||
if (server->pollFds[i + 1].fd == fd && !server->connections[i].deleted) {
|
||||
return server->connections + i;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
short serverConnectionSetEvents(struct Server *server,
|
||||
struct ServerConnection *connection,
|
||||
short events) {
|
||||
dcheck(server);
|
||||
dcheck(connection);
|
||||
dcheck(connection >= server->connections);
|
||||
dcheck(connection < server->connections + server->numConnections);
|
||||
dcheck(connection == &server->connections[connection - server->connections]);
|
||||
dcheck(!connection->deleted);
|
||||
int idx = connection - server->connections;
|
||||
short oldEvents = server->pollFds[idx + 1].events;
|
||||
server->pollFds[idx + 1].events = events;
|
||||
return oldEvents;
|
||||
}
|
||||
|
||||
void serverExitLoop(struct Server *server, int exitAll) {
|
||||
server->looping--;
|
||||
server->exitAll |= exitAll;
|
||||
}
|
||||
|
||||
void serverLoop(struct Server *server) {
|
||||
if (server->serverFd < 0) {
|
||||
int true = 1;
|
||||
server->serverFd = socket(PF_INET, SOCK_STREAM, 0);
|
||||
check(server->serverFd >= 0);
|
||||
check(!setsockopt(server->serverFd, SOL_SOCKET, SO_REUSEADDR,
|
||||
&true, sizeof(true)));
|
||||
struct sockaddr_in serverAddr = { 0 };
|
||||
serverAddr.sin_family = AF_INET;
|
||||
serverAddr.sin_addr.s_addr = INADDR_ANY;
|
||||
serverAddr.sin_port = htons(server->port);
|
||||
if (bind(server->serverFd, (struct sockaddr *)&serverAddr,
|
||||
sizeof(serverAddr))) {
|
||||
fatal("Failed to bind to port %d. Maybe, another server is already "
|
||||
"running?", server->port);
|
||||
}
|
||||
check(!listen(server->serverFd, SOMAXCONN));
|
||||
info("Listening on port %d", server->port);
|
||||
check(!server->pollFds);
|
||||
check(!server->numConnections);
|
||||
check(server->pollFds = malloc(sizeof(struct pollfd)));
|
||||
server->pollFds->fd = server->serverFd;
|
||||
server->pollFds->events = POLLIN;
|
||||
}
|
||||
time_t lastTime;
|
||||
currentTime = time(&lastTime);
|
||||
int loopDepth = ++server->looping;
|
||||
while (server->looping >= loopDepth && !server->exitAll) {
|
||||
// TODO: There probably should be some limit on the maximum number
|
||||
// of concurrently opened HTTP connections, as this could lead to
|
||||
// memory exhaustion and a DoS attack.
|
||||
time_t timeout = -1;
|
||||
int numFds = server->numConnections + 1;
|
||||
|
||||
for (int i = 0; i < server->numConnections; i++) {
|
||||
while (i < numFds - 1 && !server->pollFds[i + 1].events) {
|
||||
// Sort filedescriptors that currently do not expect any events to
|
||||
// the end of the list.
|
||||
check(--numFds > 0);
|
||||
struct pollfd tmpPollFd;
|
||||
memcpy(&tmpPollFd, server->pollFds + numFds, sizeof(struct pollfd));
|
||||
memcpy(server->pollFds + numFds, server->pollFds + i + 1,
|
||||
sizeof(struct pollfd));
|
||||
memcpy(server->pollFds + i + 1, &tmpPollFd, sizeof(struct pollfd));
|
||||
struct ServerConnection tmpConnection;
|
||||
memcpy(&tmpConnection, server->connections + numFds - 1,
|
||||
sizeof(struct ServerConnection));
|
||||
memcpy(server->connections + numFds - 1, server->connections + i,
|
||||
sizeof(struct ServerConnection));
|
||||
memcpy(server->connections + i, &tmpConnection,
|
||||
sizeof(struct ServerConnection));
|
||||
}
|
||||
|
||||
if (server->connections[i].timeout &&
|
||||
(timeout < 0 || timeout > server->connections[i].timeout)) {
|
||||
timeout = server->connections[i].timeout;
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout >= 0) {
|
||||
// Wait at least one second longer than needed, so that even if
|
||||
// poll() decides to return a second early (due to possible rounding
|
||||
// errors), we still correctly detect a timeout condition.
|
||||
if (timeout >= lastTime) {
|
||||
timeout = (timeout - lastTime + 1) * 1000;
|
||||
} else {
|
||||
timeout = 1000;
|
||||
}
|
||||
}
|
||||
|
||||
int eventCount = NOINTR(poll(server->pollFds,
|
||||
numFds,
|
||||
timeout));
|
||||
check(eventCount >= 0);
|
||||
if (timeout >= 0) {
|
||||
timeout += lastTime;
|
||||
}
|
||||
currentTime = time(&lastTime);
|
||||
int isTimeout = timeout >= 0 &&
|
||||
timeout/1000 <= lastTime;
|
||||
if (server->pollFds[0].revents) {
|
||||
eventCount--;
|
||||
if (server->pollFds[0].revents && POLLIN) {
|
||||
struct sockaddr_in clientAddr;
|
||||
socklen_t sockLen = sizeof(clientAddr);
|
||||
int clientFd = accept(
|
||||
server->serverFd, (struct sockaddr *)&clientAddr, &sockLen);
|
||||
dcheck(clientFd >= 0);
|
||||
if (clientFd >= 0) {
|
||||
check(!fcntl(clientFd, F_SETFL, O_RDWR | O_NONBLOCK));
|
||||
struct HttpConnection *http;
|
||||
http = newHttpConnection(
|
||||
server, clientFd, server->port,
|
||||
server->ssl.enabled ? &server->ssl : NULL,
|
||||
server->numericHosts);
|
||||
serverSetTimeout(
|
||||
serverAddConnection(server, clientFd, httpHandleConnection,
|
||||
(void (*)(void *))deleteHttpConnection,
|
||||
http),
|
||||
INITIAL_TIMEOUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 1;
|
||||
(isTimeout || eventCount > 0) && i <= server->numConnections;
|
||||
i++) {
|
||||
struct ServerConnection *connection = server->connections + i - 1;
|
||||
if (connection->deleted) {
|
||||
continue;
|
||||
}
|
||||
if (!eventCount) {
|
||||
server->pollFds[i].revents = 0;
|
||||
}
|
||||
if (server->pollFds[i].revents ||
|
||||
(connection->timeout && lastTime >= connection->timeout)) {
|
||||
if (server->pollFds[i].revents) {
|
||||
eventCount--;
|
||||
}
|
||||
short events = server->pollFds[i].events;
|
||||
if (!connection->handleConnection(connection, connection->arg,
|
||||
&events, server->pollFds[i].revents)){
|
||||
connection = server->connections + i - 1;
|
||||
connection->destroyConnection(connection->arg);
|
||||
connection->deleted = 1;
|
||||
} else {
|
||||
server->pollFds[i].events = events;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 1; i <= server->numConnections; i++) {
|
||||
if (server->connections[i-1].deleted) {
|
||||
memmove(server->pollFds + i, server->pollFds + i + 1,
|
||||
(server->numConnections - i) * sizeof(struct pollfd));
|
||||
memmove(server->connections + i - 1, server->connections + i,
|
||||
(server->numConnections - i)*sizeof(struct ServerConnection));
|
||||
check(--i >= 0);
|
||||
check(--server->numConnections >= 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Even if multiple clients requested for us to exit the loop, we only
|
||||
// ever exit the outer most loop.
|
||||
server->looping = loopDepth - 1;
|
||||
}
|
||||
|
||||
void serverEnableSSL(struct Server *server, int flag) {
|
||||
if (flag) {
|
||||
check(serverSupportsSSL());
|
||||
}
|
||||
sslEnable(&server->ssl, flag);
|
||||
}
|
||||
|
||||
void serverSetCertificate(struct Server *server, const char *filename,
|
||||
int autoGenerateMissing) {
|
||||
sslSetCertificate(&server->ssl, filename, autoGenerateMissing);
|
||||
}
|
||||
|
||||
void serverSetNumericHosts(struct Server *server, int numericHosts) {
|
||||
server->numericHosts = numericHosts;
|
||||
}
|
||||
|
||||
struct Trie *serverGetHttpHandlers(struct Server *server) {
|
||||
return &server->handlers;
|
||||
}
|
115
libhttp/server.h
Normal file
115
libhttp/server.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
// server.h -- Generic server that can deal with HTTP connections
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#ifndef SERVER_H__
|
||||
#define SERVER_H__
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "libhttp/trie.h"
|
||||
#include "libhttp/http.h"
|
||||
#include "libhttp/ssl.h"
|
||||
|
||||
struct Server;
|
||||
|
||||
struct ServerConnection {
|
||||
int deleted;
|
||||
time_t timeout;
|
||||
int (*handleConnection)(struct ServerConnection *c,
|
||||
void *arg, short *events,
|
||||
short revents);
|
||||
void (*destroyConnection)(void *arg);
|
||||
void *arg;
|
||||
};
|
||||
|
||||
struct Server {
|
||||
int port;
|
||||
int looping;
|
||||
int exitAll;
|
||||
int serverFd;
|
||||
int numericHosts;
|
||||
struct pollfd *pollFds;
|
||||
struct ServerConnection *connections;
|
||||
int numConnections;
|
||||
struct Trie handlers;
|
||||
struct SSLSupport ssl;
|
||||
};
|
||||
|
||||
struct Server *newServer(int port);
|
||||
void initServer(struct Server *server, int port);
|
||||
void destroyServer(struct Server *server);
|
||||
void deleteServer(struct Server *server);
|
||||
void serverRegisterHttpHandler(struct Server *server, const char *url,
|
||||
int (*handler)(struct HttpConnection *, void *,
|
||||
const char *, int), void *arg);
|
||||
void serverRegisterStreamingHttpHandler(struct Server *server, const char *url,
|
||||
int (*handler)(struct HttpConnection *, void *),
|
||||
void *arg);
|
||||
struct ServerConnection *serverAddConnection(struct Server *server, int fd,
|
||||
int (*handleConnection)(struct ServerConnection *c,
|
||||
void *arg, short *events,
|
||||
short revents),
|
||||
void (*destroyConnection)(void *arg),
|
||||
void *arg);
|
||||
void serverDeleteConnection(struct Server *server, int fd);
|
||||
void serverSetTimeout(struct ServerConnection *connection, time_t timeout);
|
||||
time_t serverGetTimeout(struct ServerConnection *connection);
|
||||
struct ServerConnection *serverGetConnection(struct Server *server,
|
||||
struct ServerConnection *hint,
|
||||
int fd);
|
||||
short serverConnectionSetEvents(struct Server *server,
|
||||
struct ServerConnection *connection,
|
||||
short events);
|
||||
void serverExitLoop(struct Server *server, int exitAll);
|
||||
void serverLoop(struct Server *server);
|
||||
void serverEnableSSL(struct Server *server, int flag);
|
||||
void serverSetCertificate(struct Server *server, const char *filename,
|
||||
int autoGenerateMissing);
|
||||
void serverSetNumericHosts(struct Server *server, int numericHosts);
|
||||
struct Trie *serverGetHttpHandlers(struct Server *server);
|
||||
|
||||
extern time_t currentTime;
|
||||
|
||||
#endif
|
515
libhttp/ssl.c
Normal file
515
libhttp/ssl.c
Normal file
|
@ -0,0 +1,515 @@
|
|||
// ssl.c -- Support functions that find and load SSL support, if available
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "libhttp/ssl.h"
|
||||
#include "libhttp/httpconnection.h"
|
||||
#include "logging/logging.h"
|
||||
|
||||
#if defined(HAVE_PTHREAD_H)
|
||||
// Pthread support is optional. Only enable it, if the library has been
|
||||
// linked into the program
|
||||
#include <pthread.h>
|
||||
extern int pthread_once(pthread_once_t *, void (*)(void))__attribute__((weak));
|
||||
extern int pthread_sigmask(int, const sigset_t *, sigset_t *)
|
||||
__attribute__((weak));
|
||||
|
||||
#endif
|
||||
|
||||
// SSL support is optional. Only enable it, if the library can be loaded.
|
||||
long (*x_BIO_ctrl)(BIO *, int, long, void *);
|
||||
BIO_METHOD * (*x_BIO_f_buffer)(void);
|
||||
void (*x_BIO_free_all)(BIO *);
|
||||
BIO * (*x_BIO_new)(BIO_METHOD *);
|
||||
BIO * (*x_BIO_new_socket)(int, int);
|
||||
BIO * (*x_BIO_pop)(BIO *);
|
||||
BIO * (*x_BIO_push)(BIO *, BIO *);
|
||||
void (*x_ERR_clear_error)(void);
|
||||
void (*x_ERR_clear_error)(void);
|
||||
unsigned long (*x_ERR_peek_error)(void);
|
||||
unsigned long (*x_ERR_peek_error)(void);
|
||||
long (*x_SSL_CTX_callback_ctrl)(SSL_CTX *, int, void (*)(void));
|
||||
int (*x_SSL_CTX_check_private_key)(const SSL_CTX *);
|
||||
long (*x_SSL_CTX_ctrl)(SSL_CTX *, int, long, void *);
|
||||
void (*x_SSL_CTX_free)(SSL_CTX *);
|
||||
SSL_CTX * (*x_SSL_CTX_new)(SSL_METHOD *);
|
||||
int (*x_SSL_CTX_use_PrivateKey_file)(SSL_CTX *, const char *, int);
|
||||
int (*x_SSL_CTX_use_certificate_file)(SSL_CTX *, const char *, int);
|
||||
long (*x_SSL_ctrl)(SSL *, int, long, void *);
|
||||
void (*x_SSL_free)(SSL *);
|
||||
int (*x_SSL_get_error)(const SSL *, int);
|
||||
void * (*x_SSL_get_ex_data)(const SSL *, int);
|
||||
BIO * (*x_SSL_get_rbio)(const SSL *);
|
||||
const char * (*x_SSL_get_servername)(const SSL *, int);
|
||||
BIO * (*x_SSL_get_wbio)(const SSL *);
|
||||
int (*x_SSL_library_init)(void);
|
||||
SSL * (*x_SSL_new)(SSL_CTX *);
|
||||
int (*x_SSL_read)(SSL *, void *, int);
|
||||
SSL_CTX * (*x_SSL_set_SSL_CTX)(SSL *, SSL_CTX *);
|
||||
void (*x_SSL_set_accept_state)(SSL *);
|
||||
void (*x_SSL_set_bio)(SSL *, BIO *, BIO *);
|
||||
int (*x_SSL_set_ex_data)(SSL *, int, void *);
|
||||
int (*x_SSL_shutdown)(SSL *);
|
||||
int (*x_SSL_write)(SSL *, const void *, int);
|
||||
SSL_METHOD * (*x_SSLv23_server_method)(void);
|
||||
|
||||
|
||||
static void sslDestroyCachedContext(void *ssl_, char *context_) {
|
||||
struct SSLSupport *ssl = (struct SSLSupport *)ssl_;
|
||||
SSL_CTX *context = (SSL_CTX *)context_;
|
||||
if (context != ssl->sslContext) {
|
||||
SSL_CTX_free(context);
|
||||
}
|
||||
}
|
||||
|
||||
struct SSLSupport *newSSL(void) {
|
||||
struct SSLSupport *ssl;
|
||||
check(ssl = malloc(sizeof(struct SSLSupport)));
|
||||
initSSL(ssl);
|
||||
return ssl;
|
||||
}
|
||||
|
||||
void initSSL(struct SSLSupport *ssl) {
|
||||
ssl->enabled = serverSupportsSSL();
|
||||
ssl->sslContext = NULL;
|
||||
ssl->sniCertificatePattern = NULL;
|
||||
ssl->generateMissing = 0;
|
||||
initTrie(&ssl->sniContexts, sslDestroyCachedContext, ssl);
|
||||
}
|
||||
|
||||
void destroySSL(struct SSLSupport *ssl) {
|
||||
if (ssl) {
|
||||
free(ssl->sniCertificatePattern);
|
||||
destroyTrie(&ssl->sniContexts);
|
||||
if (ssl->sslContext) {
|
||||
dcheck(!ERR_peek_error());
|
||||
SSL_CTX_free(ssl->sslContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void deleteSSL(struct SSLSupport *ssl) {
|
||||
destroySSL(ssl);
|
||||
free(ssl);
|
||||
}
|
||||
|
||||
#if defined(HAVE_OPENSSL)
|
||||
static void *loadSymbol(const char *lib, const char *fn) {
|
||||
void *dl = RTLD_DEFAULT;
|
||||
void *rc = dlsym(dl, fn);
|
||||
if (!rc) {
|
||||
dl = dlopen(lib, RTLD_LAZY|RTLD_GLOBAL|RTLD_NOLOAD);
|
||||
if (dl == NULL) {
|
||||
dl = dlopen(lib, RTLD_LAZY|RTLD_GLOBAL);
|
||||
}
|
||||
if (dl != NULL) {
|
||||
rc = dlsym(dl, fn);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void loadSSL(void) {
|
||||
check(!SSL_library_init);
|
||||
struct {
|
||||
void **var;
|
||||
const char *fn;
|
||||
} symbols[] = {
|
||||
{ (void **)&x_BIO_ctrl, "BIO_ctrl" },
|
||||
{ (void **)&x_BIO_f_buffer, "BIO_f_buffer" },
|
||||
{ (void **)&x_BIO_free_all, "BIO_free_all" },
|
||||
{ (void **)&x_BIO_new, "BIO_new" },
|
||||
{ (void **)&x_BIO_new_socket, "BIO_new_socket" },
|
||||
{ (void **)&x_BIO_pop, "BIO_pop" },
|
||||
{ (void **)&x_BIO_push, "BIO_push" },
|
||||
{ (void **)&x_ERR_clear_error, "ERR_clear_error" },
|
||||
{ (void **)&x_ERR_clear_error, "ERR_clear_error" },
|
||||
{ (void **)&x_ERR_peek_error, "ERR_peek_error" },
|
||||
{ (void **)&x_ERR_peek_error, "ERR_peek_error" },
|
||||
{ (void **)&x_SSL_CTX_callback_ctrl, "SSL_CTX_callback_ctrl" },
|
||||
{ (void **)&x_SSL_CTX_check_private_key, "SSL_CTX_check_private_key" },
|
||||
{ (void **)&x_SSL_CTX_ctrl, "SSL_CTX_ctrl" },
|
||||
{ (void **)&x_SSL_CTX_free, "SSL_CTX_free" },
|
||||
{ (void **)&x_SSL_CTX_new, "SSL_CTX_new" },
|
||||
{ (void **)&x_SSL_CTX_use_PrivateKey_file, "SSL_CTX_use_PrivateKey_file" },
|
||||
{ (void **)&x_SSL_CTX_use_certificate_file,"SSL_CTX_use_certificate_file"},
|
||||
{ (void **)&x_SSL_ctrl, "SSL_ctrl" },
|
||||
{ (void **)&x_SSL_free, "SSL_free" },
|
||||
{ (void **)&x_SSL_get_error, "SSL_get_error" },
|
||||
{ (void **)&x_SSL_get_ex_data, "SSL_get_ex_data" },
|
||||
{ (void **)&x_SSL_get_rbio, "SSL_get_rbio" },
|
||||
{ (void **)&x_SSL_get_servername, "SSL_get_servername" },
|
||||
{ (void **)&x_SSL_get_wbio, "SSL_get_wbio" },
|
||||
{ (void **)&x_SSL_library_init, "SSL_library_init" },
|
||||
{ (void **)&x_SSL_new, "SSL_new" },
|
||||
{ (void **)&x_SSL_read, "SSL_read" },
|
||||
{ (void **)&x_SSL_set_SSL_CTX, "SSL_set_SSL_CTX" },
|
||||
{ (void **)&x_SSL_set_accept_state, "SSL_set_accept_state" },
|
||||
{ (void **)&x_SSL_set_bio, "SSL_set_bio" },
|
||||
{ (void **)&x_SSL_set_ex_data, "SSL_set_ex_data" },
|
||||
{ (void **)&x_SSL_shutdown, "SSL_shutdown" },
|
||||
{ (void **)&x_SSL_write, "SSL_write" },
|
||||
{ (void **)&x_SSLv23_server_method, "SSLv23_server_method" }
|
||||
};
|
||||
for (int i = 0; i < sizeof(symbols)/sizeof(symbols[0]); i++) {
|
||||
if (!(*symbols[i].var = loadSymbol("libssl.so", symbols[i].fn))) {
|
||||
debug("Failed to load SSL support. Could not find \"%s\"",
|
||||
symbols[i].fn);
|
||||
for (int j = 0; j < sizeof(symbols)/sizeof(symbols[0]); j++) {
|
||||
*symbols[j].var = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
SSL_library_init();
|
||||
dcheck(!ERR_peek_error());
|
||||
debug("Loaded SSL suppport");
|
||||
}
|
||||
#endif
|
||||
|
||||
int serverSupportsSSL(void) {
|
||||
#if defined(HAVE_OPENSSL)
|
||||
// We want to call loadSSL() exactly once. For single-threaded applications,
|
||||
// this is straight-forward. For threaded applications, we need to call
|
||||
// pthread_once(), instead. We perform run-time checks for whether we are
|
||||
// single- or multi-threaded, so that the same code can be used.
|
||||
#if defined(HAVE_PTHREAD_H)
|
||||
if (!!&pthread_once) {
|
||||
static pthread_once_t once = PTHREAD_ONCE_INIT;
|
||||
pthread_once(&once, loadSSL);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
static int initialized;
|
||||
if (!initialized) {
|
||||
initialized = 1;
|
||||
loadSSL();
|
||||
}
|
||||
}
|
||||
return !!SSL_library_init;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void sslGenerateCertificate(const char *certificate, const char *serverName) {
|
||||
debug("Auto-generating missing certificate \"%s\" for \"%s\"",
|
||||
certificate, serverName);
|
||||
char *cmd = stringPrintf(NULL,
|
||||
"set -e; "
|
||||
"exec 2>/dev/null </dev/null; "
|
||||
"umask 0377; "
|
||||
"PATH=/usr/bin "
|
||||
"openssl req -x509 -nodes -days 7300 -newkey rsa:1024 -keyout /dev/stdout "
|
||||
"-out /dev/stdout -subj '/CN=%s/' | cat>'%s'",
|
||||
serverName, certificate);
|
||||
if (system(cmd)) {
|
||||
warn("Failed to generate self-signed certificate \"%s\"", certificate);
|
||||
}
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
#ifdef TLSEXT_NAMETYPE_host_name
|
||||
static int sslSNICallback(SSL *sslHndl, int *al, struct SSLSupport *ssl) {
|
||||
check(!ERR_peek_error());
|
||||
const char *name = SSL_get_servername(sslHndl,
|
||||
TLSEXT_NAMETYPE_host_name);
|
||||
if (name == NULL || !*name) {
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
struct HttpConnection *http =
|
||||
(struct HttpConnection *)SSL_get_app_data(sslHndl);
|
||||
debug("Received SNI callback for virtual host \"%s\" from \"%s:%d\"",
|
||||
name, httpGetPeerName(http), httpGetPort(http));
|
||||
char *serverName;
|
||||
check(serverName = malloc(strlen(name)+2));
|
||||
serverName[0] = '-';
|
||||
for (int i = 0;;) {
|
||||
char ch = name[i];
|
||||
if (ch >= 'A' && ch <= 'Z') {
|
||||
ch |= 0x20;
|
||||
} else if (ch != '\000' && ch != '.' && ch != '-' &&
|
||||
(ch < '0' ||(ch > '9' && ch < 'A') || (ch > 'Z' &&
|
||||
ch < 'a')|| ch > 'z')) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
serverName[++i] = ch;
|
||||
if (!ch) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!*serverName) {
|
||||
free(serverName);
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
SSL_CTX *context = (SSL_CTX *)getFromTrie(&ssl->sniContexts,
|
||||
serverName+1,
|
||||
NULL);
|
||||
if (context == NULL) {
|
||||
check(context = SSL_CTX_new(SSLv23_server_method()));
|
||||
check(ssl->sniCertificatePattern);
|
||||
char *certificate = stringPrintf(NULL, ssl->sniCertificatePattern,
|
||||
serverName);
|
||||
if (!SSL_CTX_use_certificate_file(context, certificate, SSL_FILETYPE_PEM)||
|
||||
!SSL_CTX_use_PrivateKey_file(context, certificate, SSL_FILETYPE_PEM) ||
|
||||
!SSL_CTX_check_private_key(context)) {
|
||||
if (ssl->generateMissing) {
|
||||
sslGenerateCertificate(certificate, serverName + 1);
|
||||
if (!SSL_CTX_use_certificate_file(context, certificate,
|
||||
SSL_FILETYPE_PEM) ||
|
||||
!SSL_CTX_use_PrivateKey_file(context, certificate,
|
||||
SSL_FILETYPE_PEM) ||
|
||||
!SSL_CTX_check_private_key(context)) {
|
||||
goto certificate_missing;
|
||||
}
|
||||
} else {
|
||||
certificate_missing:
|
||||
warn("Could not find matching certificate \"%s\" for \"%s\"",
|
||||
certificate, serverName + 1);
|
||||
SSL_CTX_free(context);
|
||||
context = ssl->sslContext;
|
||||
}
|
||||
}
|
||||
ERR_clear_error();
|
||||
free(certificate);
|
||||
addToTrie(&ssl->sniContexts, serverName+1, (char *)context);
|
||||
}
|
||||
free(serverName);
|
||||
if (context != ssl->sslContext) {
|
||||
check(SSL_set_SSL_CTX(sslHndl, context) > 0);
|
||||
}
|
||||
check(!ERR_peek_error());
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
void sslSetCertificate(struct SSLSupport *ssl, const char *filename,
|
||||
int autoGenerateMissing) {
|
||||
#if defined(HAVE_OPENSSL)
|
||||
check(serverSupportsSSL());
|
||||
|
||||
char *defaultCertificate;
|
||||
check(defaultCertificate = strdup(filename));
|
||||
char *ptr = strchr(defaultCertificate, '%');
|
||||
if (ptr != NULL) {
|
||||
check(!strchr(ptr+1, '%'));
|
||||
check(ptr[1] == 's');
|
||||
memmove(ptr, ptr + 2, strlen(ptr)-1);
|
||||
}
|
||||
|
||||
check(ssl->sslContext = SSL_CTX_new(SSLv23_server_method()));
|
||||
if (autoGenerateMissing) {
|
||||
if (!SSL_CTX_use_certificate_file(ssl->sslContext, defaultCertificate,
|
||||
SSL_FILETYPE_PEM) ||
|
||||
!SSL_CTX_use_PrivateKey_file(ssl->sslContext, defaultCertificate,
|
||||
SSL_FILETYPE_PEM) ||
|
||||
!SSL_CTX_check_private_key(ssl->sslContext)) {
|
||||
char hostname[256], buf[4096];
|
||||
check(!gethostname(hostname, sizeof(hostname)));
|
||||
struct hostent he_buf, *he;
|
||||
int h_err;
|
||||
if (gethostbyname_r(hostname, &he_buf, buf, sizeof(buf),
|
||||
&he, &h_err)) {
|
||||
sslGenerateCertificate(defaultCertificate, hostname);
|
||||
} else {
|
||||
sslGenerateCertificate(defaultCertificate, he->h_name);
|
||||
}
|
||||
} else {
|
||||
goto valid_certificate;
|
||||
}
|
||||
}
|
||||
if (!SSL_CTX_use_certificate_file(ssl->sslContext, defaultCertificate,
|
||||
SSL_FILETYPE_PEM) ||
|
||||
!SSL_CTX_use_PrivateKey_file(ssl->sslContext, defaultCertificate,
|
||||
SSL_FILETYPE_PEM) ||
|
||||
!SSL_CTX_check_private_key(ssl->sslContext)) {
|
||||
fatal("Cannot read valid certificate from \"%s\". "
|
||||
"Check file permissions and file format.", defaultCertificate);
|
||||
}
|
||||
valid_certificate:
|
||||
free(defaultCertificate);
|
||||
|
||||
#ifdef TLSEXT_NAMETYPE_host_name
|
||||
if (ptr != NULL) {
|
||||
check(ssl->sniCertificatePattern = strdup(filename));
|
||||
check(SSL_CTX_set_tlsext_servername_callback(ssl->sslContext,
|
||||
sslSNICallback));
|
||||
check(SSL_CTX_set_tlsext_servername_arg(ssl->sslContext, ssl));
|
||||
}
|
||||
#endif
|
||||
dcheck(!ERR_peek_error());
|
||||
ERR_clear_error();
|
||||
|
||||
ssl->generateMissing = autoGenerateMissing;
|
||||
#endif
|
||||
}
|
||||
|
||||
int sslEnable(struct SSLSupport *ssl, int enabled) {
|
||||
int old = ssl->enabled;
|
||||
ssl->enabled = enabled;
|
||||
return old;
|
||||
}
|
||||
|
||||
void sslBlockSigPipe(void) {
|
||||
sigset_t set;
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGPIPE);
|
||||
dcheck(!(&pthread_sigmask ? pthread_sigmask : sigprocmask)
|
||||
(SIG_BLOCK, &set, NULL));
|
||||
}
|
||||
|
||||
int sslUnblockSigPipe(void) {
|
||||
int signum = 0;
|
||||
sigset_t set;
|
||||
check(!sigpending(&set));
|
||||
if (sigismember(&set, SIGPIPE)) {
|
||||
sigwait(&set, &signum);
|
||||
}
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGPIPE);
|
||||
check(!(&pthread_sigmask ? pthread_sigmask : sigprocmask)
|
||||
(SIG_UNBLOCK, &set, NULL));
|
||||
return signum;
|
||||
}
|
||||
|
||||
int sslPromoteToSSL(struct SSLSupport *ssl, SSL **sslHndl, int fd,
|
||||
const char *buf, int len) {
|
||||
#if defined(HAVE_OPENSSL)
|
||||
sslBlockSigPipe();
|
||||
int rc = 0;
|
||||
check(!*sslHndl);
|
||||
dcheck(!ERR_peek_error());
|
||||
dcheck(*sslHndl = SSL_new(ssl->sslContext));
|
||||
if (*sslHndl == NULL) {
|
||||
ERR_clear_error();
|
||||
errno = EINVAL;
|
||||
rc = -1;
|
||||
} else {
|
||||
SSL_set_mode(*sslHndl, SSL_MODE_ENABLE_PARTIAL_WRITE);
|
||||
BIO *writeBIO = BIO_new_socket(fd, 0);
|
||||
BIO *readBIO = writeBIO;
|
||||
if (len > 0) {
|
||||
readBIO = BIO_new(BIO_f_buffer());
|
||||
BIO_push(readBIO, writeBIO);
|
||||
check(BIO_set_buffer_read_data(readBIO, (char *)buf, len));
|
||||
}
|
||||
SSL_set_bio(*sslHndl, readBIO, writeBIO);
|
||||
SSL_set_accept_state(*sslHndl);
|
||||
dcheck(!ERR_peek_error());
|
||||
}
|
||||
sslUnblockSigPipe();
|
||||
return rc;
|
||||
#else
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void sslFreeHndl(SSL **sslHndl) {
|
||||
#if defined(HAVE_OPENSSL)
|
||||
if (*sslHndl) {
|
||||
// OpenSSL does not always correctly perform reference counting for stacked
|
||||
// BIOs. This is particularly a problem if an SSL connection has two
|
||||
// different BIOs for the read and the write end, with one being a stacked
|
||||
// derivative of the other. Unfortunately, this is exactly the scenario
|
||||
// that we set up.
|
||||
// As a work-around, we un-stack the BIOs prior to freeing the SSL
|
||||
// connection.
|
||||
ERR_clear_error();
|
||||
BIO *writeBIO, *readBIO;
|
||||
check(writeBIO = SSL_get_wbio(*sslHndl));
|
||||
check(readBIO = SSL_get_rbio(*sslHndl));
|
||||
if (writeBIO != readBIO) {
|
||||
if (readBIO->next_bio == writeBIO) {
|
||||
// OK, that's exactly the bug we are looking for. We know how to
|
||||
// fix it.
|
||||
check(BIO_pop(readBIO) == writeBIO);
|
||||
check(readBIO->references == 1);
|
||||
check(writeBIO->references == 1);
|
||||
check(!readBIO->next_bio);
|
||||
check(!writeBIO->prev_bio);
|
||||
} else if (readBIO->next_bio == writeBIO->next_bio &&
|
||||
writeBIO->next_bio->prev_bio == writeBIO) {
|
||||
// Things get even more confused, if the SSL handshake is aborted
|
||||
// prematurely.
|
||||
// OpenSSL appears to internally stack a BIO onto the read end that
|
||||
// does not get removed afterwards. We end up with the original
|
||||
// socket BIO having two different BIOs prepended to it (one for
|
||||
// reading and one for writing). In this case, not only is the
|
||||
// reference count wrong, but the chain of next_bio/prev_bio pairs
|
||||
// is corrupted, too.
|
||||
BIO *sockBIO;
|
||||
check(sockBIO = BIO_pop(readBIO));
|
||||
check(sockBIO == BIO_pop(writeBIO));
|
||||
check(readBIO->references == 1);
|
||||
check(writeBIO->references == 1);
|
||||
check(sockBIO->references == 1);
|
||||
check(!readBIO->next_bio);
|
||||
check(!writeBIO->next_bio);
|
||||
check(!sockBIO->prev_bio);
|
||||
BIO_free_all(sockBIO);
|
||||
} else {
|
||||
// We do not know, how to fix this situation. Something must have
|
||||
// changed in the OpenSSL internals. Either, this is a new bug, or
|
||||
// somebody fixed the code in a way that we did not anticipate.
|
||||
fatal("Unexpected corruption of OpenSSL data structures");
|
||||
}
|
||||
}
|
||||
SSL_free(*sslHndl);
|
||||
dcheck(!ERR_peek_error());
|
||||
}
|
||||
#endif
|
||||
*sslHndl = NULL;
|
||||
}
|
185
libhttp/ssl.h
Normal file
185
libhttp/ssl.h
Normal file
|
@ -0,0 +1,185 @@
|
|||
// ssl.h -- Support functions that find and load SSL support, if available
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#ifndef SSL_H__
|
||||
#define SSL_H__
|
||||
|
||||
#include "libhttp/trie.h"
|
||||
|
||||
#if defined(HAVE_OPENSSL_BIO_H) && \
|
||||
defined(HAVE_OPENSSL_ERR_H) && \
|
||||
defined(HAVE_OPENSSL_SSL_H)
|
||||
#define HAVE_OPENSSL 1
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
#else
|
||||
#undef HAVE_OPENSSL
|
||||
typedef struct BIO BIO;
|
||||
typedef struct BIO_METHOD BIO_METHOD;
|
||||
typedef struct SSL SSL;
|
||||
typedef struct SSL_CTX SSL_CTX;
|
||||
typedef struct SSL_METHOD SSL_METHOD;
|
||||
#define SSL_ERROR_WANT_READ 2
|
||||
#define SSL_ERROR_WANT_WRITE 3
|
||||
#endif
|
||||
|
||||
extern long (*x_BIO_ctrl)(BIO *, int, long, void *);
|
||||
extern BIO_METHOD *(*x_BIO_f_buffer)(void);
|
||||
extern void (*x_BIO_free_all)(BIO *);
|
||||
extern BIO *(*x_BIO_new)(BIO_METHOD *);
|
||||
extern BIO *(*x_BIO_new_socket)(int, int);
|
||||
extern BIO *(*x_BIO_pop)(BIO *);
|
||||
extern BIO *(*x_BIO_push)(BIO *, BIO *);
|
||||
extern void (*x_ERR_clear_error)(void);
|
||||
extern void (*x_ERR_clear_error)(void);
|
||||
extern unsigned long (*x_ERR_peek_error)(void);
|
||||
extern unsigned long (*x_ERR_peek_error)(void);
|
||||
extern long (*x_SSL_CTX_callback_ctrl)(SSL_CTX *, int, void (*)(void));
|
||||
extern int (*x_SSL_CTX_check_private_key)(const SSL_CTX *);
|
||||
extern long (*x_SSL_CTX_ctrl)(SSL_CTX *, int, long, void *);
|
||||
extern void (*x_SSL_CTX_free)(SSL_CTX *);
|
||||
extern SSL_CTX*(*x_SSL_CTX_new)(SSL_METHOD *);
|
||||
extern int (*x_SSL_CTX_use_PrivateKey_file)(SSL_CTX *, const char *, int);
|
||||
extern int (*x_SSL_CTX_use_certificate_file)(SSL_CTX *, const char *, int);
|
||||
extern long (*x_SSL_ctrl)(SSL *, int, long, void *);
|
||||
extern void (*x_SSL_free)(SSL *);
|
||||
extern int (*x_SSL_get_error)(const SSL *, int);
|
||||
extern void *(*x_SSL_get_ex_data)(const SSL *, int);
|
||||
extern BIO *(*x_SSL_get_rbio)(const SSL *);
|
||||
extern const char *(*x_SSL_get_servername)(const SSL *, int);
|
||||
extern BIO *(*x_SSL_get_wbio)(const SSL *);
|
||||
extern int (*x_SSL_library_init)(void);
|
||||
extern SSL *(*x_SSL_new)(SSL_CTX *);
|
||||
extern int (*x_SSL_read)(SSL *, void *, int);
|
||||
extern SSL_CTX*(*x_SSL_set_SSL_CTX)(SSL *, SSL_CTX *);
|
||||
extern void (*x_SSL_set_accept_state)(SSL *);
|
||||
extern void (*x_SSL_set_bio)(SSL *, BIO *, BIO *);
|
||||
extern int (*x_SSL_set_ex_data)(SSL *, int, void *);
|
||||
extern int (*x_SSL_shutdown)(SSL *);
|
||||
extern int (*x_SSL_write)(SSL *, const void *, int);
|
||||
extern SSL_METHOD *(*x_SSLv23_server_method)(void);
|
||||
|
||||
#define BIO_ctrl x_BIO_ctrl
|
||||
#define BIO_f_buffer x_BIO_f_buffer
|
||||
#define BIO_free_all x_BIO_free_all
|
||||
#define BIO_new x_BIO_new
|
||||
#define BIO_new_socket x_BIO_new_socket
|
||||
#define BIO_pop x_BIO_pop
|
||||
#define BIO_push x_BIO_push
|
||||
#define ERR_clear_error x_ERR_clear_error
|
||||
#define ERR_clear_error x_ERR_clear_error
|
||||
#define ERR_peek_error x_ERR_peek_error
|
||||
#define ERR_peek_error x_ERR_peek_error
|
||||
#define SSL_CTX_callback_ctrl x_SSL_CTX_callback_ctrl
|
||||
#define SSL_CTX_check_private_key x_SSL_CTX_check_private_key
|
||||
#define SSL_CTX_ctrl x_SSL_CTX_ctrl
|
||||
#define SSL_CTX_free x_SSL_CTX_free
|
||||
#define SSL_CTX_new x_SSL_CTX_new
|
||||
#define SSL_CTX_use_PrivateKey_file x_SSL_CTX_use_PrivateKey_file
|
||||
#define SSL_CTX_use_certificate_file x_SSL_CTX_use_certificate_file
|
||||
#define SSL_ctrl x_SSL_ctrl
|
||||
#define SSL_free x_SSL_free
|
||||
#define SSL_get_error x_SSL_get_error
|
||||
#define SSL_get_ex_data x_SSL_get_ex_data
|
||||
#define SSL_get_rbio x_SSL_get_rbio
|
||||
#define SSL_get_servername x_SSL_get_servername
|
||||
#define SSL_get_wbio x_SSL_get_wbio
|
||||
#define SSL_library_init x_SSL_library_init
|
||||
#define SSL_new x_SSL_new
|
||||
#define SSL_read x_SSL_read
|
||||
#define SSL_set_SSL_CTX x_SSL_set_SSL_CTX
|
||||
#define SSL_set_accept_state x_SSL_set_accept_state
|
||||
#define SSL_set_bio x_SSL_set_bio
|
||||
#define SSL_set_ex_data x_SSL_set_ex_data
|
||||
#define SSL_shutdown x_SSL_shutdown
|
||||
#define SSL_write x_SSL_write
|
||||
#define SSLv23_server_method x_SSLv23_server_method
|
||||
|
||||
#undef BIO_set_buffer_read_data
|
||||
#undef SSL_CTX_set_tlsext_servername_arg
|
||||
#undef SSL_CTX_set_tlsext_servername_callback
|
||||
#undef SSL_get_app_data
|
||||
#undef SSL_set_app_data
|
||||
#undef SSL_set_mode
|
||||
#define BIO_set_buffer_read_data(b, buf, num) \
|
||||
(x_BIO_ctrl(b, BIO_C_SET_BUFF_READ_DATA, \
|
||||
num, buf))
|
||||
#define SSL_CTX_set_tlsext_servername_arg(ctx, arg) \
|
||||
(x_SSL_CTX_ctrl(ctx, \
|
||||
SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG, \
|
||||
0, (void *)arg))
|
||||
#define SSL_CTX_set_tlsext_servername_callback(ctx, cb) \
|
||||
(x_SSL_CTX_callback_ctrl(ctx, \
|
||||
SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, \
|
||||
(void (*)(void))cb))
|
||||
#define SSL_get_app_data(s) (x_SSL_get_ex_data(s, 0))
|
||||
#define SSL_set_app_data(s, arg) (x_SSL_set_ex_data(s, 0, (char *)arg))
|
||||
#define SSL_set_mode(ssl, op) (x_SSL_ctrl((ssl), SSL_CTRL_MODE, (op), NULL))
|
||||
|
||||
struct SSLSupport {
|
||||
int enabled;
|
||||
SSL_CTX *sslContext;
|
||||
char *sniCertificatePattern;
|
||||
int generateMissing;
|
||||
struct Trie sniContexts;
|
||||
};
|
||||
|
||||
int serverSupportsSSL(void);
|
||||
struct SSLSupport *newSSL();
|
||||
void initSSL(struct SSLSupport *ssl);
|
||||
void destroySSL(struct SSLSupport *ssl);
|
||||
void deleteSSL(struct SSLSupport *ssl);
|
||||
void sslGenerateCertificate(const char *certificate, const char *serverName);
|
||||
void sslSetCertificate(struct SSLSupport *ssl, const char *filename,
|
||||
int autoGenerateMissing);
|
||||
int sslEnable(struct SSLSupport *ssl, int enabled);
|
||||
void sslBlockSigPipe();
|
||||
int sslUnblockSigPipe();
|
||||
int sslPromoteToSSL(struct SSLSupport *ssl, SSL **sslHndl, int fd,
|
||||
const char *buf, int len);
|
||||
void sslFreeHndl(SSL **sslHndl);
|
||||
|
||||
#endif
|
214
libhttp/trie.c
Normal file
214
libhttp/trie.c
Normal file
|
@ -0,0 +1,214 @@
|
|||
// trie.c -- Basic implementation of a trie abstract data type
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libhttp/trie.h"
|
||||
#include "logging/logging.h"
|
||||
|
||||
struct Trie *newTrie(void (*destructor)(void *, char *), void *arg) {
|
||||
struct Trie *trie;
|
||||
check(trie = malloc(sizeof(struct Trie)));
|
||||
initTrie(trie, destructor, arg);
|
||||
return trie;
|
||||
}
|
||||
|
||||
void initTrie(struct Trie *trie, void (*destructor)(void *, char *),
|
||||
void *arg) {
|
||||
trie->destructor = destructor;
|
||||
trie->arg = arg;
|
||||
trie->key = NULL;
|
||||
trie->value = NULL;
|
||||
trie->idx = -1;
|
||||
trie->ch = '\000';
|
||||
trie->children = NULL;
|
||||
trie->numChildren = 0;
|
||||
}
|
||||
|
||||
void destroyTrie(struct Trie *trie) {
|
||||
if (trie) {
|
||||
free(trie->key);
|
||||
for (int i = 0; i < trie->numChildren; i++) {
|
||||
if (trie->destructor && !trie->children[i].ch) {
|
||||
trie->destructor(trie->arg, trie->children[i].value);
|
||||
} else {
|
||||
check(!trie->children[i].value);
|
||||
}
|
||||
destroyTrie(&trie->children[i]);
|
||||
}
|
||||
free(trie->children);
|
||||
}
|
||||
}
|
||||
|
||||
void deleteTrie(struct Trie *trie) {
|
||||
destroyTrie(trie);
|
||||
free(trie);
|
||||
}
|
||||
|
||||
static void addLeafToTrie(struct Trie *trie, char ch, const char *key, int len,
|
||||
void *value) {
|
||||
check (len >= 0);
|
||||
if (len) {
|
||||
check(trie->key = malloc(len));
|
||||
memcpy(trie->key, key, len);
|
||||
} else {
|
||||
trie->key = NULL;
|
||||
}
|
||||
trie->value = NULL;
|
||||
trie->idx = len;
|
||||
trie->ch = ch;
|
||||
check(trie->children = malloc(sizeof(struct Trie)));
|
||||
trie->numChildren = 1;
|
||||
initTrie(trie->children, trie->destructor, trie->arg);
|
||||
trie->children->value = value;
|
||||
}
|
||||
|
||||
void addToTrie(struct Trie *trie, const char *key, char *value) {
|
||||
if (trie->numChildren == 0) {
|
||||
addLeafToTrie(trie, '\000', key, strlen(key), value);
|
||||
} else {
|
||||
nextNode:;
|
||||
int len = strlen(key);
|
||||
for (int i = 0; i < trie->idx; i++) {
|
||||
if (key[i] != trie->key[i]) {
|
||||
struct Trie *child;
|
||||
check(child = malloc(2*sizeof(struct Trie)));
|
||||
child->destructor = trie->destructor;
|
||||
child->arg = trie->arg;
|
||||
check(child->key = malloc(trie->idx - i - 1));
|
||||
memcpy(child->key, trie->key + i + 1, trie->idx - i - 1);
|
||||
child->value = trie->value;
|
||||
child->idx = trie->idx - i - 1;
|
||||
child->ch = trie->key[i];
|
||||
child->children = trie->children;
|
||||
child->numChildren = trie->numChildren;
|
||||
trie->value = NULL;
|
||||
trie->idx = i;
|
||||
trie->children = child;
|
||||
trie->numChildren = 2;
|
||||
child++;
|
||||
child->destructor = trie->destructor;
|
||||
child->arg = trie->arg;
|
||||
if (key[i]) {
|
||||
addLeafToTrie(child, key[i], key + i + 1, len - i - 1, value);
|
||||
} else {
|
||||
initTrie(child, trie->destructor, trie->arg);
|
||||
child->value = value;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < trie->numChildren; i++) {
|
||||
if (key[trie->idx] == trie->children[i].ch) {
|
||||
if (trie->children[i].ch) {
|
||||
key += trie->idx + 1;
|
||||
trie = &trie->children[i];
|
||||
goto nextNode;
|
||||
} else {
|
||||
if (trie->destructor) {
|
||||
trie->destructor(trie->arg, trie->children[i].value);
|
||||
}
|
||||
trie->children[i].value = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
key += trie->idx;
|
||||
len -= trie->idx;
|
||||
check(trie->children = realloc(
|
||||
trie->children, ++trie->numChildren*sizeof(struct Trie)));
|
||||
struct Trie *newNode = &trie->children[trie->numChildren-1];
|
||||
if (*key) {
|
||||
newNode->destructor = trie->destructor;
|
||||
newNode->arg = trie->arg;
|
||||
addLeafToTrie(newNode, *key, key + 1, len - 1, value);
|
||||
} else {
|
||||
initTrie(newNode, trie->destructor, trie->arg);
|
||||
trie->value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *getFromTrie(const struct Trie *trie, const char *key, char **diff) {
|
||||
if (diff) {
|
||||
*diff = NULL;
|
||||
}
|
||||
for (;;) {
|
||||
if (trie->idx > 0) {
|
||||
if (memcmp(trie->key, key, trie->idx)) {
|
||||
return NULL;
|
||||
}
|
||||
key += trie->idx;
|
||||
}
|
||||
int partial = -1;
|
||||
for (int i = 0; ; i++) {
|
||||
if (i >= trie->numChildren) {
|
||||
if (diff && partial >= 0) {
|
||||
// If the caller provided a "diff" pointer, then we allow partial
|
||||
// matches for the longest possible prefix that is a key in the
|
||||
// trie. Upon return, the "diff" pointer points to the first
|
||||
// character in the key does not match.
|
||||
*diff = (char *)key;
|
||||
return trie->children[partial].value;
|
||||
}
|
||||
return NULL;
|
||||
} else if (*key == trie->children[i].ch) {
|
||||
if (!*key) {
|
||||
if (diff) {
|
||||
*diff = (char *)key;
|
||||
}
|
||||
return trie->children[i].value;
|
||||
}
|
||||
trie = &trie->children[i];
|
||||
key++;
|
||||
break;
|
||||
} else if (!trie->children[i].ch) {
|
||||
partial = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
70
libhttp/trie.h
Normal file
70
libhttp/trie.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
// trie.h -- Basic implementation of a trie abstract data type
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#ifndef TRIE_H__
|
||||
#define TRIE_H__
|
||||
|
||||
#include "libhttp/http.h"
|
||||
|
||||
struct Trie {
|
||||
void (*destructor)(void *, char *);
|
||||
void *arg;
|
||||
char *key;
|
||||
char *value;
|
||||
int idx;
|
||||
char ch;
|
||||
struct Trie *children;
|
||||
int numChildren;
|
||||
};
|
||||
|
||||
struct Trie *newTrie(void (*destructor)(void *, char *), void *arg);
|
||||
void initTrie(struct Trie *trie, void (*destructor)(void *, char *),
|
||||
void *arg);
|
||||
void destroyTrie(struct Trie *trie);
|
||||
void deleteTrie(struct Trie *trie);
|
||||
void addToTrie(struct Trie *trie, const char *key, char *value);
|
||||
char *getFromTrie(const struct Trie *trie, const char *key, char **diff);
|
||||
|
||||
#endif /* TRIE_H__ */
|
393
libhttp/url.c
Normal file
393
libhttp/url.c
Normal file
|
@ -0,0 +1,393 @@
|
|||
// url.c -- Object representing uniform resource locators
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libhttp/url.h"
|
||||
|
||||
#include "logging/logging.h"
|
||||
|
||||
static char *urlUnescape(char *s) {
|
||||
int warned = 0;
|
||||
char *r = s;
|
||||
for (char *u = s; *u; ) {
|
||||
char ch = *u++;
|
||||
if (ch == '+') {
|
||||
ch = ' ';
|
||||
} else if (ch == '%') {
|
||||
char c1 = *u;
|
||||
if ((c1 >= '0' && c1 <= '9') || ((c1 &= ~0x20) >= 'A' && c1 <= 'F')) {
|
||||
ch = c1 - (c1 > '9' ? 'A' - 10 : '0');
|
||||
char c2 = *++u;
|
||||
if ((c2 >= '0' && c2 <= '9') || ((c2 &= ~0x20) >= 'A' && c2 <= 'F')) {
|
||||
ch = (ch << 4) + c2 - (c2 > '9' ? 'A' - 10 : '0');
|
||||
++u;
|
||||
} else if (!warned++) {
|
||||
warn("Malformed URL encoded data \"%s\"", r);
|
||||
}
|
||||
} else if (!warned++) {
|
||||
warn("Malformed URL encoded data \"%s\"", r);
|
||||
}
|
||||
}
|
||||
*s++ = ch;
|
||||
}
|
||||
*s = '\000';
|
||||
return r;
|
||||
}
|
||||
|
||||
static void urlDestroyHashMapEntry(void *arg, char *key, char *value) {
|
||||
free(key);
|
||||
free(value);
|
||||
}
|
||||
|
||||
static char *urlMakeString(const char *buf, int len) {
|
||||
if (!buf) {
|
||||
return NULL;
|
||||
} else {
|
||||
char *s;
|
||||
check(s = malloc(len + 1));
|
||||
memcpy(s, buf, len);
|
||||
s[len] = '\000';
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
static void urlParseQueryString(struct URL *url, const char *query, int len) {
|
||||
const char *key = query;
|
||||
const char *value = NULL;
|
||||
for (const char *ampersand = query; len-- >= 0; ampersand++) {
|
||||
char ch = len >= 0 ? *ampersand : '\000';
|
||||
if (ch == '=' && !value) {
|
||||
value = ampersand + 1;
|
||||
} else if (ch == '&' || len < 0) {
|
||||
int kl = (value ? value-1 : ampersand) - key;
|
||||
int vl = value ? ampersand - value : 0;
|
||||
if (kl) {
|
||||
char *k = urlMakeString(key, kl);
|
||||
urlUnescape(k);
|
||||
char *v = NULL;
|
||||
if (value) {
|
||||
v = urlMakeString(value, vl);
|
||||
urlUnescape(v);
|
||||
}
|
||||
addToHashMap(&url->args, k, v);
|
||||
}
|
||||
key = ampersand + 1;
|
||||
value = NULL;
|
||||
}
|
||||
if (!ch) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void urlParseHeaderLine(struct HashMap *hashmap, const char *s,
|
||||
int len) {
|
||||
while (s && len > 0) {
|
||||
while (len > 0 && (*s == ' ' || *s == ';')) {
|
||||
s++;
|
||||
len--;
|
||||
}
|
||||
const char *key = s;
|
||||
const char *value = NULL;
|
||||
while (len > 0 && *s != ';') {
|
||||
if (*s == '=' && value == NULL) {
|
||||
value = s + 1;
|
||||
}
|
||||
s++;
|
||||
len--;
|
||||
}
|
||||
int kl = (value ? value-1 : s) - key;
|
||||
int vl = value ? s - value : 0;
|
||||
if (kl) {
|
||||
char *k = urlMakeString(key, kl);
|
||||
for (char *t = k; *t; t++) {
|
||||
if (*t >= 'a' && *t <= 'z') {
|
||||
*t |= 0x20;
|
||||
}
|
||||
}
|
||||
char *v = NULL;
|
||||
if (value) {
|
||||
if (vl >= 2 && value[0] == '"' && value[vl-1] == '"') {
|
||||
value++;
|
||||
vl--;
|
||||
}
|
||||
v = urlMakeString(value, vl);
|
||||
}
|
||||
addToHashMap(hashmap, k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char *urlMemstr(const char *buf, int len, const char *s) {
|
||||
int sLen = strlen(s);
|
||||
if (!sLen) {
|
||||
return buf;
|
||||
}
|
||||
while (len >= sLen) {
|
||||
if (len > sLen) {
|
||||
char *first = memchr(buf, *s, len - sLen);
|
||||
if (!first) {
|
||||
return NULL;
|
||||
}
|
||||
len -= first - buf;
|
||||
buf = first;
|
||||
}
|
||||
if (!memcmp(buf, s, sLen)) {
|
||||
return buf;
|
||||
}
|
||||
buf++;
|
||||
len--;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int urlMemcmp(const char *buf, int len, const char *s) {
|
||||
int sLen = strlen(s);
|
||||
if (len < sLen) {
|
||||
return s[len];
|
||||
} else {
|
||||
return memcmp(buf, s, sLen);
|
||||
}
|
||||
}
|
||||
|
||||
static int urlMemcasecmp(const char *buf, int len, const char *s) {
|
||||
int sLen = strlen(s);
|
||||
if (len < sLen) {
|
||||
return s[len];
|
||||
} else {
|
||||
return strncasecmp(buf, s, sLen);
|
||||
}
|
||||
}
|
||||
|
||||
static void urlParsePart(struct URL *url, const char *buf, int len) {
|
||||
// Most browsers seem to forget quoting data in the header fields. This
|
||||
// means, it is quite possible for an HTML form to cause the submission of
|
||||
// unparseable "multipart/form-data". If this happens, we just give up
|
||||
// and ignore the malformed data.
|
||||
// Example:
|
||||
// <form method="POST" enctype="multipart/form-data">
|
||||
// <input type="file" name="" X: x="">
|
||||
// <input type="submit">
|
||||
// </form>
|
||||
char *name = NULL;
|
||||
for (const char *eol; !!(eol = urlMemstr(buf, len, "\r\n")); ) {
|
||||
if (buf == eol) {
|
||||
buf += 2;
|
||||
len -= 2;
|
||||
if (name) {
|
||||
char *value = len ? urlMakeString(buf, len) : NULL;
|
||||
addToHashMap(&url->args, name, value);
|
||||
name = NULL;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if (!name && !urlMemcasecmp(buf, len, "content-disposition:")) {
|
||||
struct HashMap fields;
|
||||
initHashMap(&fields, urlDestroyHashMapEntry, NULL);
|
||||
urlParseHeaderLine(&fields, buf + 20, eol - buf - 20);
|
||||
if (getRefFromHashMap(&fields, "form-data")) {
|
||||
// We currently don't bother to deal with binary files (e.g. files
|
||||
// that include NUL characters). If this ever becomes necessary,
|
||||
// we could check for the existence of a "filename" field and use
|
||||
// that as an indicator to store the payload in something other
|
||||
// than "url->args".
|
||||
name = (char *)getFromHashMap(&fields, "name");
|
||||
if (name && *name) {
|
||||
check(name = strdup(name));
|
||||
}
|
||||
}
|
||||
destroyHashMap(&fields);
|
||||
}
|
||||
len -= eol - buf + 2;
|
||||
buf = eol + 2;
|
||||
}
|
||||
}
|
||||
free(name);
|
||||
}
|
||||
|
||||
static void urlParsePostBody(struct URL *url,
|
||||
const struct HttpConnection *http,
|
||||
const char *buf, int len) {
|
||||
struct HashMap contentType;
|
||||
initHashMap(&contentType, urlDestroyHashMapEntry, NULL);
|
||||
const char *ctHeader = getFromHashMap(&http->header, "content-type");
|
||||
urlParseHeaderLine(&contentType, ctHeader, ctHeader ? strlen(ctHeader) : 0);
|
||||
if (getRefFromHashMap(&contentType, "application/x-www-form-urlencoded")) {
|
||||
urlParseQueryString(url, buf, len);
|
||||
} else if (getRefFromHashMap(&contentType, "multipart/form-data")) {
|
||||
const char *boundary = getFromHashMap(&contentType, "boundary");
|
||||
if (boundary && *boundary) {
|
||||
const char *lastPart = NULL;
|
||||
for (const char *part = buf; len > 0; ) {
|
||||
const char *ptr;
|
||||
if ((part == buf && (ptr = urlMemstr(part, len, "--")) != NULL) ||
|
||||
(ptr = urlMemstr(part, len, "\r\n--")) != NULL) {
|
||||
len -= ptr - part + (part == buf ? 2 : 4);
|
||||
part = ptr + (part == buf ? 2 : 4);
|
||||
if (!urlMemcmp(part, len, boundary)) {
|
||||
int i = strlen(boundary);
|
||||
len -= i;
|
||||
part += i;
|
||||
if (!urlMemcmp(part, len, "\r\n")) {
|
||||
len -= 2;
|
||||
part += 2;
|
||||
if (lastPart) {
|
||||
urlParsePart(url, lastPart, ptr - lastPart);
|
||||
} else {
|
||||
if (ptr != buf) {
|
||||
info("Ignoring prologue before \"multipart/form-data\"");
|
||||
}
|
||||
}
|
||||
lastPart = part;
|
||||
} else if (!urlMemcmp(part, len, "--\r\n")) {
|
||||
len -= 4;
|
||||
part += 4;
|
||||
urlParsePart(url, lastPart, ptr - lastPart);
|
||||
lastPart = NULL;
|
||||
if (len > 0) {
|
||||
info("Ignoring epilogue past end of \"multipart/"
|
||||
"form-data\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lastPart) {
|
||||
warn("Missing final \"boundary\" for \"multipart/form-data\"");
|
||||
}
|
||||
} else {
|
||||
warn("Missing \"boundary\" information for \"multipart/form-data\"");
|
||||
}
|
||||
}
|
||||
destroyHashMap(&contentType);
|
||||
}
|
||||
|
||||
struct URL *newURL(const struct HttpConnection *http,
|
||||
const char *buf, int len) {
|
||||
struct URL *url;
|
||||
check(url = malloc(sizeof(struct URL)));
|
||||
initURL(url, http, buf, len);
|
||||
return url;
|
||||
}
|
||||
|
||||
void initURL(struct URL *url, const struct HttpConnection *http,
|
||||
const char *buf, int len) {
|
||||
url->protocol = strdup(httpGetProtocol(http));
|
||||
url->user = NULL;
|
||||
url->password = NULL;
|
||||
url->host = strdup(httpGetHost(http));
|
||||
url->port = httpGetPort(http);
|
||||
url->path = strdup(httpGetPath(http));
|
||||
url->pathinfo = strdup(httpGetPathInfo(http));
|
||||
url->query = strdup(httpGetQuery(http));
|
||||
url->anchor = NULL;
|
||||
initHashMap(&url->args, urlDestroyHashMapEntry, NULL);
|
||||
if (!strcmp(http->method, "GET")) {
|
||||
urlParseQueryString(url, url->query, strlen(url->query));
|
||||
} else if (!strcmp(http->method, "POST")) {
|
||||
urlParsePostBody(url, http, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
void destroyURL(struct URL *url) {
|
||||
if (url) {
|
||||
free(url->protocol);
|
||||
free(url->user);
|
||||
free(url->password);
|
||||
free(url->host);
|
||||
free(url->path);
|
||||
free(url->pathinfo);
|
||||
free(url->query);
|
||||
free(url->anchor);
|
||||
destroyHashMap(&url->args);
|
||||
}
|
||||
}
|
||||
|
||||
void deleteURL(struct URL *url) {
|
||||
destroyURL(url);
|
||||
free(url);
|
||||
}
|
||||
|
||||
const char *urlGetProtocol(struct URL *url) {
|
||||
return url->protocol;
|
||||
}
|
||||
|
||||
const char *urlGetUser(struct URL *url) {
|
||||
return url->user;
|
||||
}
|
||||
|
||||
const char *urlGetPassword(struct URL *url) {
|
||||
return url->password;
|
||||
}
|
||||
|
||||
const char *urlGetHost(struct URL *url) {
|
||||
return url->host;
|
||||
}
|
||||
|
||||
int urlGetPort(struct URL *url) {
|
||||
return url->port;
|
||||
}
|
||||
|
||||
const char *urlGetPath(struct URL *url) {
|
||||
return url->path;
|
||||
}
|
||||
|
||||
const char *urlGetPathInfo(struct URL *url) {
|
||||
return url->pathinfo;
|
||||
}
|
||||
|
||||
const char *urlGetQuery(struct URL *url) {
|
||||
return url->query;
|
||||
}
|
||||
|
||||
const char *urlGetAnchor(struct URL *url) {
|
||||
return url->anchor;
|
||||
}
|
||||
|
||||
const struct HashMap *urlGetArgs(struct URL *url) {
|
||||
return &url->args;
|
||||
}
|
84
libhttp/url.h
Normal file
84
libhttp/url.h
Normal file
|
@ -0,0 +1,84 @@
|
|||
// url.h -- Object representing uniform resource locators
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#ifndef URL_H__
|
||||
#define URL_H__
|
||||
|
||||
#include "libhttp/http.h"
|
||||
|
||||
#include "libhttp/hashmap.h"
|
||||
#include "libhttp/httpconnection.h"
|
||||
|
||||
struct URL {
|
||||
char *protocol;
|
||||
char *user;
|
||||
char *password;
|
||||
char *host;
|
||||
int port;
|
||||
char *path;
|
||||
char *pathinfo;
|
||||
char *query;
|
||||
char *anchor;
|
||||
struct HashMap args;
|
||||
};
|
||||
|
||||
struct URL *newURL(const struct HttpConnection *http,
|
||||
const char *buf, int len);
|
||||
void initURL(struct URL *url, const struct HttpConnection *http,
|
||||
const char *buf, int len);
|
||||
void destroyURL(struct URL *url);
|
||||
void deleteURL(struct URL *url);
|
||||
const char *urlGetProtocol(struct URL *url);
|
||||
const char *urlGetUser(struct URL *url);
|
||||
const char *urlGetPassword(struct URL *url);
|
||||
const char *urlGetHost(struct URL *url);
|
||||
int urlGetPort(struct URL *url);
|
||||
const char *urlGetPath(struct URL *url);
|
||||
const char *urlGetPathInfo(struct URL *url);
|
||||
const char *urlGetQuery(struct URL *url);
|
||||
const char *urlGetAnchor(struct URL *url);
|
||||
const struct HashMap *urlGetArgs(struct URL *url);
|
||||
|
||||
#endif /* URL_H__ */
|
170
logging/logging.c
Normal file
170
logging/logging.c
Normal file
|
@ -0,0 +1,170 @@
|
|||
// logging.c -- Utility functions for managing log messages
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "logging/logging.h"
|
||||
|
||||
static int verbosity = MSG_DEFAULT;
|
||||
|
||||
static void debugMsg(int level, const char *fmt, va_list ap) {
|
||||
if (level <= verbosity) {
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fputs("\n", stderr);
|
||||
}
|
||||
}
|
||||
|
||||
void debug(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
debugMsg(MSG_DEBUG, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void info(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
debugMsg(MSG_INFO, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void warn(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
debugMsg(MSG_WARN, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void error(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
debugMsg(MSG_ERROR, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void message(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
debugMsg(MSG_MESSAGE, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void fatal(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
debugMsg(MSG_MESSAGE, fmt, ap);
|
||||
va_end(ap);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
int logIsDebug(void) {
|
||||
return verbosity >= MSG_DEBUG;
|
||||
}
|
||||
|
||||
int logIsInfo(void) {
|
||||
return verbosity >= MSG_INFO;
|
||||
}
|
||||
|
||||
int logIsWarn(void) {
|
||||
return verbosity >= MSG_WARN;
|
||||
}
|
||||
|
||||
int logIsError(void) {
|
||||
return verbosity >= MSG_ERROR;
|
||||
}
|
||||
|
||||
int logIsMessage(void) {
|
||||
return verbosity >= MSG_MESSAGE;
|
||||
}
|
||||
|
||||
int logIsQuiet(void) {
|
||||
return verbosity <= MSG_QUIET;
|
||||
}
|
||||
|
||||
int logIsDefault(void) {
|
||||
return verbosity == MSG_DEFAULT;
|
||||
}
|
||||
|
||||
int logIsVerbose(void) {
|
||||
return verbosity >= MSG_ERROR;
|
||||
}
|
||||
|
||||
void logSetLogLevel(int level) {
|
||||
check(level >= MSG_QUIET && level <= MSG_DEBUG);
|
||||
verbosity = level;
|
||||
}
|
||||
|
||||
char *vStringPrintf(char *buf, const char *fmt, va_list ap) {
|
||||
int offset = buf ? strlen(buf) : 0;
|
||||
int len = 80;
|
||||
check(buf = realloc(buf, offset + len));
|
||||
int p = vsnprintf(buf + offset, len, fmt, ap);
|
||||
if (p >= len) {
|
||||
check(buf = realloc(buf, offset + p + 1));
|
||||
check(vsnprintf(buf + offset, p + 1, fmt, ap) == p);
|
||||
} else if (p < 0) {
|
||||
int inc = 256;
|
||||
do {
|
||||
len += inc;
|
||||
check(len < (1 << 20));
|
||||
if (inc < (32 << 10)) {
|
||||
inc <<= 1;
|
||||
}
|
||||
check(buf = realloc(buf, offset + len));
|
||||
} while (vsnprintf(buf + offset, len, fmt, ap) < 0);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *stringPrintf(char *buf, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
char *s = vStringPrintf(buf, fmt, ap);
|
||||
va_end(ap);
|
||||
return s;
|
||||
}
|
92
logging/logging.h
Normal file
92
logging/logging.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
// logging.h -- Utility functions for managing log messages
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#ifndef LOGGING_H__
|
||||
#define LOGGING_H__
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#define MSG_QUIET -1
|
||||
#define MSG_MESSAGE 0
|
||||
#define MSG_ERROR 1
|
||||
#define MSG_WARN 2
|
||||
#define MSG_INFO 3
|
||||
#define MSG_DEBUG 4
|
||||
#define MSG_DEFAULT MSG_ERROR
|
||||
|
||||
#define check(x) do { \
|
||||
if (!(x)) \
|
||||
fatal("Check failed at "__FILE__":%d in %s(): %s", \
|
||||
__LINE__, __func__, #x); \
|
||||
} while (0)
|
||||
|
||||
#define dcheck(x) do { \
|
||||
if (!(x)) \
|
||||
(logIsDebug() ? fatal : error)( \
|
||||
"Check failed at "__FILE__":%d in %s(): %s", \
|
||||
__LINE__, __func__, #x); \
|
||||
} while (0)
|
||||
|
||||
void debug(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
void info(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
void warn(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
void error(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
void message(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
void fatal(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
int logIsDebug(void);
|
||||
int logIsInfo(void);
|
||||
int logIsWarn(void);
|
||||
int logIsError(void);
|
||||
int logIsMessage(void);
|
||||
int logIsQuiet(void);
|
||||
int logIsDefault(void);
|
||||
int logIsVerbose(void);
|
||||
void logSetLogLevel(int level);
|
||||
|
||||
char *vStringPrintf(char *buf, const char *fmt, va_list ap);
|
||||
char *stringPrintf(char *buf, const char *fmt, ...)
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
|
||||
#endif /* LOGGING_H__ */
|
367
missing
Normal file
367
missing
Normal file
|
@ -0,0 +1,367 @@
|
|||
#! /bin/sh
|
||||
# Common stub for a few missing GNU programs while installing.
|
||||
|
||||
scriptversion=2006-05-10.23
|
||||
|
||||
# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006
|
||||
# Free Software Foundation, Inc.
|
||||
# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
|
||||
|
||||
# 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, 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 Street, Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
if test $# -eq 0; then
|
||||
echo 1>&2 "Try \`$0 --help' for more information"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run=:
|
||||
sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
|
||||
sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
|
||||
|
||||
# In the cases where this matters, `missing' is being run in the
|
||||
# srcdir already.
|
||||
if test -f configure.ac; then
|
||||
configure_ac=configure.ac
|
||||
else
|
||||
configure_ac=configure.in
|
||||
fi
|
||||
|
||||
msg="missing on your system"
|
||||
|
||||
case $1 in
|
||||
--run)
|
||||
# Try to run requested program, and just exit if it succeeds.
|
||||
run=
|
||||
shift
|
||||
"$@" && exit 0
|
||||
# Exit code 63 means version mismatch. This often happens
|
||||
# when the user try to use an ancient version of a tool on
|
||||
# a file that requires a minimum version. In this case we
|
||||
# we should proceed has if the program had been absent, or
|
||||
# if --run hadn't been passed.
|
||||
if test $? = 63; then
|
||||
run=:
|
||||
msg="probably too old"
|
||||
fi
|
||||
;;
|
||||
|
||||
-h|--h|--he|--hel|--help)
|
||||
echo "\
|
||||
$0 [OPTION]... PROGRAM [ARGUMENT]...
|
||||
|
||||
Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
|
||||
error status if there is no known handling for PROGRAM.
|
||||
|
||||
Options:
|
||||
-h, --help display this help and exit
|
||||
-v, --version output version information and exit
|
||||
--run try to run the given command, and emulate it if it fails
|
||||
|
||||
Supported PROGRAM values:
|
||||
aclocal touch file \`aclocal.m4'
|
||||
autoconf touch file \`configure'
|
||||
autoheader touch file \`config.h.in'
|
||||
autom4te touch the output file, or create a stub one
|
||||
automake touch all \`Makefile.in' files
|
||||
bison create \`y.tab.[ch]', if possible, from existing .[ch]
|
||||
flex create \`lex.yy.c', if possible, from existing .c
|
||||
help2man touch the output file
|
||||
lex create \`lex.yy.c', if possible, from existing .c
|
||||
makeinfo touch the output file
|
||||
tar try tar, gnutar, gtar, then tar without non-portable flags
|
||||
yacc create \`y.tab.[ch]', if possible, from existing .[ch]
|
||||
|
||||
Send bug reports to <bug-automake@gnu.org>."
|
||||
exit $?
|
||||
;;
|
||||
|
||||
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
|
||||
echo "missing $scriptversion (GNU Automake)"
|
||||
exit $?
|
||||
;;
|
||||
|
||||
-*)
|
||||
echo 1>&2 "$0: Unknown \`$1' option"
|
||||
echo 1>&2 "Try \`$0 --help' for more information"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
# Now exit if we have it, but it failed. Also exit now if we
|
||||
# don't have it and --version was passed (most likely to detect
|
||||
# the program).
|
||||
case $1 in
|
||||
lex|yacc)
|
||||
# Not GNU programs, they don't have --version.
|
||||
;;
|
||||
|
||||
tar)
|
||||
if test -n "$run"; then
|
||||
echo 1>&2 "ERROR: \`tar' requires --run"
|
||||
exit 1
|
||||
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
*)
|
||||
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
|
||||
# We have it, but it failed.
|
||||
exit 1
|
||||
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
|
||||
# Could not run --version or --help. This is probably someone
|
||||
# running `$TOOL --version' or `$TOOL --help' to check whether
|
||||
# $TOOL exists and not knowing $TOOL uses missing.
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# If it does not exist, or fails to run (possibly an outdated version),
|
||||
# try to emulate it.
|
||||
case $1 in
|
||||
aclocal*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`acinclude.m4' or \`${configure_ac}'. You might want
|
||||
to install the \`Automake' and \`Perl' packages. Grab them from
|
||||
any GNU archive site."
|
||||
touch aclocal.m4
|
||||
;;
|
||||
|
||||
autoconf)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`${configure_ac}'. You might want to install the
|
||||
\`Autoconf' and \`GNU m4' packages. Grab them from any GNU
|
||||
archive site."
|
||||
touch configure
|
||||
;;
|
||||
|
||||
autoheader)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`acconfig.h' or \`${configure_ac}'. You might want
|
||||
to install the \`Autoconf' and \`GNU m4' packages. Grab them
|
||||
from any GNU archive site."
|
||||
files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
|
||||
test -z "$files" && files="config.h"
|
||||
touch_files=
|
||||
for f in $files; do
|
||||
case $f in
|
||||
*:*) touch_files="$touch_files "`echo "$f" |
|
||||
sed -e 's/^[^:]*://' -e 's/:.*//'`;;
|
||||
*) touch_files="$touch_files $f.in";;
|
||||
esac
|
||||
done
|
||||
touch $touch_files
|
||||
;;
|
||||
|
||||
automake*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
|
||||
You might want to install the \`Automake' and \`Perl' packages.
|
||||
Grab them from any GNU archive site."
|
||||
find . -type f -name Makefile.am -print |
|
||||
sed 's/\.am$/.in/' |
|
||||
while read f; do touch "$f"; done
|
||||
;;
|
||||
|
||||
autom4te)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is needed, but is $msg.
|
||||
You might have modified some files without having the
|
||||
proper tools for further handling them.
|
||||
You can get \`$1' as part of \`Autoconf' from any GNU
|
||||
archive site."
|
||||
|
||||
file=`echo "$*" | sed -n "$sed_output"`
|
||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
|
||||
if test -f "$file"; then
|
||||
touch $file
|
||||
else
|
||||
test -z "$file" || exec >$file
|
||||
echo "#! /bin/sh"
|
||||
echo "# Created by GNU Automake missing as a replacement of"
|
||||
echo "# $ $@"
|
||||
echo "exit 0"
|
||||
chmod +x $file
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
bison|yacc)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' $msg. You should only need it if
|
||||
you modified a \`.y' file. You may need the \`Bison' package
|
||||
in order for those modifications to take effect. You can get
|
||||
\`Bison' from any GNU archive site."
|
||||
rm -f y.tab.c y.tab.h
|
||||
if test $# -ne 1; then
|
||||
eval LASTARG="\${$#}"
|
||||
case $LASTARG in
|
||||
*.y)
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
|
||||
if test -f "$SRCFILE"; then
|
||||
cp "$SRCFILE" y.tab.c
|
||||
fi
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
|
||||
if test -f "$SRCFILE"; then
|
||||
cp "$SRCFILE" y.tab.h
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if test ! -f y.tab.h; then
|
||||
echo >y.tab.h
|
||||
fi
|
||||
if test ! -f y.tab.c; then
|
||||
echo 'main() { return 0; }' >y.tab.c
|
||||
fi
|
||||
;;
|
||||
|
||||
lex|flex)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a \`.l' file. You may need the \`Flex' package
|
||||
in order for those modifications to take effect. You can get
|
||||
\`Flex' from any GNU archive site."
|
||||
rm -f lex.yy.c
|
||||
if test $# -ne 1; then
|
||||
eval LASTARG="\${$#}"
|
||||
case $LASTARG in
|
||||
*.l)
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
|
||||
if test -f "$SRCFILE"; then
|
||||
cp "$SRCFILE" lex.yy.c
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if test ! -f lex.yy.c; then
|
||||
echo 'main() { return 0; }' >lex.yy.c
|
||||
fi
|
||||
;;
|
||||
|
||||
help2man)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a dependency of a manual page. You may need the
|
||||
\`Help2man' package in order for those modifications to take
|
||||
effect. You can get \`Help2man' from any GNU archive site."
|
||||
|
||||
file=`echo "$*" | sed -n "$sed_output"`
|
||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
|
||||
if test -f "$file"; then
|
||||
touch $file
|
||||
else
|
||||
test -z "$file" || exec >$file
|
||||
echo ".ab help2man is required to generate this page"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
makeinfo)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a \`.texi' or \`.texinfo' file, or any other file
|
||||
indirectly affecting the aspect of the manual. The spurious
|
||||
call might also be the consequence of using a buggy \`make' (AIX,
|
||||
DU, IRIX). You might want to install the \`Texinfo' package or
|
||||
the \`GNU make' package. Grab either from any GNU archive site."
|
||||
# The file to touch is that specified with -o ...
|
||||
file=`echo "$*" | sed -n "$sed_output"`
|
||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
|
||||
if test -z "$file"; then
|
||||
# ... or it is the one specified with @setfilename ...
|
||||
infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
|
||||
file=`sed -n '
|
||||
/^@setfilename/{
|
||||
s/.* \([^ ]*\) *$/\1/
|
||||
p
|
||||
q
|
||||
}' $infile`
|
||||
# ... or it is derived from the source name (dir/f.texi becomes f.info)
|
||||
test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
|
||||
fi
|
||||
# If the file does not exist, the user really needs makeinfo;
|
||||
# let's fail without touching anything.
|
||||
test -f $file || exit 1
|
||||
touch $file
|
||||
;;
|
||||
|
||||
tar)
|
||||
shift
|
||||
|
||||
# We have already tried tar in the generic part.
|
||||
# Look for gnutar/gtar before invocation to avoid ugly error
|
||||
# messages.
|
||||
if (gnutar --version > /dev/null 2>&1); then
|
||||
gnutar "$@" && exit 0
|
||||
fi
|
||||
if (gtar --version > /dev/null 2>&1); then
|
||||
gtar "$@" && exit 0
|
||||
fi
|
||||
firstarg="$1"
|
||||
if shift; then
|
||||
case $firstarg in
|
||||
*o*)
|
||||
firstarg=`echo "$firstarg" | sed s/o//`
|
||||
tar "$firstarg" "$@" && exit 0
|
||||
;;
|
||||
esac
|
||||
case $firstarg in
|
||||
*h*)
|
||||
firstarg=`echo "$firstarg" | sed s/h//`
|
||||
tar "$firstarg" "$@" && exit 0
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
echo 1>&2 "\
|
||||
WARNING: I can't seem to be able to run \`tar' with the given arguments.
|
||||
You may want to install GNU tar or Free paxutils, or check the
|
||||
command line arguments."
|
||||
exit 1
|
||||
;;
|
||||
|
||||
*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is needed, and is $msg.
|
||||
You might have modified some files without having the
|
||||
proper tools for further handling them. Check the \`README' file,
|
||||
it often tells you about the needed prerequisites for installing
|
||||
this package. You may also peek at any GNU archive site, in case
|
||||
some other package would contain this missing \`$1' program."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-end: "$"
|
||||
# End:
|
BIN
shellinabox/beep.wav
Normal file
BIN
shellinabox/beep.wav
Normal file
Binary file not shown.
260
shellinabox/externalfile.c
Normal file
260
shellinabox/externalfile.c
Normal file
|
@ -0,0 +1,260 @@
|
|||
// externalfile.h -- Serve static files through HTTP/HTTPS
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "shellinabox/externalfile.h"
|
||||
#include "shellinabox/service.h"
|
||||
#include "shellinabox/session.h"
|
||||
#include "libhttp/server.h"
|
||||
#include "logging/logging.h"
|
||||
|
||||
|
||||
static int externalFileHttpHandler(HttpConnection *http, void *arg,
|
||||
const char *buf, int len) {
|
||||
checkGraveyard();
|
||||
struct ExternalFileState *state
|
||||
= (struct ExternalFileState *)httpGetPrivate(http);
|
||||
if (!state) {
|
||||
// Normalize the path info
|
||||
URL *url = newURL(http, buf, len);
|
||||
const char *pathInfo = urlGetPathInfo(url);
|
||||
while (*pathInfo == '/') {
|
||||
pathInfo++;
|
||||
}
|
||||
|
||||
// Compute file name of external file
|
||||
char *fn;
|
||||
check(fn = malloc(strlen((char *)arg) +
|
||||
(*pathInfo ? strlen(pathInfo) + 1 : 0) + 1));
|
||||
strcpy(fn, (char *)arg);
|
||||
if (*pathInfo) {
|
||||
// Append pathInfo, if available
|
||||
strcat(fn, "/");
|
||||
const char *ptr = pathInfo;
|
||||
while (*ptr == '/') {
|
||||
ptr++;
|
||||
}
|
||||
strcat(fn, ptr);
|
||||
|
||||
// Any files/directories starting with a dot are inaccessible to us
|
||||
do {
|
||||
if (*ptr == '.') {
|
||||
deleteURL(url);
|
||||
free(fn);
|
||||
httpSendReply(http, 404, "File not found", NULL);
|
||||
return HTTP_DONE;
|
||||
}
|
||||
ptr = strchr(ptr + 1, '/');
|
||||
} while (ptr);
|
||||
}
|
||||
|
||||
// Open file for reading
|
||||
int fd = NOINTR(open(fn, O_RDONLY|O_LARGEFILE));
|
||||
|
||||
// Recognize a couple of common MIME types
|
||||
static const struct {
|
||||
const char *suffix, *mimeType;
|
||||
} mimeTypes[] = { { "html", "text/html; charset=utf-8" },
|
||||
{ "txt", "text/plain; charset=utf-8" },
|
||||
{ "js", "text/javascript; charset=utf-8" },
|
||||
{ "css", "text/css; charset=utf-8" },
|
||||
{ "ico", "image/x-icon" },
|
||||
{ "jpg", "image/jpeg" },
|
||||
{ "gif", "image/gif" },
|
||||
{ "png", "image/png" },
|
||||
{ "wav", "audio/x-wav" },
|
||||
{ "mp3", "audio/mpeg" },
|
||||
{ "au", "audio/basic" },
|
||||
{ "mid", "audio/midi" },
|
||||
{ NULL, NULL } };
|
||||
const char *mimeType = "application/octet-stream";
|
||||
char *suffix = strrchr(fn, '.');
|
||||
if (!suffix) {
|
||||
suffix = strrchr(urlGetPath(url), '.');
|
||||
}
|
||||
if (suffix) {
|
||||
suffix++;
|
||||
for (int i = 0; mimeTypes[i].suffix; i++) {
|
||||
if (!strcmp(suffix, mimeTypes[i].suffix)) {
|
||||
mimeType = mimeTypes[i].mimeType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
deleteURL(url);
|
||||
|
||||
if (fd < 0) {
|
||||
free(fn);
|
||||
httpSendReply(http, 404, "File not found", NULL);
|
||||
return HTTP_DONE;
|
||||
}
|
||||
|
||||
// We only serve regular files, and restrict the file size to 100MB.
|
||||
// As a special-case, we also allow access to /dev/null.
|
||||
struct stat64 sb = { 0 };
|
||||
if (strcmp(fn, "/dev/null") &&
|
||||
(fstat64(fd, &sb) ||
|
||||
!S_ISREG(sb.st_mode) ||
|
||||
sb.st_size > (100 << 20))) {
|
||||
free(fn);
|
||||
NOINTR(close(fd));
|
||||
httpSendReply(http, 404, "File not found", NULL);
|
||||
return HTTP_DONE;
|
||||
}
|
||||
free(fn);
|
||||
|
||||
// Set up response header
|
||||
char *response = stringPrintf(NULL,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: %s\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"\r\n",
|
||||
mimeType, (int)sb.st_size);
|
||||
int respLen = strlen(response);
|
||||
|
||||
ssize_t bytes = -1;
|
||||
if (strcmp(httpGetMethod(http), "HEAD")) {
|
||||
// Read at most 64kB in one go
|
||||
ssize_t dataLen = 65536;
|
||||
if (dataLen > sb.st_size) {
|
||||
dataLen = sb.st_size;
|
||||
}
|
||||
check(response = realloc(response, respLen + dataLen + 1));
|
||||
bytes = NOINTR(read(fd, response + respLen, dataLen));
|
||||
|
||||
if (bytes < 0) {
|
||||
free(response);
|
||||
NOINTR(close(fd));
|
||||
httpSendReply(http, 404, "File not found", NULL);
|
||||
return HTTP_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (bytes < 0 || bytes == sb.st_size) {
|
||||
// Read entire file. Transmit it in one go.
|
||||
httpTransfer(http, response,
|
||||
respLen + (bytes > 0 ? bytes : 0));
|
||||
NOINTR(close(fd));
|
||||
return HTTP_DONE;
|
||||
} else {
|
||||
// Transmit partial reply and store state for future calls into the
|
||||
// handler.
|
||||
httpTransferPartialReply(http, response, respLen + bytes);
|
||||
|
||||
check(state = malloc(sizeof(struct ExternalFileState)));
|
||||
state->fd = fd;
|
||||
state->totalSize = sb.st_size;
|
||||
state->partialSize = bytes;
|
||||
httpSetPrivate(http, state);
|
||||
|
||||
return HTTP_PARTIAL_REPLY;
|
||||
}
|
||||
} else {
|
||||
// We get called again, because all previously read partial data has now
|
||||
// been sent to the peer.
|
||||
int rc = HTTP_DONE;
|
||||
|
||||
// If the connection was closed unexpectedly, clean up now
|
||||
if (!buf) {
|
||||
done:
|
||||
NOINTR(close(state->fd));
|
||||
free(state);
|
||||
httpSetPrivate(http, NULL);
|
||||
return rc;
|
||||
} else {
|
||||
ssize_t dataLen = 65536;
|
||||
if (dataLen > state->totalSize - state->partialSize) {
|
||||
dataLen = state->totalSize - state->partialSize;
|
||||
}
|
||||
char *buf;
|
||||
check(buf = malloc(dataLen));
|
||||
ssize_t bytes = NOINTR(read(state->fd, buf, dataLen));
|
||||
if (bytes < 0) {
|
||||
free(buf);
|
||||
rc = HTTP_ERROR;
|
||||
goto done;
|
||||
}
|
||||
state->partialSize += bytes;
|
||||
if (state->partialSize >= state->totalSize) {
|
||||
// Done serving the entire file
|
||||
httpTransfer(http, buf, bytes);
|
||||
goto done;
|
||||
} else {
|
||||
// More partial data pending
|
||||
httpTransferPartialReply(http, buf, bytes);
|
||||
return HTTP_PARTIAL_REPLY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int registerExternalFiles(void *arg, const char *key, char **value) {
|
||||
Server *server = (Server *)arg;
|
||||
if (*key == '/') {
|
||||
// Absolute URL paths get registered for this particular path, only
|
||||
serverRegisterHttpHandler(server, key, externalFileHttpHandler, *value);
|
||||
} else {
|
||||
// Relative URL paths get registered for each of the services
|
||||
for (int i = 0; i < numServices; i++) {
|
||||
char *path;
|
||||
check(path = malloc(strlen(services[i]->path) + strlen(key) + 2));
|
||||
strcpy(path, services[i]->path);
|
||||
if (!*services[i]->path ||
|
||||
strrchr(services[i]->path, '\000')[-1] != '/') {
|
||||
strcat(path, "/");
|
||||
}
|
||||
strcat(path, key);
|
||||
serverRegisterHttpHandler(server, path, externalFileHttpHandler, *value);
|
||||
free(path);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
57
shellinabox/externalfile.h
Normal file
57
shellinabox/externalfile.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
// externalfile.h -- Serve static files through HTTP/HTTPS
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#ifndef EXTERNALFILE_H__
|
||||
#define EXTERNALFILE_H__
|
||||
|
||||
struct ExternalFileState {
|
||||
int fd;
|
||||
int totalSize;
|
||||
int partialSize;
|
||||
};
|
||||
|
||||
int registerExternalFiles(void *arg, const char *key, char **value);
|
||||
|
||||
#endif
|
BIN
shellinabox/favicon.ico
Normal file
BIN
shellinabox/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
986
shellinabox/launcher.c
Normal file
986
shellinabox/launcher.c
Normal file
|
@ -0,0 +1,986 @@
|
|||
// launcher.c -- Launch services from a privileged process
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <utmpx.h>
|
||||
|
||||
#if defined(HAVE_SECURITY_PAM_APPL_H) && defined(HAVE_SECURITY_PAM_MISC_H)
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/pam_misc.h>
|
||||
#else
|
||||
struct pam_message;
|
||||
struct pam_response;
|
||||
struct pam_conv;
|
||||
typedef struct pam_handle pam_handle_t;
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_PTHREAD_H)
|
||||
#include <pthread.h>
|
||||
extern int pthread_once(pthread_once_t *, void (*)(void))__attribute__((weak));
|
||||
#endif
|
||||
|
||||
#include "shellinabox/launcher.h"
|
||||
#include "shellinabox/privileges.h"
|
||||
#include "shellinabox/service.h"
|
||||
#include "libhttp/hashmap.h"
|
||||
#include "logging/logging.h"
|
||||
|
||||
// If PAM support is available, take advantage of it. Otherwise, silently fall
|
||||
// back on legacy operations for session management.
|
||||
static int (*x_pam_acct_mgmt)(pam_handle_t *, int);
|
||||
static int (*x_pam_authenticate)(pam_handle_t *, int);
|
||||
static int (*x_pam_close_session)(pam_handle_t *, int);
|
||||
static int (*x_pam_end)(pam_handle_t *, int);
|
||||
static int (*x_pam_get_item)(const pam_handle_t *, int, const void **);
|
||||
static int (*x_pam_open_session)(pam_handle_t *, int);
|
||||
static int (*x_pam_set_item)(pam_handle_t *, int, const void *);
|
||||
static int (*x_pam_start)(const char *, const char *, const struct pam_conv *,
|
||||
pam_handle_t **);
|
||||
static int (*x_misc_conv)(int, const struct pam_message **,
|
||||
struct pam_response **, void *);
|
||||
|
||||
// Older versions of glibc might not support fdopendir(). That's OK, we can
|
||||
// work around the lack of it, at a small performance loss.
|
||||
extern DIR *fdopendir(int) __attribute__((weak));
|
||||
|
||||
static int launcher = -1;
|
||||
|
||||
|
||||
static void *loadSymbol(const char *lib, const char *fn) {
|
||||
void *dl = RTLD_DEFAULT;
|
||||
void *rc = dlsym(dl, fn);
|
||||
if (!rc) {
|
||||
dl = dlopen(lib, RTLD_LAZY|RTLD_GLOBAL|RTLD_NOLOAD);
|
||||
if (dl == NULL) {
|
||||
dl = dlopen(lib, RTLD_LAZY|RTLD_GLOBAL);
|
||||
}
|
||||
if (dl != NULL) {
|
||||
rc = dlsym(dl, fn);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void loadPAM(void) {
|
||||
check(!x_pam_start);
|
||||
check(!x_misc_conv);
|
||||
struct {
|
||||
void **var;
|
||||
const char *lib;
|
||||
const char *fn;
|
||||
} symbols[] = {
|
||||
{ (void **)&x_pam_acct_mgmt, "libpam.so", "pam_acct_mgmt" },
|
||||
{ (void **)&x_pam_authenticate, "libpam.so", "pam_authenticate" },
|
||||
{ (void **)&x_pam_close_session, "libpam.so", "pam_close_session" },
|
||||
{ (void **)&x_pam_end, "libpam.so", "pam_end" },
|
||||
{ (void **)&x_pam_get_item, "libpam.so", "pam_get_item" },
|
||||
{ (void **)&x_pam_open_session, "libpam.so", "pam_open_session" },
|
||||
{ (void **)&x_pam_set_item, "libpam.so", "pam_set_item" },
|
||||
{ (void **)&x_pam_start, "libpam.so", "pam_start" },
|
||||
{ (void **)&x_misc_conv, "libpam_misc.so", "misc_conv" }
|
||||
};
|
||||
for (int i = 0; i < sizeof(symbols)/sizeof(symbols[0]); i++) {
|
||||
if (!(*symbols[i].var = loadSymbol(symbols[i].lib, symbols[i].fn))) {
|
||||
debug("Failed to load PAM support. Could not find \"%s\"",
|
||||
symbols[i].fn);
|
||||
for (int j = 0; j < sizeof(symbols)/sizeof(symbols[0]); j++) {
|
||||
*symbols[j].var = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
debug("Loaded PAM suppport");
|
||||
}
|
||||
|
||||
int supportsPAM(void) {
|
||||
#if defined(HAVE_SECURITY_PAM_APPL_H) && defined(HAVE_SECURITY_PAM_MISC_H)
|
||||
|
||||
// We want to call loadPAM() exactly once. For single-threaded applications,
|
||||
// this is straight-forward. For threaded applications, we need to call
|
||||
// pthread_once(), instead. We perform run-time checks for whether we are
|
||||
// single- or multi-threaded, so that the same code can be used.
|
||||
#if defined(HAVE_PTHREAD_H)
|
||||
if (!!&pthread_once) {
|
||||
static pthread_once_t once = PTHREAD_ONCE_INIT;
|
||||
pthread_once(&once, loadPAM);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
static int initialized;
|
||||
if (!initialized) {
|
||||
initialized = 1;
|
||||
loadPAM();
|
||||
}
|
||||
}
|
||||
return x_misc_conv && x_pam_start;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int launchChild(int service, struct Session *session) {
|
||||
struct LaunchRequest request = {
|
||||
.service = service,
|
||||
.width = session->width,
|
||||
.height = session->height };
|
||||
strncat(request.peerName, httpGetPeerName(session->http),
|
||||
sizeof(request.peerName));
|
||||
if (NOINTR(write(launcher, &request, sizeof(request))) != sizeof(request)) {
|
||||
return -1;
|
||||
}
|
||||
pid_t pid;
|
||||
char cmsg_buf[CMSG_SPACE(sizeof(int))];
|
||||
struct iovec iov = { 0 };
|
||||
struct msghdr msg = { 0 };
|
||||
iov.iov_base = &pid;
|
||||
iov.iov_len = sizeof(pid);
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = &cmsg_buf;
|
||||
msg.msg_controllen = sizeof(cmsg_buf);
|
||||
int bytes = NOINTR(recvmsg(launcher, &msg, 0));
|
||||
if (bytes < 0) {
|
||||
return -1;
|
||||
}
|
||||
check(bytes == sizeof(pid));
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
check(cmsg);
|
||||
check(cmsg->cmsg_level == SOL_SOCKET);
|
||||
check(cmsg->cmsg_type == SCM_RIGHTS);
|
||||
session->pty = *(int *)CMSG_DATA(cmsg);
|
||||
return pid;
|
||||
}
|
||||
|
||||
struct Utmp {
|
||||
const char pid[32];
|
||||
int pty;
|
||||
int useLogin;
|
||||
struct utmpx utmpx;
|
||||
};
|
||||
|
||||
static HashMap *childProcesses;
|
||||
|
||||
void initUtmp(struct Utmp *utmp, int useLogin, const char *ptyPath,
|
||||
const char *peerName) {
|
||||
memset(utmp, 0, sizeof(struct Utmp));
|
||||
utmp->pty = -1;
|
||||
utmp->useLogin = useLogin;
|
||||
utmp->utmpx.ut_type = useLogin ? LOGIN_PROCESS : USER_PROCESS;
|
||||
dcheck(!strncmp(ptyPath, "/dev/pts", 8));
|
||||
strncat(&utmp->utmpx.ut_line[0], ptyPath + 5, sizeof(utmp->utmpx.ut_line));
|
||||
strncat(&utmp->utmpx.ut_id[0], ptyPath + 8, sizeof(utmp->utmpx.ut_id));
|
||||
strncat(&utmp->utmpx.ut_user[0], "SHELLINABOX", sizeof(utmp->utmpx.ut_user));
|
||||
strncat(&utmp->utmpx.ut_host[0], peerName, sizeof(utmp->utmpx.ut_host));
|
||||
struct timeval tv;
|
||||
check(!gettimeofday(&tv, NULL));
|
||||
utmp->utmpx.ut_tv.tv_sec = tv.tv_sec;
|
||||
utmp->utmpx.ut_tv.tv_usec = tv.tv_usec;
|
||||
}
|
||||
|
||||
struct Utmp *newUtmp(int useLogin, const char *ptyPath,
|
||||
const char *peerName) {
|
||||
struct Utmp *utmp;
|
||||
check(utmp = malloc(sizeof(struct Utmp)));
|
||||
initUtmp(utmp, useLogin, ptyPath, peerName);
|
||||
return utmp;
|
||||
}
|
||||
|
||||
void destroyUtmp(struct Utmp *utmp) {
|
||||
if (utmp) {
|
||||
if (utmp->pty >= 0) {
|
||||
utmp->utmpx.ut_type = DEAD_PROCESS;
|
||||
memset(&utmp->utmpx.ut_user, 0, sizeof(utmp->utmpx.ut_user));
|
||||
memset(&utmp->utmpx.ut_host, 0, sizeof(utmp->utmpx.ut_host));
|
||||
struct timeval tv;
|
||||
check(!gettimeofday(&tv, NULL));
|
||||
utmp->utmpx.ut_tv.tv_sec = tv.tv_sec;
|
||||
utmp->utmpx.ut_tv.tv_usec = tv.tv_usec;
|
||||
|
||||
// Temporarily regain privileges to update the utmp database
|
||||
uid_t r_uid, e_uid, s_uid;
|
||||
uid_t r_gid, e_gid, s_gid;
|
||||
check(!getresuid(&r_uid, &e_uid, &s_uid));
|
||||
check(!getresgid(&r_gid, &e_gid, &s_gid));
|
||||
setresuid(0, 0, 0);
|
||||
setresgid(0, 0, 0);
|
||||
|
||||
setutxent();
|
||||
pututxline(&utmp->utmpx);
|
||||
endutxent();
|
||||
if (!utmp->useLogin) {
|
||||
updwtmpx("/var/log/wtmp", &utmp->utmpx);
|
||||
}
|
||||
|
||||
// Switch back to the lower privileges
|
||||
check(!setresgid(r_gid, e_gid, s_gid));
|
||||
check(!setresuid(r_uid, e_uid, s_uid));
|
||||
|
||||
NOINTR(close(utmp->pty));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void deleteUtmp(struct Utmp *utmp) {
|
||||
destroyUtmp(utmp);
|
||||
free(utmp);
|
||||
}
|
||||
|
||||
static void destroyUtmpHashEntry(void *arg, char *key, char *value) {
|
||||
deleteUtmp((struct Utmp *)value);
|
||||
}
|
||||
|
||||
static int forkPty(int *pty, int useLogin, struct Utmp **utmp,
|
||||
const char *peerName) {
|
||||
int slave;
|
||||
char ptyPath[PATH_MAX];
|
||||
if ((*pty = getpt()) < 0 ||
|
||||
grantpt(*pty) < 0 ||
|
||||
unlockpt(*pty) < 0 ||
|
||||
ptsname_r(*pty, ptyPath, sizeof(ptyPath)) < 0 ||
|
||||
(slave = NOINTR(open(ptyPath, O_RDWR|O_NOCTTY))) < 0) {
|
||||
if (*pty >= 0) {
|
||||
NOINTR(close(*pty));
|
||||
}
|
||||
*pty = -1;
|
||||
*utmp = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Fill in utmp entry
|
||||
*utmp = newUtmp(useLogin, ptyPath, peerName);
|
||||
|
||||
// Now, fork off the child process
|
||||
pid_t pid;
|
||||
if ((pid = fork()) < 0) {
|
||||
NOINTR(close(slave));
|
||||
NOINTR(close(*pty));
|
||||
*pty = -1;
|
||||
deleteUtmp(*utmp);
|
||||
*utmp = NULL;
|
||||
return -1;
|
||||
} else if (pid == 0) {
|
||||
pid = getpid();
|
||||
snprintf((char *)&(*utmp)->pid[0], sizeof((*utmp)->pid), "%d", pid);
|
||||
(*utmp)->utmpx.ut_pid = pid;
|
||||
(*utmp)->pty = slave;
|
||||
|
||||
// Close all file handles. If possible, scan through "/proc/self/fd" as
|
||||
// that is faster than calling close() on all possible file handles.
|
||||
int dirFd = !&fdopendir ? -1 : open("/proc/self/fd", O_RDONLY);
|
||||
if (dirFd < 0) {
|
||||
for (int i = sysconf(_SC_OPEN_MAX); --i > 0; ) {
|
||||
if (i != slave) {
|
||||
NOINTR(close(i));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DIR *dir;
|
||||
check(dir = fdopendir(dirFd));
|
||||
struct dirent de, *res;
|
||||
while (!readdir_r(dir, &de, &res) && res) {
|
||||
int fd = atoi(res->d_name);
|
||||
if (fd != slave && fd != dirFd) {
|
||||
NOINTR(close(fd));
|
||||
}
|
||||
}
|
||||
check(!closedir(dir));
|
||||
}
|
||||
|
||||
// Become the session/process-group leader
|
||||
setsid();
|
||||
setpgid(0, 0);
|
||||
|
||||
// Redirect standard I/O to the pty
|
||||
dup2(slave, 0);
|
||||
dup2(slave, 1);
|
||||
dup2(slave, 2);
|
||||
if (slave > 2) {
|
||||
NOINTR(close(slave));
|
||||
}
|
||||
*pty = 0;
|
||||
|
||||
// Force the pty to be our control terminal
|
||||
NOINTR(close(NOINTR(open(ptyPath, O_RDWR))));
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
snprintf((char *)&(*utmp)->pid[0], sizeof((*utmp)->pid), "%d", pid);
|
||||
(*utmp)->utmpx.ut_pid = pid;
|
||||
(*utmp)->pty = *pty;
|
||||
fcntl(*pty, F_SETFL, O_NONBLOCK|O_RDWR);
|
||||
NOINTR(close(slave));
|
||||
return pid;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct passwd *getPWEnt(uid_t uid) {
|
||||
struct passwd pwbuf, *pw;
|
||||
char *buf;
|
||||
int len = sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
check(len > 0);
|
||||
check(buf = malloc(len));
|
||||
check(!getpwuid_r(uid, &pwbuf, buf, len, &pw) && pw);
|
||||
struct passwd *passwd;
|
||||
check(passwd = malloc(sizeof(struct passwd) +
|
||||
strlen(pw->pw_name) +
|
||||
strlen(pw->pw_passwd) +
|
||||
strlen(pw->pw_gecos) +
|
||||
strlen(pw->pw_dir) +
|
||||
strlen(pw->pw_shell) + 5));
|
||||
passwd->pw_uid = pw->pw_uid;
|
||||
passwd->pw_gid = pw->pw_gid;
|
||||
strcpy(passwd->pw_shell = strrchr(
|
||||
strcpy(passwd->pw_dir = strrchr(
|
||||
strcpy(passwd->pw_gecos = strrchr(
|
||||
strcpy(passwd->pw_passwd = strrchr(
|
||||
strcpy(passwd->pw_name = (char *)(passwd + 1),
|
||||
pw->pw_name), '\000') + 1,
|
||||
pw->pw_passwd), '\000') + 1,
|
||||
pw->pw_gecos), '\000') + 1,
|
||||
pw->pw_dir), '\000') + 1,
|
||||
pw->pw_shell);
|
||||
free(buf);
|
||||
return passwd;
|
||||
}
|
||||
|
||||
static void sigAlrmHandler(int sig, siginfo_t *info, void *unused) {
|
||||
puts("\nLogin timed out after 60 seconds.");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
static pam_handle_t *internalLogin(struct Service *service, struct Utmp *utmp,
|
||||
char ***environment) {
|
||||
// Time out after 60 seconds
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sa.sa_sigaction = sigAlrmHandler;
|
||||
check(!sigaction(SIGALRM, &sa, NULL));
|
||||
alarm(60);
|
||||
|
||||
// Use PAM to negotiate user authentication and authorization
|
||||
const struct passwd *pw;
|
||||
pam_handle_t *pam = NULL;
|
||||
struct pam_conv conv = { .conv = x_misc_conv };
|
||||
if (service->authUser) {
|
||||
check(supportsPAM());
|
||||
check(x_pam_start("shellinabox", NULL, &conv, &pam) == PAM_SUCCESS);
|
||||
|
||||
// Change the prompt to include the host name
|
||||
struct utsname uts;
|
||||
check(!uname(&uts));
|
||||
const char *origPrompt;
|
||||
check(x_pam_get_item(pam, PAM_USER_PROMPT, (void *)&origPrompt) ==
|
||||
PAM_SUCCESS);
|
||||
char *prompt;
|
||||
check(prompt = stringPrintf(NULL, "%s %s", uts.nodename,
|
||||
origPrompt ? origPrompt : "login: "));
|
||||
check(x_pam_set_item(pam, PAM_USER_PROMPT, prompt) == PAM_SUCCESS);
|
||||
|
||||
// Up to three attempts to enter the user id and password
|
||||
for (int i = 0;;) {
|
||||
check(x_pam_set_item(pam, PAM_USER, NULL) == PAM_SUCCESS);
|
||||
int rc;
|
||||
if ((rc = x_pam_authenticate(pam, PAM_SILENT)) ==
|
||||
PAM_SUCCESS &&
|
||||
(geteuid() ||
|
||||
(rc = x_pam_acct_mgmt(pam, PAM_SILENT)) ==
|
||||
PAM_SUCCESS)) {
|
||||
break;
|
||||
}
|
||||
if (++i == 3) {
|
||||
// Quit if login failed.
|
||||
puts("\nMaximum number of tries exceeded (3)");
|
||||
x_pam_end(pam, rc);
|
||||
_exit(1);
|
||||
} else {
|
||||
puts("\nLogin incorrect");
|
||||
}
|
||||
}
|
||||
check(x_pam_set_item(pam, PAM_USER_PROMPT, "login: ") == PAM_SUCCESS);
|
||||
free(prompt);
|
||||
|
||||
// Retrieve user id, and group id.
|
||||
const char *name;
|
||||
check(x_pam_get_item(pam, PAM_USER, (void *)&name) == PAM_SUCCESS);
|
||||
pw = getPWEnt(getUserId(name));
|
||||
check(service->uid < 0);
|
||||
check(service->gid < 0);
|
||||
check(!service->user);
|
||||
check(!service->group);
|
||||
service->uid = pw->pw_uid;
|
||||
service->gid = pw->pw_gid;
|
||||
check(service->user = strdup(pw->pw_name));
|
||||
service->group = getGroupName(pw->pw_gid);
|
||||
} else {
|
||||
check(service->uid >= 0);
|
||||
check(service->gid >= 0);
|
||||
check(service->user);
|
||||
check(service->group);
|
||||
if (supportsPAM()) {
|
||||
check(x_pam_start("shellinabox", service->user, &conv, &pam) ==
|
||||
PAM_SUCCESS);
|
||||
int rc;
|
||||
|
||||
// PAM account management requires root access. Just skip it, if we
|
||||
// are running with lower privileges.
|
||||
if (geteuid() &&
|
||||
(rc = x_pam_acct_mgmt(pam, PAM_SILENT)) !=
|
||||
PAM_SUCCESS) {
|
||||
x_pam_end(pam, rc);
|
||||
_exit(1);
|
||||
}
|
||||
}
|
||||
pw = getPWEnt(service->uid);
|
||||
}
|
||||
|
||||
if (restricted &&
|
||||
(service->uid != restricted || service->gid != pw->pw_gid)) {
|
||||
puts("\nAccess denied!");
|
||||
x_pam_end(pam, PAM_SUCCESS);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
if (pam) {
|
||||
check(x_pam_set_item(pam, PAM_TTY, (const void **)utmp->utmpx.ut_line) ==
|
||||
PAM_SUCCESS);
|
||||
}
|
||||
|
||||
// Retrieve supplementary group ids.
|
||||
int ngroups = 0;
|
||||
getgrouplist(service->user, pw->pw_gid, NULL, &ngroups);
|
||||
check(ngroups >= 0);
|
||||
if (ngroups > 0) {
|
||||
// Set supplementary group ids
|
||||
gid_t *groups;
|
||||
check(groups = malloc((ngroups + 1) * sizeof(gid_t)));
|
||||
groups[ngroups] = service->gid;
|
||||
check(getgrouplist(service->user, pw->pw_gid, groups, &ngroups) ==
|
||||
ngroups);
|
||||
|
||||
// Make sure that any group that was requested on the command line is
|
||||
// included, if it is not one of the normal groups for this user.
|
||||
for (int i = 0; ; i++) {
|
||||
if (i == ngroups) {
|
||||
ngroups++;
|
||||
break;
|
||||
} else if (groups[i] == service->gid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
setgroups(ngroups, groups);
|
||||
free(groups);
|
||||
}
|
||||
|
||||
// Add standard environment variables
|
||||
int numEnvVars = 0;
|
||||
for (char **e = *environment; *e; numEnvVars++, e++) {
|
||||
}
|
||||
check(*environment = realloc(*environment,
|
||||
(numEnvVars + 6)*sizeof(char *)));
|
||||
(*environment)[numEnvVars++] = stringPrintf(NULL, "HOME=%s", pw->pw_dir);
|
||||
(*environment)[numEnvVars++] = stringPrintf(NULL, "SHELL=%s", pw->pw_shell);
|
||||
check(
|
||||
(*environment)[numEnvVars++] = strdup(
|
||||
"PATH=/usr/local/bin:/usr/bin:/bin:/usr/games"));
|
||||
(*environment)[numEnvVars++] = stringPrintf(NULL, "LOGNAME=%s",
|
||||
service->user);
|
||||
(*environment)[numEnvVars++] = stringPrintf(NULL, "USER=%s", service->user);
|
||||
(*environment)[numEnvVars++] = NULL;
|
||||
free((void *)pw);
|
||||
|
||||
// Update utmp/wtmp entries
|
||||
memset(&utmp->utmpx.ut_user, 0, sizeof(utmp->utmpx.ut_user));
|
||||
strncat(&utmp->utmpx.ut_user[0], service->user, sizeof(utmp->utmpx.ut_user));
|
||||
setutxent();
|
||||
pututxline(&utmp->utmpx);
|
||||
endutxent();
|
||||
updwtmpx("/var/log/wtmp", &utmp->utmpx);
|
||||
|
||||
alarm(0);
|
||||
return pam;
|
||||
}
|
||||
|
||||
static void destroyVariableHashEntry(void *arg, char *key, char *value) {
|
||||
free(key);
|
||||
free(value);
|
||||
}
|
||||
|
||||
static void execService(int width, int height, struct Service *service,
|
||||
const char *peerName, char **environment) {
|
||||
// Create a hash table with all the variables that we can expand. This
|
||||
// includes all environment variables being passed to the child.
|
||||
HashMap *vars;
|
||||
check(vars = newHashMap(destroyVariableHashEntry, NULL));
|
||||
for (char **e = environment; *e; e++) {
|
||||
char *ptr = strchr(*e, '=');
|
||||
char *key, *value;
|
||||
if (!ptr) {
|
||||
check(key = strdup(*e));
|
||||
check(value = strdup(""));
|
||||
} else {
|
||||
check(key = malloc(ptr - *e + 1));
|
||||
memcpy(key, *e, ptr - *e);
|
||||
key[ptr - *e] = '\000';
|
||||
check(value = strdup(ptr + 1));
|
||||
}
|
||||
// All of our variables are lower-case
|
||||
for (ptr = key; *ptr; ptr++) {
|
||||
if (*ptr >= 'A' && *ptr <= 'Z') {
|
||||
*ptr += 'a' - 'A';
|
||||
}
|
||||
}
|
||||
addToHashMap(vars, key, value);
|
||||
}
|
||||
char *key, *value;
|
||||
check(key = strdup("gid"));
|
||||
addToHashMap(vars, key, stringPrintf(NULL, "%d", service->gid));
|
||||
check(key = strdup("group"));
|
||||
check(value = strdup(service->group));
|
||||
addToHashMap(vars, key, value);
|
||||
check(key = strdup("peer"));
|
||||
check(value = strdup(peerName));
|
||||
addToHashMap(vars, key, value);
|
||||
check(key = strdup("uid"));
|
||||
addToHashMap(vars, key, stringPrintf(NULL, "%d", service->uid));
|
||||
|
||||
enum { ENV, ARGS } state = ENV;
|
||||
enum { NONE, SINGLE, DOUBLE
|
||||
} quote = NONE;
|
||||
char *cmdline;
|
||||
check(cmdline = strdup(service->cmdline));
|
||||
int argc = 0;
|
||||
char **argv;
|
||||
check(argv = malloc(sizeof(char *)));
|
||||
key = NULL;
|
||||
value = NULL;
|
||||
for (char *ptr = cmdline; ; ptr++) {
|
||||
if (!key && *ptr && *ptr != ' ') {
|
||||
key = ptr;
|
||||
}
|
||||
switch (*ptr) {
|
||||
case '\'':
|
||||
if (quote == SINGLE || quote == NONE) {
|
||||
memmove(ptr, ptr + 1, strlen(ptr));
|
||||
ptr--;
|
||||
quote = quote == SINGLE ? NONE : SINGLE;
|
||||
} else {
|
||||
dcheck(quote == DOUBLE);
|
||||
}
|
||||
break;
|
||||
case '\"':
|
||||
if (quote == DOUBLE || quote == NONE) {
|
||||
memmove(ptr, ptr + 1, strlen(ptr));
|
||||
ptr--;
|
||||
quote = quote == DOUBLE ? NONE : DOUBLE;
|
||||
} else {
|
||||
dcheck(quote == SINGLE);
|
||||
}
|
||||
break;
|
||||
case '$':
|
||||
if ((quote == NONE || quote == DOUBLE) && ptr[1] == '{') {
|
||||
// Always treat environment variables as if they were quoted. There
|
||||
// is not good reason for us to try to look for spaces within
|
||||
// expanded environment variables. This just leads to subtle bugs.
|
||||
char *end = ptr + 2;
|
||||
while (*end && *end != '}') {
|
||||
end++;
|
||||
}
|
||||
char ch = *end;
|
||||
*end = '\000';
|
||||
const char *repl = getFromHashMap(vars, ptr);
|
||||
int replLen = repl ? strlen(repl) : 0;
|
||||
*end = ch;
|
||||
if (ch) {
|
||||
end++;
|
||||
}
|
||||
memmove(ptr + replLen, end, strlen(end) + 1);
|
||||
if (repl) {
|
||||
memcpy(ptr, repl, replLen);
|
||||
}
|
||||
ptr += replLen;
|
||||
}
|
||||
break;
|
||||
case '\\':
|
||||
if (!ptr[1]) {
|
||||
*ptr-- = '\000';
|
||||
} else {
|
||||
memmove(ptr, ptr + 1, strlen(ptr));
|
||||
}
|
||||
break;
|
||||
case '=':
|
||||
// This is the seperator between keys and values of any environment
|
||||
// variable that we are asked to set.
|
||||
if (state == ENV && quote == NONE && !value) {
|
||||
*ptr = '\000';
|
||||
value = ptr + 1;
|
||||
}
|
||||
break;
|
||||
case ' ':
|
||||
// If this space character is not quoted, this is the start of a new
|
||||
// command line argument.
|
||||
if (quote != NONE) {
|
||||
break;
|
||||
}
|
||||
// Fall thru
|
||||
case '\000':;
|
||||
char ch = *ptr;
|
||||
if (key) {
|
||||
*ptr = '\000';
|
||||
if (state == ENV && value) {
|
||||
// Override an existing environment variable.
|
||||
int numEnvVars = 0;
|
||||
int len = strlen(key);
|
||||
for (char **e = environment; *e; e++, numEnvVars++) {
|
||||
if (!strncmp(*e, key, len) && (*e)[len] == '=') {
|
||||
check(*e = realloc(*e, len + strlen(value) + 2));
|
||||
strcpy((*e) + len + 1, value);
|
||||
numEnvVars = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Add a new environment variable
|
||||
if (numEnvVars >= 0) {
|
||||
check(environment = realloc(environment,
|
||||
(numEnvVars + 2)*sizeof(char *)));
|
||||
value[-1] = '=';
|
||||
environment[numEnvVars++] = strdup(key);
|
||||
environment[numEnvVars] = NULL;
|
||||
}
|
||||
} else {
|
||||
// Add entry to argv.
|
||||
state = ARGS;
|
||||
argv[argc++] = key;
|
||||
check(argv = realloc(argv, (argc + 1)*sizeof(char *)));
|
||||
}
|
||||
}
|
||||
key = NULL;
|
||||
value = NULL;
|
||||
if (!ch) {
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
done:
|
||||
argv[argc] = NULL;
|
||||
deleteHashMap(vars);
|
||||
check(argc);
|
||||
|
||||
extern char **environ;
|
||||
environ = environment;
|
||||
char *cmd = strrchr(argv[0], '/');
|
||||
execvp(cmd ? cmd + 1: argv[0], argv);
|
||||
}
|
||||
|
||||
void setWindowSize(int pty, int width, int height) {
|
||||
if (width > 0 && height > 0) {
|
||||
struct winsize win;
|
||||
win.ws_row = height;
|
||||
win.ws_col = width;
|
||||
win.ws_xpixel = 0;
|
||||
win.ws_ypixel = 0;
|
||||
ioctl(pty, TIOCSWINSZ, &win);
|
||||
}
|
||||
}
|
||||
|
||||
static void childProcess(struct Service *service, int width, int height,
|
||||
struct Utmp *utmp, const char *peerName) {
|
||||
// Set initial window size
|
||||
setWindowSize(0, width, height);
|
||||
|
||||
// Set up environment variables
|
||||
static const char *legalEnv[] = { "TZ", "HZ", NULL };
|
||||
char **environment;
|
||||
check(environment = malloc(2*sizeof(char *)));
|
||||
int numEnvVars = 1;
|
||||
environment[0] = "TERM=xterm";
|
||||
if (width > 0 && height > 0) {
|
||||
numEnvVars += 2;
|
||||
check(environment = realloc(environment,
|
||||
(numEnvVars + 1)*sizeof(char *)));
|
||||
environment[numEnvVars-2] = stringPrintf(NULL, "COLUMNS=%d", width);
|
||||
environment[numEnvVars-1] = stringPrintf(NULL, "LINES=%d", height);
|
||||
}
|
||||
for (int i = 0; legalEnv[i]; i++) {
|
||||
char *value = getenv(legalEnv[i]);
|
||||
if (value) {
|
||||
numEnvVars++;
|
||||
check(environment = realloc(environment,
|
||||
(numEnvVars + 1)*sizeof(char *)));
|
||||
environment[numEnvVars-1] = stringPrintf(NULL, "%s=%s",
|
||||
legalEnv[i], value);
|
||||
}
|
||||
}
|
||||
environment[numEnvVars] = NULL;
|
||||
|
||||
// Set initial terminal settings
|
||||
struct termios tt;
|
||||
tcgetattr(0, &tt);
|
||||
cfsetispeed(&tt, 38400);
|
||||
cfsetospeed(&tt, 38400);
|
||||
tt.c_iflag = TTYDEF_IFLAG & ~ISTRIP;
|
||||
tt.c_oflag = TTYDEF_OFLAG;
|
||||
tt.c_lflag = TTYDEF_LFLAG;
|
||||
tt.c_cflag = (TTYDEF_CFLAG & ~(CS7|PARENB|HUPCL)) | CS8;
|
||||
tt.c_cc[VERASE] = '\x7F';
|
||||
tcsetattr(0, TCSAFLUSH, &tt);
|
||||
|
||||
// Assert root privileges in order to update utmp entry.
|
||||
setresuid(0, 0, 0);
|
||||
setresgid(0, 0, 0);
|
||||
setutxent();
|
||||
struct utmpx utmpx = utmp->utmpx;
|
||||
if (service->useLogin || service->authUser) {
|
||||
utmpx.ut_type = LOGIN_PROCESS;
|
||||
memset(utmpx.ut_host, 0, sizeof(utmpx.ut_host));
|
||||
}
|
||||
pututxline(&utmpx);
|
||||
endutxent();
|
||||
if (!utmp->useLogin) {
|
||||
memset(&utmpx.ut_user, 0, sizeof(utmpx.ut_user));
|
||||
strncat(&utmpx.ut_user[0], "LOGIN", sizeof(utmpx.ut_user));
|
||||
updwtmpx("/var/log/wtmp", &utmpx);
|
||||
}
|
||||
|
||||
// Create session. We might have to fork another process as PAM wants us
|
||||
// to close the session when the child terminates. And we must retain
|
||||
// permissions, as session closure could require root permissions.
|
||||
// None of this really applies if we are running as an unprivileged user.
|
||||
// In that case, we do not bother about session management.
|
||||
if (!service->useLogin) {
|
||||
pam_handle_t *pam = internalLogin(service, utmp, &environment);
|
||||
if (pam && !geteuid()) {
|
||||
check(x_pam_open_session(pam, PAM_SILENT) == PAM_SUCCESS);
|
||||
pid_t pid = fork();
|
||||
switch (pid) {
|
||||
case -1:
|
||||
_exit(1);
|
||||
case 0:
|
||||
break;
|
||||
default:;
|
||||
// Finish all pending PAM operations.
|
||||
int status, rc;
|
||||
check(waitpid(pid, &status, 0) == pid);
|
||||
check((rc = x_pam_close_session(pam, PAM_SILENT)) ==
|
||||
PAM_SUCCESS);
|
||||
check(x_pam_end(pam, rc) == PAM_SUCCESS);
|
||||
_exit(WIFEXITED(status) ? WEXITSTATUS(status) : -WTERMSIG(status));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Change user and group ids
|
||||
check(!setresgid(service->gid, service->gid, service->gid));
|
||||
check(!setresuid(service->uid, service->uid, service->uid));
|
||||
|
||||
// Change working directory
|
||||
if (service->useHomeDir) {
|
||||
check(!service->useLogin);
|
||||
const struct passwd *pw = getPWEnt(getuid());
|
||||
check(!service->cwd);
|
||||
check(service->cwd = strdup(pw->pw_dir));
|
||||
free((void *)pw);
|
||||
}
|
||||
check(service->cwd);
|
||||
if (!*service->cwd || *service->cwd != '/' || chdir(service->cwd)) {
|
||||
check(service->cwd = realloc((char *)service->cwd, 2));
|
||||
strcpy((char *)service->cwd, "/");
|
||||
puts("No directory, logging in with HOME=/");
|
||||
check(!chdir("/"));
|
||||
for (int i = 0; environment[i]; i++) {
|
||||
if (!strncmp(environment[i], "HOME=", 5)) {
|
||||
free(environment[i]);
|
||||
check(environment[i] = strdup("HOME=/"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, launch the child process.
|
||||
if (service->useLogin) {
|
||||
execle("/bin/login", "login", "-p", "-h", peerName, NULL, environment);
|
||||
} else {
|
||||
execService(width, height, service, peerName, environment);
|
||||
}
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
static void sigChildHandler(int sig, siginfo_t *info, void *unused) {
|
||||
}
|
||||
|
||||
static void launcherDaemon(int fd) {
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
|
||||
sa.sa_sigaction = sigChildHandler;
|
||||
check(!sigaction(SIGCHLD, &sa, NULL));
|
||||
|
||||
struct LaunchRequest request;
|
||||
for (;;) {
|
||||
errno = 0;
|
||||
int len = read(fd, &request, sizeof(request));
|
||||
if (len != sizeof(request) && errno != EINTR) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Check whether our read operation got interrupted, because a child
|
||||
// has died.
|
||||
int status;
|
||||
pid_t pid;
|
||||
while (NOINTR(pid = waitpid(-1, &status, WNOHANG)) > 0) {
|
||||
if (WIFEXITED(pid) || WIFSIGNALED(pid)) {
|
||||
char key[32];
|
||||
snprintf(&key[0], sizeof(key), "%d", pid);
|
||||
deleteFromHashMap(childProcesses, key);
|
||||
}
|
||||
}
|
||||
if (len != sizeof(request)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
check(request.service >= 0);
|
||||
check(request.service < numServices);
|
||||
|
||||
// Sanitize the host name, so that we do not pass any unexpected characters
|
||||
// to our child process.
|
||||
request.peerName[sizeof(request.peerName)-1] = '\000';
|
||||
for (char *s = request.peerName; *s; s++) {
|
||||
if (!((*s >= '0' && *s <= '9') ||
|
||||
(*s >= 'A' && *s <= 'Z') ||
|
||||
(*s >= 'a' && *s <= 'z') ||
|
||||
*s == '.' || *s == '-')) {
|
||||
*s = '-';
|
||||
}
|
||||
}
|
||||
|
||||
// Fork and exec the child process.
|
||||
int pty;
|
||||
struct Utmp *utmp;
|
||||
if ((pid = forkPty(&pty,
|
||||
services[request.service]->useLogin,
|
||||
&utmp, request.peerName)) < 0) {
|
||||
} else if (pid == 0) {
|
||||
childProcess(services[request.service], request.width, request.height,
|
||||
utmp, request.peerName);
|
||||
_exit(1);
|
||||
} else {
|
||||
// Remember the utmp entry so that we can clean up when the child
|
||||
// terminates.
|
||||
if (!childProcesses) {
|
||||
childProcesses = newHashMap(destroyUtmpHashEntry, NULL);
|
||||
}
|
||||
addToHashMap(childProcesses, utmp->pid, (char *)utmp);
|
||||
|
||||
// Send file handle and process id back to parent
|
||||
char cmsg_buf[CMSG_SPACE(sizeof(int))];
|
||||
struct iovec iov = { 0 };
|
||||
struct msghdr msg = { 0 };
|
||||
iov.iov_base = &pid;
|
||||
iov.iov_len = sizeof(pid);
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = &cmsg_buf;
|
||||
msg.msg_controllen = sizeof(cmsg_buf);
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
check(cmsg);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
*(int *)CMSG_DATA(cmsg) = pty;
|
||||
if (NOINTR(sendmsg(fd, &msg, 0)) != sizeof(pid)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
deleteHashMap(childProcesses);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
void forkLauncher(void) {
|
||||
int pair[2];
|
||||
check(!socketpair(AF_UNIX, SOCK_STREAM, 0, pair));
|
||||
|
||||
switch (fork()) {
|
||||
case 0:;
|
||||
// If our real-uid is not "root", then we should not allow anybody to
|
||||
// login unauthenticated users as anyone other than their own.
|
||||
uid_t tmp;
|
||||
check(!getresuid(&restricted, &tmp, &tmp));
|
||||
|
||||
// Temporarily drop most permissions. We still retain the ability to
|
||||
// switch back to root, which is necessary for launching "login".
|
||||
lowerPrivileges();
|
||||
NOINTR(close(pair[0]));
|
||||
launcherDaemon(pair[1]);
|
||||
fatal("exit() failed!");
|
||||
case -1:
|
||||
fatal("fork() failed!");
|
||||
break;
|
||||
default:
|
||||
NOINTR(close(pair[1]));
|
||||
launcher = pair[0];
|
||||
return;
|
||||
}
|
||||
}
|
65
shellinabox/launcher.h
Normal file
65
shellinabox/launcher.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
// launcher.h -- Launch services from a privileged process
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#ifndef LAUNCHER_H__
|
||||
#define LAUNCHER_H__
|
||||
|
||||
#include "shellinabox/launcher.h"
|
||||
#include "shellinabox/session.h"
|
||||
#include "logging/logging.h"
|
||||
|
||||
|
||||
struct LaunchRequest {
|
||||
int service;
|
||||
int width, height;
|
||||
char peerName[128];
|
||||
};
|
||||
|
||||
int supportsPAM(void);
|
||||
int launchChild(int service, struct Session *session);
|
||||
void setWindowSize(int pty, int width, int height);
|
||||
void forkLauncher(void);
|
||||
|
||||
#endif
|
238
shellinabox/privileges.c
Normal file
238
shellinabox/privileges.c
Normal file
|
@ -0,0 +1,238 @@
|
|||
// privileges.c -- Manage process privileges
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <limits.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "shellinabox/privileges.h"
|
||||
#include "logging/logging.h"
|
||||
|
||||
int runAsUser = -1;
|
||||
int runAsGroup = -1;
|
||||
uid_t restricted;
|
||||
|
||||
|
||||
void removeGroupPrivileges(void) {
|
||||
// Remove all supplementary groups. Allow this command to fail. That could
|
||||
// happen if we run as an unprivileged user.
|
||||
setgroups(0, (gid_t *)"");
|
||||
if (runAsGroup >= 0) {
|
||||
// Try to switch the user-provided group.
|
||||
if (setresgid(runAsGroup, runAsGroup, runAsGroup)) {
|
||||
if (restricted) {
|
||||
_exit(1);
|
||||
} else {
|
||||
fatal("Only privileged users can change their group memberships");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gid_t r, e, s;
|
||||
check(!getresgid(&r, &e, &s));
|
||||
if (r) {
|
||||
// If we were started as a set-gid binary, drop these permissions, now.
|
||||
check(!setresgid(r, r, r));
|
||||
} else {
|
||||
// If we are running as root, switch to "nogroup"
|
||||
gid_t n = getGroupId("nogroup");
|
||||
check(!setresgid(n, n, n));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lowerPrivileges(void) {
|
||||
// Permanently lower all group permissions. We do not actually need these,
|
||||
// as we still have "root" user privileges in our saved-uid.
|
||||
removeGroupPrivileges();
|
||||
|
||||
// Temporarily lower user privileges. If we used to have "root" privileges,
|
||||
// we can later still regain them.
|
||||
setresuid(-1, -1, 0);
|
||||
|
||||
if (runAsUser >= 0) {
|
||||
// Try to switch to the user-provided user id.
|
||||
check(!setresuid(runAsUser, runAsUser, -1));
|
||||
} else {
|
||||
uid_t r, e, s;
|
||||
check(!getresuid(&r, &e, &s));
|
||||
if (r) {
|
||||
// If we were started as a set-uid binary, temporarily lower these
|
||||
// permissions.
|
||||
check(!setresuid(r, r, -1));
|
||||
} else {
|
||||
// If we are running as "root", temporarily switch to "nobody".
|
||||
uid_t n = getUserId("nobody");
|
||||
check(!setresuid(n, n, -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dropPrivileges(void) {
|
||||
// Drop all group privileges.
|
||||
removeGroupPrivileges();
|
||||
|
||||
if (runAsUser >= 0) {
|
||||
// Try to switch to the user-provided user id.
|
||||
if (setresuid(runAsUser, runAsUser, runAsUser)) {
|
||||
fatal("Only privileged users can change their user id.");
|
||||
}
|
||||
} else {
|
||||
uid_t r, e, s;
|
||||
check(!getresuid(&r, &e, &s));
|
||||
if (r) {
|
||||
// If we were started as a set-uid binary, permanently drop these
|
||||
// permissions.
|
||||
check(!setresuid(r, r, r));
|
||||
} else {
|
||||
// If we are running as "root", permanently switch to "nobody".
|
||||
uid_t n = getUserId("nobody");
|
||||
check(!setresuid(n, n, n));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *getUserName(uid_t uid) {
|
||||
struct passwd pwbuf, *pw;
|
||||
char *buf;
|
||||
int len = sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
check(len > 0);
|
||||
check(buf = malloc(len));
|
||||
char *user;
|
||||
if (getpwuid_r(uid, &pwbuf, buf, len, &pw) || !pw) {
|
||||
check(user = malloc(32));
|
||||
snprintf(user, 32, "%d", uid);
|
||||
} else {
|
||||
check(user = strdup(pw->pw_name));
|
||||
}
|
||||
free(buf);
|
||||
return user;
|
||||
}
|
||||
|
||||
uid_t getUserId(const char *name) {
|
||||
struct passwd pwbuf, *pw;
|
||||
char *buf;
|
||||
int len = sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
check(len > 0);
|
||||
check(buf = malloc(len));
|
||||
if (getpwnam_r(name, &pwbuf, buf, len, &pw) || !pw) {
|
||||
fatal("Cannot look up user id \"%s\"", name);
|
||||
}
|
||||
uid_t uid = pw->pw_uid;
|
||||
free(buf);
|
||||
return uid;
|
||||
}
|
||||
|
||||
uid_t parseUser(const char *arg, const char **name) {
|
||||
char *end;
|
||||
errno = 0;
|
||||
unsigned long l = strtoul(arg, &end, 10);
|
||||
if (errno || l > INT_MAX || *end) {
|
||||
if (name) {
|
||||
check(*name = strdup(arg));
|
||||
}
|
||||
return getUserId(arg);
|
||||
} else {
|
||||
if (name) {
|
||||
*name = getUserName((uid_t)l);
|
||||
}
|
||||
return (uid_t)l;
|
||||
}
|
||||
}
|
||||
|
||||
const char *getGroupName(gid_t gid) {
|
||||
struct group grbuf, *gr;
|
||||
char *buf;
|
||||
int len = sysconf(_SC_GETGR_R_SIZE_MAX);
|
||||
check(len > 0);
|
||||
check(buf = malloc(len));
|
||||
char *group;
|
||||
if (getgrgid_r(gid, &grbuf, buf, len, &gr) || !gr) {
|
||||
check(group = malloc(32));
|
||||
snprintf(group, 32, "%d", gid);
|
||||
} else {
|
||||
check(group = strdup(gr->gr_name));
|
||||
}
|
||||
free(buf);
|
||||
return group;
|
||||
}
|
||||
|
||||
gid_t getGroupId(const char *name) {
|
||||
struct group grbuf, *gr;
|
||||
char *buf;
|
||||
int len = sysconf(_SC_GETGR_R_SIZE_MAX);
|
||||
check(len > 0);
|
||||
check(buf = malloc(len));
|
||||
if (getgrnam_r(name, &grbuf, buf, len, &gr) || !gr) {
|
||||
fatal("Cannot look up group \"%s\"", name);
|
||||
}
|
||||
gid_t gid = gr->gr_gid;
|
||||
free(buf);
|
||||
return gid;
|
||||
}
|
||||
|
||||
gid_t parseGroup(const char *arg, const char **name) {
|
||||
char *end;
|
||||
errno = 0;
|
||||
unsigned long l = strtoul(arg, &end, 10);
|
||||
if (errno || l > INT_MAX || *end) {
|
||||
if (name) {
|
||||
check(*name = strdup(arg));
|
||||
}
|
||||
return getGroupId(arg);
|
||||
} else {
|
||||
if (name) {
|
||||
*name = getGroupName((uid_t)l);
|
||||
}
|
||||
return (uid_t)l;
|
||||
}
|
||||
}
|
65
shellinabox/privileges.h
Normal file
65
shellinabox/privileges.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
// privileges.h -- Manage process privileges
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#ifndef PRIVILEGES_H__
|
||||
#define PRIVILEGES_H__
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
extern int runAsUser;
|
||||
extern int runAsGroup;
|
||||
extern uid_t restricted;
|
||||
|
||||
void removeGroupPrivileges(void);
|
||||
void lowerPrivileges(void);
|
||||
void dropPrivileges(void);
|
||||
const char *getUserName(uid_t uid);
|
||||
uid_t getUserId(const char *name);
|
||||
uid_t parseUser(const char *arg, const char **name);
|
||||
const char *getGroupName(gid_t gid);
|
||||
gid_t getGroupId(const char *name);
|
||||
gid_t parseGroup(const char *arg, const char **name);
|
||||
|
||||
#endif
|
73
shellinabox/root_page.html
Normal file
73
shellinabox/root_page.html
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<!--
|
||||
ShellInABox - Make command line applications available as AJAX web applications
|
||||
Copyright (C) 2008 Markus Gutschke markus@shellinabox.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
In addition to these license terms, the author grants the following
|
||||
additional rights:
|
||||
|
||||
If you modify this program, or any covered work, by linking or
|
||||
combining it with the OpenSSL project's OpenSSL library (or a
|
||||
modified version of that library), containing parts covered by the
|
||||
terms of the OpenSSL or SSLeay licenses, the author
|
||||
grants you additional permission to convey the resulting work.
|
||||
Corresponding Source for a non-source form of such a combination
|
||||
shall include the source code for the parts of OpenSSL used as well
|
||||
as that of the covered work.
|
||||
|
||||
You may at your option choose to remove this additional permission from
|
||||
the work, or from any part of it.
|
||||
|
||||
It is possible to build this program in a way that it loads OpenSSL
|
||||
libraries at run-time. If doing so, the following notices are required
|
||||
by the OpenSSL and SSLeay licenses:
|
||||
|
||||
This product includes software developed by the OpenSSL Project
|
||||
for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
|
||||
This product includes cryptographic software written by Eric Young
|
||||
(eay@cryptsoft.com)
|
||||
|
||||
The most up-to-date version of this program is always available from
|
||||
http://shellinabox.com
|
||||
-->
|
||||
<title>Shell In A Box</title>
|
||||
<link rel="stylesheet" href="styles.css" type="text/css">
|
||||
<script type="text/javascript"><!--
|
||||
// We would like to hide overflowing lines as this can lead to
|
||||
// visually jarring results if the browser substitutes oversized
|
||||
// Unicode characters from different fonts. Unfortunately, a bug
|
||||
// in Firefox prevents it from allowing multi-line text
|
||||
// selections whenever we change the "overflow" style. So, only
|
||||
// do so for non-Netscape browsers.
|
||||
if (typeof navigator.appName == 'undefined' ||
|
||||
navigator.appName != 'Netscape') {
|
||||
document.write('<style type="text/css">' +
|
||||
'#vt100 #console div, #vt100 #alt_console div {' +
|
||||
' overflow: hidden;' +
|
||||
'}' +
|
||||
'</style>');
|
||||
}
|
||||
--></script>
|
||||
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
|
||||
<script type="text/javascript" src="ShellInABox.js"></script>
|
||||
</head>
|
||||
<body onload="new ShellInABox()" scroll="no"><noscript>JavaScript
|
||||
must be enabled for ShellInABox</noscript></body>
|
||||
</html>
|
198
shellinabox/service.c
Normal file
198
shellinabox/service.c
Normal file
|
@ -0,0 +1,198 @@
|
|||
// service.c -- Service descriptions
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "shellinabox/service.h"
|
||||
#include "shellinabox/launcher.h"
|
||||
#include "shellinabox/privileges.h"
|
||||
#include "logging/logging.h"
|
||||
|
||||
|
||||
struct Service **services;
|
||||
int numServices;
|
||||
|
||||
void initService(struct Service *service, const char *arg) {
|
||||
// The first part of the argument is the path where the service should
|
||||
// be mounted. Remove any trailing slashes and make sure there is exactly
|
||||
// one leading slash before copying it into service->path.
|
||||
char *desc;
|
||||
check(desc = strdup(arg));
|
||||
while (*arg == '/') {
|
||||
arg++;
|
||||
}
|
||||
char *ptr;
|
||||
if ((ptr = strchr(arg, ':')) == NULL) {
|
||||
error:
|
||||
fatal("Syntax error in service description \"%s\".", desc);
|
||||
}
|
||||
service->id = -1;
|
||||
check(service->path = malloc(ptr - arg + 2));
|
||||
((char *)service->path)[0] = '/';
|
||||
memcpy((char *)service->path + 1, arg, ptr - arg);
|
||||
((char *)service->path)[ptr - arg + 1] = '\000';
|
||||
while (service->path[1] && strrchr(service->path, '\000')[-1] == '/') {
|
||||
strrchr(service->path, '\000')[-1] = '\000';
|
||||
}
|
||||
arg = ptr + 1;
|
||||
|
||||
// The next part of the argument is either the word 'LOGIN' or the
|
||||
// application definition.
|
||||
if (!strcmp(arg, "LOGIN")) {
|
||||
if (geteuid()) {
|
||||
fatal("Must be \"root\" to invoke \"/bin/login\". Maybe, change "
|
||||
"--service definitions?");
|
||||
}
|
||||
service->useLogin = 1;
|
||||
service->useHomeDir = 0;
|
||||
service->authUser = 0;
|
||||
service->uid = 0;
|
||||
service->gid = 0;
|
||||
check(service->user = strdup("root"));
|
||||
check(service->group = strdup("root"));
|
||||
check(service->cwd = strdup("/"));
|
||||
check(service->cmdline = strdup(
|
||||
"/bin/login -p -h ${peer}"));
|
||||
} else {
|
||||
service->useLogin = 0;
|
||||
|
||||
// The user definition is either the word 'AUTH' or a valid user and
|
||||
// group id.
|
||||
if ((ptr = strchr(arg, ':')) == NULL) {
|
||||
goto error;
|
||||
}
|
||||
*ptr = '\000';
|
||||
if (supportsPAM() && !strcmp(arg, "AUTH")) {
|
||||
service->authUser = 1;
|
||||
service->uid = -1;
|
||||
service->gid = -1;
|
||||
service->user = NULL;
|
||||
service->group = NULL;
|
||||
} else {
|
||||
service->authUser = 0;
|
||||
|
||||
// Numeric or symbolic user id
|
||||
service->uid = parseUser(arg, &service->user);
|
||||
*ptr = ':';
|
||||
arg = ptr + 1;
|
||||
|
||||
// Numeric or symbolic group id
|
||||
if ((ptr = strchr(arg, ':')) == NULL) {
|
||||
goto error;
|
||||
}
|
||||
*ptr = '\000';
|
||||
service->gid = parseGroup(arg, &service->group);
|
||||
}
|
||||
*ptr = ':';
|
||||
arg = ptr + 1;
|
||||
|
||||
// The next part of the argument is the starting working directory
|
||||
if ((ptr = strchr(arg, ':')) == NULL) {
|
||||
goto error;
|
||||
}
|
||||
*ptr = '\000';
|
||||
if (!strcmp(arg, "HOME")) {
|
||||
service->useHomeDir = 1;
|
||||
service->cwd = NULL;
|
||||
} else {
|
||||
if (*arg != '/') {
|
||||
fatal("Working directories must have absolute paths");
|
||||
}
|
||||
service->useHomeDir = 0;
|
||||
check(service->cwd = strdup(arg));
|
||||
}
|
||||
*ptr = ':';
|
||||
arg = ptr + 1;
|
||||
|
||||
// The final argument is the command line
|
||||
if (!*arg) {
|
||||
goto error;
|
||||
}
|
||||
check(service->cmdline = strdup(arg));
|
||||
}
|
||||
free(desc);
|
||||
}
|
||||
|
||||
struct Service *newService(const char *arg) {
|
||||
struct Service *service;
|
||||
check(service = malloc(sizeof(struct Service)));
|
||||
initService(service, arg);
|
||||
return service;
|
||||
}
|
||||
|
||||
void destroyService(struct Service *service) {
|
||||
if (service) {
|
||||
free((char *)service->path);
|
||||
free((char *)service->user);
|
||||
free((char *)service->group);
|
||||
free((char *)service->cwd);
|
||||
free((char *)service->cmdline);
|
||||
}
|
||||
}
|
||||
|
||||
void deleteService(struct Service *service) {
|
||||
destroyService(service);
|
||||
free(service);
|
||||
}
|
||||
|
||||
void destroyServiceHashEntry(void *arg, char *key, char *value) {
|
||||
}
|
||||
|
||||
static int enumerateServicesHelper(void *arg, const char *key, char **value) {
|
||||
check(services = realloc(services,
|
||||
++numServices * sizeof(struct Service *)));
|
||||
services[numServices-1] = *(struct Service **)value;
|
||||
services[numServices-1]->id = numServices-1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void enumerateServices(HashMap *serviceTable) {
|
||||
check(!services);
|
||||
check(!numServices);
|
||||
iterateOverHashMap(serviceTable, enumerateServicesHelper, NULL);
|
||||
}
|
75
shellinabox/service.h
Normal file
75
shellinabox/service.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
// service.h -- Service descriptions
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#ifndef SERVICE_H__
|
||||
#define SERVICE_H__
|
||||
|
||||
#include "libhttp/hashmap.h"
|
||||
|
||||
struct Service {
|
||||
int id;
|
||||
const char *path;
|
||||
int useLogin;
|
||||
int useHomeDir;
|
||||
int authUser;
|
||||
int uid;
|
||||
int gid;
|
||||
const char *user;
|
||||
const char *group;
|
||||
const char *cwd;
|
||||
const char *cmdline;
|
||||
};
|
||||
|
||||
extern struct Service **services;
|
||||
extern int numServices;
|
||||
|
||||
void initService(struct Service *service, const char *arg);
|
||||
struct Service *newService(const char *arg);
|
||||
void destroyService(struct Service *service);
|
||||
void deleteService(struct Service *service);
|
||||
void destroyServiceHashEntry(void *arg, char *key, char *value);
|
||||
void enumerateServices(HashMap *serviceTable);
|
||||
|
||||
#endif
|
220
shellinabox/session.c
Normal file
220
shellinabox/session.c
Normal file
|
@ -0,0 +1,220 @@
|
|||
// session.c -- Session management for HTTP/HTTPS connections
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "shellinabox/session.h"
|
||||
#include "logging/logging.h"
|
||||
|
||||
static HashMap *sessions;
|
||||
|
||||
|
||||
static struct Graveyard {
|
||||
struct Graveyard *next;
|
||||
time_t timeout;
|
||||
const char *sessionKey;
|
||||
} *graveyard;
|
||||
|
||||
void addToGraveyard(struct Session *session) {
|
||||
// It is possible for a child process to die, but for the Session to
|
||||
// linger around, because the browser has also navigated away and thus
|
||||
// nobody ever calls completePendingRequest(). We put these Sessions into
|
||||
// the graveyard and reap them after a while.
|
||||
struct Graveyard *g;
|
||||
check(g = malloc(sizeof(struct Graveyard)));
|
||||
g->next = graveyard;
|
||||
g->timeout = time(NULL) + AJAX_TIMEOUT;
|
||||
g->sessionKey = strdup(session->sessionKey);
|
||||
graveyard = g;
|
||||
}
|
||||
|
||||
static void checkGraveyardInternal(int expireAll) {
|
||||
if (!graveyard) {
|
||||
return;
|
||||
}
|
||||
time_t timeout = time(NULL) - (expireAll ? 2*AJAX_TIMEOUT : 0);
|
||||
for (struct Graveyard **g = &graveyard, *old = *g;
|
||||
old; ) {
|
||||
if (old->timeout < timeout) {
|
||||
*g = old->next;
|
||||
deleteFromHashMap(sessions, old->sessionKey);
|
||||
free((char *)old->sessionKey);
|
||||
free(old);
|
||||
} else {
|
||||
g = &old->next;
|
||||
}
|
||||
old = *g;
|
||||
}
|
||||
}
|
||||
|
||||
void checkGraveyard(void) {
|
||||
checkGraveyardInternal(0);
|
||||
}
|
||||
|
||||
void initSession(struct Session *session, const char *sessionKey,
|
||||
Server *server, URL *url, const char *peerName) {
|
||||
session->sessionKey = sessionKey;
|
||||
session->server = server;
|
||||
check(session->peerName = strdup(peerName));
|
||||
session->connection = NULL;
|
||||
session->http = NULL;
|
||||
session->url = url;
|
||||
session->done = 0;
|
||||
session->pty = -1;
|
||||
session->width = 0;
|
||||
session->height = 0;
|
||||
session->buffered = NULL;
|
||||
session->len = 0;
|
||||
}
|
||||
|
||||
struct Session *newSession(const char *sessionKey, Server *server, URL *url,
|
||||
const char *peerName) {
|
||||
struct Session *session;
|
||||
check(session = malloc(sizeof(struct Session)));
|
||||
initSession(session, sessionKey, server, url, peerName);
|
||||
return session;
|
||||
}
|
||||
|
||||
void destroySession(struct Session *session) {
|
||||
if (session) {
|
||||
free((char *)session->peerName);
|
||||
free((char *)session->sessionKey);
|
||||
deleteURL(session->url);
|
||||
if (session->pty >= 0) {
|
||||
NOINTR(close(session->pty));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void deleteSession(struct Session *session) {
|
||||
destroySession(session);
|
||||
free(session);
|
||||
}
|
||||
|
||||
void finishSession(struct Session *session) {
|
||||
deleteFromHashMap(sessions, session->sessionKey);
|
||||
}
|
||||
|
||||
void finishAllSessions(void) {
|
||||
checkGraveyardInternal(1);
|
||||
deleteHashMap(sessions);
|
||||
}
|
||||
|
||||
static void destroySessionHashEntry(void *arg, char *key, char *value) {
|
||||
deleteSession((struct Session *)value);
|
||||
}
|
||||
|
||||
static char *newSessionKey(void) {
|
||||
int fd;
|
||||
check((fd = NOINTR(open("/dev/urandom", O_RDONLY))) >= 0);
|
||||
unsigned char buf[16];
|
||||
check(NOINTR(read(fd, buf, sizeof(buf))) == sizeof(buf));
|
||||
NOINTR(close(fd));
|
||||
char *sessionKey;
|
||||
check(sessionKey = malloc((8*sizeof(buf) + 5)/6 + 1));
|
||||
char *ptr = sessionKey;
|
||||
int count = 0;
|
||||
int bits = 0;
|
||||
for (int i = 0;;) {
|
||||
bits = (bits << 8) | buf[i];
|
||||
count += 8;
|
||||
drain:
|
||||
while (count >= 6) {
|
||||
*ptr++ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
|
||||
"ghijklmnopqrstuvwxyz0123456789+/"
|
||||
[(bits >> (count -= 6)) & 0x3F];
|
||||
}
|
||||
if (++i >= sizeof(buf)) {
|
||||
if (count && i == sizeof(buf)) {
|
||||
bits <<= 8;
|
||||
count += 8;
|
||||
goto drain;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*ptr = '\000';
|
||||
check(!getFromHashMap(sessions, sessionKey));
|
||||
return sessionKey;
|
||||
}
|
||||
|
||||
struct Session *findSession(int *isNew, HttpConnection *http, URL *url) {
|
||||
*isNew = 1;
|
||||
if (!sessions) {
|
||||
sessions = newHashMap(destroySessionHashEntry, NULL);
|
||||
}
|
||||
const HashMap *args = urlGetArgs(url);
|
||||
const char *sessionKey = getFromHashMap(args, "session");
|
||||
struct Session *session;
|
||||
if (!sessionKey || !*sessionKey) {
|
||||
// Caller did not know the session key, yet. Create a new one.
|
||||
check(sessionKey = newSessionKey());
|
||||
session = newSession(sessionKey, httpGetServer(http), url,
|
||||
httpGetPeerName(http));
|
||||
addToHashMap(sessions, sessionKey, (const char *)session);
|
||||
debug("Creating new session: %s", sessionKey);
|
||||
} else {
|
||||
*isNew = 0;
|
||||
session = (struct Session *)getFromHashMap(sessions,
|
||||
sessionKey);
|
||||
if (session) {
|
||||
deleteURL(session->url);
|
||||
session->url = url;
|
||||
} else {
|
||||
debug("Failed to find session: %s", sessionKey);
|
||||
deleteURL(url);
|
||||
}
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
void iterateOverSessions(int (*fnc)(void *, const char *, char **), void *arg){
|
||||
iterateOverHashMap(sessions, fnc, arg);
|
||||
}
|
81
shellinabox/session.h
Normal file
81
shellinabox/session.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
// session.h -- Session management for HTTP/HTTPS connections
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#ifndef SESSION_H__
|
||||
#define SESSION_H__
|
||||
|
||||
#include "libhttp/http.h"
|
||||
|
||||
#define AJAX_TIMEOUT 45
|
||||
|
||||
struct Session {
|
||||
const char *sessionKey;
|
||||
Server *server;
|
||||
ServerConnection *connection;
|
||||
const char *peerName;
|
||||
HttpConnection *http;
|
||||
URL *url;
|
||||
int done;
|
||||
int pty;
|
||||
int width;
|
||||
int height;
|
||||
char *buffered;
|
||||
int len;
|
||||
};
|
||||
|
||||
void addToGraveyard(struct Session *session);
|
||||
void checkGraveyard(void);
|
||||
void initSession(struct Session *session, const char *sessionKey,
|
||||
Server *server, URL *url, const char *peerName);
|
||||
struct Session *newSession(const char *sessionKey, Server *server, URL *url,
|
||||
const char *peerName);
|
||||
void destroySession(struct Session *session);
|
||||
void deleteSession(struct Session *session);
|
||||
void finishSession(struct Session *session);
|
||||
void finishAllSessions(void);
|
||||
struct Session *findSession(int *isNew, HttpConnection *http, URL *url);
|
||||
void iterateOverSessions(int (*fnc)(void *, const char *, char **), void *arg);
|
||||
|
||||
#endif /* SESSION_H__ */
|
323
shellinabox/shell_in_a_box.js
Normal file
323
shellinabox/shell_in_a_box.js
Normal file
|
@ -0,0 +1,323 @@
|
|||
// ShellInABox.js -- Use XMLHttpRequest to provide an AJAX terminal emulator.
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
//
|
||||
//
|
||||
// Notes:
|
||||
//
|
||||
// The author believes that for the purposes of this license, you meet the
|
||||
// requirements for publishing the source code, if your web server publishes
|
||||
// the source in unmodified form (i.e. with licensing information, comments,
|
||||
// formatting, and identifier names intact). If there are technical reasons
|
||||
// that require you to make changes to the source code when serving the
|
||||
// JavaScript (e.g to remove pre-processor directives from the source), these
|
||||
// changes should be done in a reversible fashion.
|
||||
//
|
||||
// The author does not consider websites that reference this script in
|
||||
// unmodified form, and web servers that serve this script in unmodified form
|
||||
// to be derived works. As such, they are believed to be outside of the
|
||||
// scope of this license and not subject to the rights or restrictions of the
|
||||
// GNU General Public License.
|
||||
//
|
||||
// If in doubt, consult a legal professional familiar with the laws that
|
||||
// apply in your country.
|
||||
|
||||
#define XHR_UNITIALIZED 0
|
||||
#define XHR_OPEN 1
|
||||
#define XHR_SENT 2
|
||||
#define XHR_RECEIVING 3
|
||||
#define XHR_LOADED 4
|
||||
|
||||
// IE does not define XMLHttpRequest by default, so we provide a suitable
|
||||
// wrapper.
|
||||
if (typeof XMLHttpRequest == 'undefined') {
|
||||
XMLHttpRequest = function() {
|
||||
try { return new ActiveXObject('Msxml2.XMLHTTP.6.0');} catch (e) { }
|
||||
try { return new ActiveXObject('Msxml2.XMLHTTP.3.0');} catch (e) { }
|
||||
try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) { }
|
||||
try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) { }
|
||||
throw new Error('');
|
||||
};
|
||||
}
|
||||
|
||||
function extend(subClass, baseClass) {
|
||||
function inheritance() { }
|
||||
inheritance.prototype = baseClass.prototype;
|
||||
subClass.prototype = new inheritance();
|
||||
subClass.prototype.constructor = subClass;
|
||||
subClass.prototype.superClass = baseClass.prototype;
|
||||
};
|
||||
|
||||
function ShellInABox(url, container) {
|
||||
if (url == undefined) {
|
||||
this.url = document.location.href;
|
||||
} else {
|
||||
this.url = url;
|
||||
}
|
||||
this.nextUrl = this.url;
|
||||
this.session = null;
|
||||
this.pendingKeys = '';
|
||||
this.keysInFlight = false;
|
||||
this.superClass.constructor.call(this, container);
|
||||
|
||||
// We have to initiate the first XMLHttpRequest from a timer. Otherwise,
|
||||
// Chrome never realizes that the page has loaded.
|
||||
setTimeout(function(shellInABox) {
|
||||
return function() {
|
||||
shellInABox.sendRequest();
|
||||
};
|
||||
}(this), 1);
|
||||
};
|
||||
extend(ShellInABox, VT100);
|
||||
|
||||
ShellInABox.prototype.sessionClosed = function() {
|
||||
if (this.session) {
|
||||
this.session = undefined;
|
||||
if (this.cursorX > 0) {
|
||||
this.vt100('\r\n');
|
||||
}
|
||||
this.vt100('Session closed.');
|
||||
}
|
||||
this.showReconnect(true);
|
||||
};
|
||||
|
||||
ShellInABox.prototype.reconnect = function() {
|
||||
this.showReconnect(false);
|
||||
if (!this.session) {
|
||||
if (this.url != this.nextUrl) {
|
||||
document.location.replace(this.nextUrl);
|
||||
} else {
|
||||
this.pendingKeys = '';
|
||||
this.keysInFlight = false;
|
||||
this.reset(true);
|
||||
this.sendRequest();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ShellInABox.prototype.sendRequest = function(request) {
|
||||
if (request == undefined) {
|
||||
request = new XMLHttpRequest();
|
||||
}
|
||||
request.open('POST', this.url, true);
|
||||
request.setRequestHeader('Content-Type',
|
||||
'application/x-www-form-urlencoded; charset=utf-8');
|
||||
request.onreadystatechange = function(shellInABox) {
|
||||
return function() {
|
||||
try {
|
||||
return shellInABox.onReadyStateChange(request);
|
||||
} catch (e) {
|
||||
shellInABox.sessionClosed();
|
||||
}
|
||||
}
|
||||
}(this);
|
||||
request.send(
|
||||
'width=' + this.terminalWidth +
|
||||
'&height=' + this.terminalHeight +
|
||||
(this.session ? "&session=" + encodeURIComponent(this.session) : ""));
|
||||
};
|
||||
|
||||
ShellInABox.prototype.onReadyStateChange = function(request) {
|
||||
if (request.readyState == XHR_LOADED) {
|
||||
if (request.status == 200) {
|
||||
var response = eval('(' + request.responseText + ')');
|
||||
|
||||
if (response.data) {
|
||||
this.vt100(response.data);
|
||||
}
|
||||
|
||||
if (!response.session ||
|
||||
this.session && this.session != response.session) {
|
||||
this.sessionClosed();
|
||||
} else {
|
||||
this.session = response.session;
|
||||
this.sendRequest(request);
|
||||
}
|
||||
} else if (request.status == 0) {
|
||||
// Time Out
|
||||
this.sendRequest(request);
|
||||
} else {
|
||||
this.sessionClosed();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ShellInABox.prototype.sendKeys = function(keys) {
|
||||
if (this.keysInFlight || this.session == undefined) {
|
||||
this.pendingKeys += keys;
|
||||
} else {
|
||||
this.keysInFlight = true;
|
||||
keys = this.pendingKeys + keys;
|
||||
this.pendingKeys = '';
|
||||
var request = new XMLHttpRequest();
|
||||
request.open('POST', this.url, true);
|
||||
request.setRequestHeader('Content-Type',
|
||||
'application/x-www-form-urlencoded; charset=utf-8');
|
||||
request.onreadystatechange = function(shellInABox) {
|
||||
return function() {
|
||||
try {
|
||||
return shellInABox.keyPressReadyStateChange(request);
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
}(this);
|
||||
request.send(
|
||||
'width=' + this.terminalWidth +
|
||||
'&height=' + this.terminalHeight +
|
||||
"&session=" + encodeURIComponent(this.session) +
|
||||
"&keys=" + encodeURIComponent(keys));
|
||||
}
|
||||
};
|
||||
|
||||
ShellInABox.prototype.keyPressReadyStateChange = function(request) {
|
||||
if (request.readyState == XHR_LOADED) {
|
||||
this.keysInFlight = false;
|
||||
if (this.pendingKeys) {
|
||||
this.sendKeys('');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ShellInABox.prototype.keysPressed = function(ch) {
|
||||
var hex = '0123456789ABCDEF';
|
||||
var s = '';
|
||||
for (var i = 0; i < ch.length; i++) {
|
||||
var c = ch.charCodeAt(i);
|
||||
if (c < 128) {
|
||||
s += hex.charAt(c >> 4) + hex.charAt(c & 0xF);
|
||||
} else if (c < 0x800) {
|
||||
s += hex.charAt(0xC + (c >> 10) ) +
|
||||
hex.charAt( (c >> 6) & 0xF ) +
|
||||
hex.charAt(0x8 + ((c >> 4) & 0x3)) +
|
||||
hex.charAt( c & 0xF );
|
||||
} else if (c < 0x10000) {
|
||||
s += 'E' +
|
||||
hex.charAt( (c >> 12) ) +
|
||||
hex.charAt(0x8 + (c >> 10) & 0x3 ) +
|
||||
hex.charAt( (c >> 6) & 0xF ) +
|
||||
hex.charAt(0x8 + ((c >> 4) & 0x3)) +
|
||||
hex.charAt( c & 0xF );
|
||||
} else if (c < 0x110000) {
|
||||
s += 'F' +
|
||||
hex.charAt( (c >> 18) ) +
|
||||
hex.charAt(0x8 + (c >> 16) & 0x3 ) +
|
||||
hex.charAt( (c >> 12) & 0xF ) +
|
||||
hex.charAt(0x8 + (c >> 10) & 0x3 ) +
|
||||
hex.charAt( (c >> 6) & 0xF ) +
|
||||
hex.charAt(0x8 + ((c >> 4) & 0x3)) +
|
||||
hex.charAt( c & 0xF );
|
||||
}
|
||||
}
|
||||
this.sendKeys(s);
|
||||
};
|
||||
|
||||
ShellInABox.prototype.resized = function(w, h) {
|
||||
// Do not send a resize request until we are fully initialized.
|
||||
if (this.session) {
|
||||
// sendKeys() always transmits the current terminal size. So, flush all
|
||||
// pending keys.
|
||||
this.sendKeys('');
|
||||
}
|
||||
};
|
||||
|
||||
ShellInABox.prototype.toggleSSL = function() {
|
||||
this.nextUrl = this.nextUrl.match(/^https:/)
|
||||
? this.nextUrl.replace(/^https:/, 'http:').replace(/\/*$/, '/plain')
|
||||
: this.nextUrl.replace(/^http/, 'https').replace(/\/*plain$/, '');
|
||||
if (this.nextUrl.match(/^[:]*:\/\/[^/]*$/)) {
|
||||
this.nextUrl += '/';
|
||||
}
|
||||
if (this.session && this.nextUrl != this.url) {
|
||||
alert('This change will take effect the next time you login.');
|
||||
}
|
||||
};
|
||||
|
||||
ShellInABox.prototype.extendContextMenu = function(entries, actions) {
|
||||
// Modify the entries and actions in place, adding any locally defined
|
||||
// menu entries.
|
||||
var oldActions = [ ];
|
||||
for (var i = 0; i < actions.length; i++) {
|
||||
oldActions[i] = actions[i];
|
||||
}
|
||||
for (var node = entries.firstChild, i = 0, j = 0; node;
|
||||
node = node.nextSibling) {
|
||||
if (node.tagName == 'LI') {
|
||||
actions[i++] = oldActions[j++];
|
||||
if (node.id == "endconfig") {
|
||||
node.id = '';
|
||||
if (serverSupportsSSL) {
|
||||
// If the server supports both SSL and plain text connections,
|
||||
// provide a menu entry to switch between the two.
|
||||
var newNode = document.createElement('li');
|
||||
newNode.innerHTML =
|
||||
(this.nextUrl.match(/^https:/) ? '✔ ' : '') + 'Secure';
|
||||
if (node.nextSibling) {
|
||||
entries.insertBefore(newNode, node.nextSibling);
|
||||
} else {
|
||||
entries.appendChild(newNode);
|
||||
}
|
||||
actions[i++] = this.toggleSSL;
|
||||
node = newNode;
|
||||
}
|
||||
node.id = 'endconfig';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
ShellInABox.prototype.about = function() {
|
||||
alert("Shell In A Box version " + VERSION +
|
||||
"\nCopyright 2008 by Markus Gutschke\n" +
|
||||
"For more information check http://shellinabox.com" +
|
||||
(serverSupportsSSL ?
|
||||
"\n\n" +
|
||||
"This product includes software developed by the OpenSSL Project\n" +
|
||||
"for use in the OpenSSL Toolkit. (http://www.openssl.org/)\n" +
|
||||
"\n" +
|
||||
"This product includes cryptographic software written by " +
|
||||
"Eric Young\n(eay@cryptsoft.com)" :
|
||||
""));
|
||||
};
|
||||
|
831
shellinabox/shellinaboxd.c
Normal file
831
shellinabox/shellinaboxd.c
Normal file
|
@ -0,0 +1,831 @@
|
|||
// shellinaboxd.c -- A custom web server that makes command line applications
|
||||
// available as AJAX web applications.
|
||||
// Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
//
|
||||
// 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// In addition to these license terms, the author grants the following
|
||||
// additional rights:
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or
|
||||
// combining it with the OpenSSL project's OpenSSL library (or a
|
||||
// modified version of that library), containing parts covered by the
|
||||
// terms of the OpenSSL or SSLeay licenses, the author
|
||||
// grants you additional permission to convey the resulting work.
|
||||
// Corresponding Source for a non-source form of such a combination
|
||||
// shall include the source code for the parts of OpenSSL used as well
|
||||
// as that of the covered work.
|
||||
//
|
||||
// You may at your option choose to remove this additional permission from
|
||||
// the work, or from any part of it.
|
||||
//
|
||||
// It is possible to build this program in a way that it loads OpenSSL
|
||||
// libraries at run-time. If doing so, the following notices are required
|
||||
// by the OpenSSL and SSLeay licenses:
|
||||
//
|
||||
// This product includes software developed by the OpenSSL Project
|
||||
// for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
//
|
||||
// This product includes cryptographic software written by Eric Young
|
||||
// (eay@cryptsoft.com)
|
||||
//
|
||||
//
|
||||
// The most up-to-date version of this program is always available from
|
||||
// http://shellinabox.com
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <locale.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "libhttp/http.h"
|
||||
#include "logging/logging.h"
|
||||
#include "shellinabox/externalfile.h"
|
||||
#include "shellinabox/launcher.h"
|
||||
#include "shellinabox/privileges.h"
|
||||
#include "shellinabox/service.h"
|
||||
#include "shellinabox/session.h"
|
||||
|
||||
#define PORTNUM 4200
|
||||
#define MAX_RESPONSE 2048
|
||||
|
||||
static int port = PORTNUM;
|
||||
static int numericHosts = 0;
|
||||
static int enableSSL = 1;
|
||||
static char *certificateDir;
|
||||
static HashMap *externalFiles;
|
||||
|
||||
|
||||
static char *jsonEscape(const char *buf, int len) {
|
||||
static const char *hexDigit = "0123456789ABCDEF";
|
||||
|
||||
// Determine the space that is needed to encode the buffer
|
||||
int count = 0;
|
||||
const char *ptr = buf;
|
||||
for (int i = 0; i < len; i++) {
|
||||
unsigned char ch = *(unsigned char *)ptr++;
|
||||
if (ch < ' ') {
|
||||
switch (ch) {
|
||||
case '\b': case '\f': case '\n': case '\r': case '\t':
|
||||
count += 2;
|
||||
break;
|
||||
default:
|
||||
count += 6;
|
||||
break;
|
||||
}
|
||||
} else if (ch == '"' || ch == '\\' || ch == '/') {
|
||||
count += 2;
|
||||
} else if (ch > '\x7F') {
|
||||
count += 6;
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Encode the buffer using JSON string escaping
|
||||
char *result;
|
||||
check(result = malloc(count + 1));
|
||||
char *dst = result;
|
||||
ptr = buf;
|
||||
for (int i = 0; i < len; i++) {
|
||||
unsigned char ch = *(unsigned char *)ptr++;
|
||||
if (ch < ' ') {
|
||||
*dst++ = '\\';
|
||||
switch (ch) {
|
||||
case '\b': *dst++ = 'b'; break;
|
||||
case '\f': *dst++ = 'f'; break;
|
||||
case '\n': *dst++ = 'n'; break;
|
||||
case '\r': *dst++ = 'r'; break;
|
||||
case '\t': *dst++ = 't'; break;
|
||||
default:
|
||||
unicode:
|
||||
*dst++ = 'u';
|
||||
*dst++ = '0';
|
||||
*dst++ = '0';
|
||||
*dst++ = hexDigit[ch >> 4];
|
||||
*dst++ = hexDigit[ch & 0xF];
|
||||
break;
|
||||
}
|
||||
} else if (ch == '"' || ch == '\\' || ch == '/') {
|
||||
*dst++ = '\\';
|
||||
*dst++ = ch;
|
||||
} else if (ch > '\x7F') {
|
||||
*dst++ = '\\';
|
||||
goto unicode;
|
||||
} else {
|
||||
*dst++ = ch;
|
||||
}
|
||||
}
|
||||
*dst++ = '\000';
|
||||
return result;
|
||||
}
|
||||
|
||||
static int completePendingRequest(struct Session *session,
|
||||
char *buf, int len, int maxLength) {
|
||||
// If there is no pending HTTP request, save the data and return
|
||||
// immediately.
|
||||
if (!session->http) {
|
||||
if (len) {
|
||||
if (session->buffered) {
|
||||
check(session->buffered = realloc(session->buffered,
|
||||
session->len + len));
|
||||
memcpy(session->buffered + session->len, buf, len);
|
||||
session->len += len;
|
||||
} else {
|
||||
check(session->buffered = malloc(len));
|
||||
memcpy(session->buffered, buf, len);
|
||||
session->len = len;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If we have a pending HTTP request, we can reply to it, now.
|
||||
char *data;
|
||||
if (session->buffered) {
|
||||
check(session->buffered = realloc(session->buffered,
|
||||
session->len + len));
|
||||
memcpy(session->buffered + session->len, buf, len);
|
||||
session->len += len;
|
||||
if (maxLength > 0 && session->len > maxLength) {
|
||||
data = jsonEscape(session->buffered, maxLength);
|
||||
session->len -= maxLength;
|
||||
memmove(session->buffered, session->buffered + maxLength,
|
||||
session->len);
|
||||
} else {
|
||||
data = jsonEscape(session->buffered, session->len);
|
||||
free(session->buffered);
|
||||
session->buffered = NULL;
|
||||
session->len = 0;
|
||||
}
|
||||
} else {
|
||||
if (maxLength > 0 && len > maxLength) {
|
||||
session->len = len - maxLength;
|
||||
check(session->buffered = malloc(session->len));
|
||||
memcpy(session->buffered, buf + maxLength, session->len);
|
||||
data = jsonEscape(buf, maxLength);
|
||||
} else {
|
||||
data = jsonEscape(buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
char *json = stringPrintf(NULL, "{"
|
||||
"\"session\":\"%s\","
|
||||
"\"data\":\"%s\""
|
||||
"}",
|
||||
session->sessionKey, data);
|
||||
free(data);
|
||||
HttpConnection *http = session->http;
|
||||
char *response = stringPrintf(NULL,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: application/json; "
|
||||
"charset=utf-8\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"\r\n"
|
||||
"%s",
|
||||
strlen(json),
|
||||
strcmp(httpGetMethod(http),
|
||||
"HEAD") ? json : "");
|
||||
free(json);
|
||||
session->http = NULL;
|
||||
httpTransfer(http, response, strlen(response));
|
||||
}
|
||||
if (session->done && !session->buffered) {
|
||||
finishSession(session);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void sessionDone(void *arg) {
|
||||
debug("Child terminated");
|
||||
struct Session *session = (struct Session *)arg;
|
||||
session->done = 1;
|
||||
addToGraveyard(session);
|
||||
completePendingRequest(session, "", 0, INT_MAX);
|
||||
}
|
||||
|
||||
static int handleSession(struct ServerConnection *connection, void *arg,
|
||||
short *events, short revents) {
|
||||
struct Session *session = (struct Session *)arg;
|
||||
session->connection = connection;
|
||||
int len = MAX_RESPONSE - session->len;
|
||||
if (len <= 0) {
|
||||
len = 1;
|
||||
}
|
||||
char buf[len];
|
||||
int bytes = 0;
|
||||
if (revents & POLLIN) {
|
||||
bytes = NOINTR(read(session->pty, buf, len));
|
||||
if (bytes <= 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
int timedOut = serverGetTimeout(connection) < 0;
|
||||
if (bytes || timedOut) {
|
||||
if (!session->http && timedOut) {
|
||||
debug("Timeout. Closing session.");
|
||||
return 0;
|
||||
}
|
||||
check(!session->done);
|
||||
check(completePendingRequest(session, buf, bytes, MAX_RESPONSE));
|
||||
if (session->len >= MAX_RESPONSE) {
|
||||
serverConnectionSetEvents(session->server, connection, 0);
|
||||
}
|
||||
serverSetTimeout(connection, AJAX_TIMEOUT);
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int invalidatePendingHttpSession(void *arg, const char *key,
|
||||
char **value) {
|
||||
struct Session *session = *(struct Session **)value;
|
||||
if (session->http && session->http == (HttpConnection *)arg) {
|
||||
debug("Clearing pending HTTP connection for session %s", key);
|
||||
session->http = NULL;
|
||||
serverDeleteConnection(session->server, session->pty);
|
||||
|
||||
// Return zero in order to remove this HTTP from the "session" hashmap
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the session is still in use, do not remove it from the "sessions" map
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dataHandler(HttpConnection *http, struct Service *service,
|
||||
const char *buf, int len, URL *url) {
|
||||
if (!buf) {
|
||||
// Somebody unexpectedly closed our http connection (e.g. because of a
|
||||
// timeout). This is the last notification that we will get.
|
||||
deleteURL(url);
|
||||
iterateOverSessions(invalidatePendingHttpSession, http);
|
||||
return HTTP_DONE;
|
||||
}
|
||||
|
||||
// Find an existing session, or create the record for a new one
|
||||
int isNew;
|
||||
struct Session *session = findSession(&isNew, http, url);
|
||||
if (session == NULL) {
|
||||
httpSendReply(http, 400, "Bad Request", NULL);
|
||||
return HTTP_DONE;
|
||||
}
|
||||
|
||||
if (!isNew && strcmp(session->peerName, httpGetPeerName(http))) {
|
||||
error("Peername changed from %s to %s",
|
||||
session->peerName, httpGetPeerName(http));
|
||||
httpSendReply(http, 400, "Bad Request", NULL);
|
||||
return HTTP_DONE;
|
||||
}
|
||||
|
||||
const HashMap *args = urlGetArgs(session->url);
|
||||
int oldWidth = session->width;
|
||||
int oldHeight = session->height;
|
||||
const char *width = getFromHashMap(args, "width");
|
||||
const char *height = getFromHashMap(args, "height");
|
||||
const char *keys = getFromHashMap(args, "keys");
|
||||
|
||||
// Adjust window dimensions if provided by client
|
||||
if (width && height) {
|
||||
session->width = atoi(width);
|
||||
session->height = atoi(height);
|
||||
}
|
||||
|
||||
// If the caller provided the "keys" parameter, this request sends key
|
||||
// strokes but does not expect a reply.
|
||||
if (!keys) {
|
||||
if (session->http &&
|
||||
!completePendingRequest(session, "", 0, MAX_RESPONSE)) {
|
||||
httpSendReply(http, 400, "Bad Request", NULL);
|
||||
return HTTP_DONE;
|
||||
}
|
||||
session->http = http;
|
||||
|
||||
// Create a new session, if the client did not provide an existing one
|
||||
if (isNew) {
|
||||
debug("Creating new child process");
|
||||
if (launchChild(service->id, session) < 0) {
|
||||
deleteSession(session);
|
||||
httpSendReply(http, 500, "Internal Error", NULL);
|
||||
return HTTP_DONE;
|
||||
}
|
||||
session->connection = serverAddConnection(httpGetServer(http),
|
||||
session->pty, handleSession,
|
||||
sessionDone, session);
|
||||
serverSetTimeout(session->connection, AJAX_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset window dimensions of the pseudo TTY, if changed since last time set.
|
||||
if (session->width > 0 && session->height > 0 &&
|
||||
(session->width != oldWidth || session->height != oldHeight)) {
|
||||
debug("Window size changed to %dx%d", session->width, session->height);
|
||||
setWindowSize(session->pty, session->width, session->height);
|
||||
}
|
||||
|
||||
// Process keypresses, if any. Then send a synchronous reply.
|
||||
if (keys) {
|
||||
char *keyCodes;
|
||||
check(keyCodes = malloc(strlen(keys)/2));
|
||||
int len = 0;
|
||||
for (const unsigned char *ptr = (const unsigned char *)keys; ;) {
|
||||
unsigned c0 = *ptr++;
|
||||
if (c0 < '0' || (c0 > '9' && c0 < 'A') ||
|
||||
(c0 > 'F' && c0 < 'a') || c0 > 'f') {
|
||||
break;
|
||||
}
|
||||
unsigned c1 = *ptr++;
|
||||
if (c1 < '0' || (c1 > '9' && c1 < 'A') ||
|
||||
(c1 > 'F' && c1 < 'a') || c1 > 'f') {
|
||||
break;
|
||||
}
|
||||
keyCodes[len++] = 16*((c0 & 0xF) + 9*(c0 > '9')) +
|
||||
(c1 & 0xF) + 9*(c1 > '9');
|
||||
}
|
||||
if (write(session->pty, keyCodes, len) < 0 && errno == EAGAIN) {
|
||||
completePendingRequest(session, "\007", 1, MAX_RESPONSE);
|
||||
}
|
||||
free(keyCodes);
|
||||
httpSendReply(http, 200, "OK", " ");
|
||||
return HTTP_DONE;
|
||||
}
|
||||
|
||||
session->connection = serverGetConnection(session->server,
|
||||
session->connection,
|
||||
session->pty);
|
||||
if (session->buffered) {
|
||||
if (completePendingRequest(session, "", 0, MAX_RESPONSE) &&
|
||||
session->connection) {
|
||||
// Reset the timeout, as we just received a new request.
|
||||
serverSetTimeout(session->connection, AJAX_TIMEOUT);
|
||||
if (session->len < MAX_RESPONSE) {
|
||||
// Re-enable input on the child's pty
|
||||
serverConnectionSetEvents(session->server, session->connection,POLLIN);
|
||||
}
|
||||
}
|
||||
return HTTP_DONE;
|
||||
} else if (session->connection) {
|
||||
// Re-enable input on the child's pty
|
||||
serverConnectionSetEvents(session->server, session->connection, POLLIN);
|
||||
serverSetTimeout(session->connection, AJAX_TIMEOUT);
|
||||
}
|
||||
|
||||
return HTTP_SUSPEND;
|
||||
}
|
||||
|
||||
static void serveStaticFile(HttpConnection *http, const char *contentType,
|
||||
const char *start, const char *end) {
|
||||
char *response = stringPrintf(NULL,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: %s\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"\r\n",
|
||||
contentType, end - start);
|
||||
int len = strlen(response);
|
||||
if (strcmp(httpGetMethod(http), "HEAD")) {
|
||||
check(response = realloc(response, len + (end - start)));
|
||||
memcpy(response + len, start, end - start);
|
||||
len += end - start;
|
||||
}
|
||||
httpTransfer(http, response, len);
|
||||
}
|
||||
|
||||
static int shellInABoxHttpHandler(HttpConnection *http, void *arg,
|
||||
const char *buf, int len) {
|
||||
checkGraveyard();
|
||||
URL *url = newURL(http, buf, len);
|
||||
const HashMap *headers = httpGetHeaders(http);
|
||||
const char *contentType = getFromHashMap(headers, "content-type");
|
||||
|
||||
// Normalize the path info
|
||||
const char *pathInfo = urlGetPathInfo(url);
|
||||
while (*pathInfo == '/') {
|
||||
pathInfo++;
|
||||
}
|
||||
const char *endPathInfo;
|
||||
for (endPathInfo = pathInfo;
|
||||
*endPathInfo && *endPathInfo != '/';
|
||||
endPathInfo++) {
|
||||
}
|
||||
int pathInfoLength = endPathInfo - pathInfo;
|
||||
|
||||
// The root page either serves the AJAX application or redirects to the
|
||||
// secure HTTPS URL.
|
||||
if (!pathInfoLength ||
|
||||
(pathInfoLength == 5 && !memcmp(pathInfo, "plain", 5))) {
|
||||
if (contentType &&
|
||||
!strncasecmp(contentType, "application/x-www-form-urlencoded", 33)) {
|
||||
// XMLHttpRequest carrying data between the AJAX application and the
|
||||
// client session.
|
||||
return dataHandler(http, arg, buf, len, url);
|
||||
}
|
||||
if (enableSSL && !pathInfoLength && strcmp(urlGetProtocol(url), "https")) {
|
||||
httpSendReply(http, 200, "Shell In A Box",
|
||||
"<script type=\"text/javascript\"><!--\n"
|
||||
"document.location.replace("
|
||||
"document.location.href.replace(/^http:/,'https:'));\n"
|
||||
"--></script>\n"
|
||||
"<noscript>\n"
|
||||
"JavaScript must be enabled for ShellInABox\n"
|
||||
"</noscript>");
|
||||
} else {
|
||||
extern char rootPageStart[];
|
||||
extern char rootPageEnd[];
|
||||
serveStaticFile(http, "text/html; charset=utf-8",
|
||||
rootPageStart, rootPageEnd);
|
||||
}
|
||||
} else if (pathInfoLength == 8 && !memcmp(pathInfo, "beep.wav", 8)) {
|
||||
// Serve the audio sample for the console bell.
|
||||
extern char beepStart[];
|
||||
extern char beepEnd[];
|
||||
serveStaticFile(http, "audio/x-wav", beepStart, beepEnd);
|
||||
} else if (pathInfoLength == 11 && !memcmp(pathInfo, "favicon.ico", 11)) {
|
||||
// Serve the favicon
|
||||
extern char faviconStart[];
|
||||
extern char faviconEnd[];
|
||||
serveStaticFile(http, "image/x-icon", faviconStart, faviconEnd);
|
||||
} else if (pathInfoLength == 14 && !memcmp(pathInfo, "ShellInABox.js", 14)) {
|
||||
// Serve both vt100.js and shell_in_a_box.js in the same transaction.
|
||||
// Also, indicate to the client whether the server is SSL enabled.
|
||||
extern char vt100Start[];
|
||||
extern char vt100End[];
|
||||
extern char shellInABoxStart[];
|
||||
extern char shellInABoxEnd[];
|
||||
char *sslState = stringPrintf(NULL,
|
||||
"serverSupportsSSL = %s;\n\n",
|
||||
enableSSL ? "true" : "false");
|
||||
int sslStateLength = strlen(sslState);
|
||||
int contentLength = sslStateLength +
|
||||
(vt100End - vt100Start) +
|
||||
(shellInABoxEnd - shellInABoxStart);
|
||||
char *response = stringPrintf(NULL,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/javascript; charset=utf-8\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"\r\n",
|
||||
contentLength);
|
||||
int headerLength = strlen(response);
|
||||
if (strcmp(httpGetMethod(http), "HEAD")) {
|
||||
check(response = realloc(response, headerLength + contentLength));
|
||||
memcpy(memcpy(memcpy(
|
||||
response + headerLength, sslState, sslStateLength) + sslStateLength,
|
||||
vt100Start, vt100End - vt100Start) + (vt100End - vt100Start),
|
||||
shellInABoxStart, shellInABoxEnd - shellInABoxStart);
|
||||
} else {
|
||||
contentLength = 0;
|
||||
}
|
||||
free(sslState);
|
||||
httpTransfer(http, response, headerLength + contentLength);
|
||||
} else if (pathInfoLength == 10 && !memcmp(pathInfo, "styles.css", 10)) {
|
||||
// Serve the style sheet.
|
||||
extern char stylesStart[];
|
||||
extern char stylesEnd[];
|
||||
serveStaticFile(http, "text/css; charset=utf-8", stylesStart, stylesEnd);
|
||||
} else {
|
||||
httpSendReply(http, 404, "File not found", NULL);
|
||||
}
|
||||
|
||||
deleteURL(url);
|
||||
return HTTP_DONE;
|
||||
}
|
||||
|
||||
static int strtoint(const char *s, int minVal, int maxVal) {
|
||||
char *ptr;
|
||||
if (!*s) {
|
||||
fatal("Missing numeric value.");
|
||||
}
|
||||
long l = strtol(s, &ptr, 10);
|
||||
if (*ptr || l < minVal || l > maxVal) {
|
||||
fatal("Range error on numeric value \"%s\".", s);
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
static void usage(void) {
|
||||
// Drop privileges so that we can tell which uid/gid we would normally
|
||||
// run at.
|
||||
dropPrivileges();
|
||||
uid_t r_uid, e_uid, s_uid;
|
||||
uid_t r_gid, e_gid, s_gid;
|
||||
check(!getresuid(&r_uid, &e_uid, &s_uid));
|
||||
check(!getresgid(&r_gid, &e_gid, &s_gid));
|
||||
const char *user = getUserName(r_uid);
|
||||
const char *group = getGroupName(r_gid);
|
||||
|
||||
message("Usage: shellinaboxd [OPTIONS]...\n"
|
||||
"Starts an HTTP server that serves terminal emulators to AJAX "
|
||||
"enabled browsers.\n"
|
||||
"\n"
|
||||
"List of command line options:\n"
|
||||
" -b, --background[=PIDFILE] run in background\n"
|
||||
"%s"
|
||||
" -d, --debug enable debug mode\n"
|
||||
" -f, --static-file=URL:FILE serve static file from URL path\n"
|
||||
" -g, --group=GID switch to this group (default: %s)\n"
|
||||
" -h, --help print this message\n"
|
||||
" -n, --numeric do not resolve hostnames\n"
|
||||
" -p, --port=PORT select a port (default: %d)\n"
|
||||
" -s, --service=SERVICE define one or more services\n"
|
||||
"%s"
|
||||
" -q, --quiet turn off all messages\n"
|
||||
" -u, --user=UID switch to this user (default: %s)\n"
|
||||
" -v, --verbose enable logging messages\n"
|
||||
" --version prints version information\n"
|
||||
"\n"
|
||||
"Debug, quiet, and verbose are mutually exclusive.\n"
|
||||
"\n"
|
||||
"One or more --service arguments define services that should "
|
||||
"be made available \n"
|
||||
"through the web interface:\n"
|
||||
" SERVICE := <url-path> ':' APP\n"
|
||||
" APP := 'LOGIN' | USER ':' CWD ':' <cmdline>\n"
|
||||
" USER := %s<username> ':' <groupname>\n"
|
||||
" CWD := 'HOME' | <dir>\n"
|
||||
"\n"
|
||||
"<cmdline> supports variable expansion:\n"
|
||||
" ${columns} - number of columns\n"
|
||||
" ${gid} - gid id\n"
|
||||
" ${group} - group name\n"
|
||||
" ${home} - home directory\n"
|
||||
" ${lines} - number of rows\n"
|
||||
" ${peer} - name of remote peer\n"
|
||||
" ${uid} - user id\n"
|
||||
" ${user} - user name",
|
||||
!serverSupportsSSL() ? "" :
|
||||
" -c, --cert=CERTDIR set certificate dir "
|
||||
"(default: $PWD)\n",
|
||||
group, PORTNUM,
|
||||
!serverSupportsSSL() ? "" :
|
||||
" -t, --disable-ssl disable transparent SSL support\n",
|
||||
user, supportsPAM() ? "'AUTH' | " : "");
|
||||
free((char *)user);
|
||||
free((char *)group);
|
||||
}
|
||||
|
||||
static void destroyExternalFileHashEntry(void *arg, char *key, char *value) {
|
||||
free(key);
|
||||
free(value);
|
||||
}
|
||||
|
||||
static void parseArgs(int argc, char * const argv[]) {
|
||||
int hasSSL = serverSupportsSSL();
|
||||
if (!hasSSL) {
|
||||
enableSSL = 0;
|
||||
}
|
||||
int demonize = 0;
|
||||
const char *pidfile = NULL;
|
||||
int verbosity = MSG_DEFAULT;
|
||||
externalFiles = newHashMap(destroyExternalFileHashEntry, NULL);
|
||||
HashMap *services = newHashMap(destroyServiceHashEntry, NULL);
|
||||
for (;;) {
|
||||
static const char optstring[] = "+hbc:df:g:np:s:tqu:v";
|
||||
static struct option options[] = {
|
||||
{ "help", 0, 0, 'h' },
|
||||
{ "background", 2, 0, 'b' },
|
||||
{ "cert", 1, 0, 'c' },
|
||||
{ "debug", 0, 0, 'd' },
|
||||
{ "static-file", 1, 0, 'f' },
|
||||
{ "group", 1, 0, 'g' },
|
||||
{ "numeric", 0, 0, 'n' },
|
||||
{ "port", 1, 0, 'p' },
|
||||
{ "service", 1, 0, 's' },
|
||||
{ "disable-ssl", 0, 0, 't' },
|
||||
{ "quiet", 0, 0, 'q' },
|
||||
{ "user", 1, 0, 'u' },
|
||||
{ "verbose", 0, 0, 'v' },
|
||||
{ "version", 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 } };
|
||||
int idx = -1;
|
||||
int c = getopt_long(argc, argv, optstring, options, &idx);
|
||||
if (c > 0) {
|
||||
for (int i = 0; options[i].name; i++) {
|
||||
if (options[i].val == c) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (c < 0) {
|
||||
break;
|
||||
}
|
||||
if (idx-- <= 0) {
|
||||
// Help (or invalid argument)
|
||||
usage();
|
||||
exit(idx != -1);
|
||||
} else if (!idx--) {
|
||||
// Background
|
||||
demonize = 1;
|
||||
if (optarg && pidfile) {
|
||||
fatal("Only one pidfile can be given");
|
||||
}
|
||||
if (optarg) {
|
||||
pidfile = strdup(optarg);
|
||||
}
|
||||
} else if (!idx--) {
|
||||
// Certificate
|
||||
if (!hasSSL) {
|
||||
warn("Ignoring certificate directory, as SSL support is unavailable");
|
||||
}
|
||||
if (certificateDir) {
|
||||
fatal("Only one certificate directory can be selected");
|
||||
}
|
||||
check(certificateDir = strdup(optarg));
|
||||
} else if (!idx--) {
|
||||
// Debug
|
||||
if (!logIsDefault() && !logIsDebug()) {
|
||||
fatal("--debug is mutually exclusive with --quiet and --verbose.");
|
||||
}
|
||||
verbosity = MSG_DEBUG;
|
||||
logSetLogLevel(verbosity);
|
||||
} else if (!idx--) {
|
||||
// Static file
|
||||
char *ptr, *path, *file;
|
||||
if ((ptr = strchr(optarg, ':')) == NULL) {
|
||||
fatal("Syntax error in static-file definition \"%s\".", optarg);
|
||||
}
|
||||
check(path = malloc(ptr - optarg + 1));
|
||||
memcpy(path, optarg, ptr - optarg);
|
||||
path[ptr - optarg] = '\000';
|
||||
file = strdup(ptr + 1);
|
||||
if (getRefFromHashMap(externalFiles, path)) {
|
||||
fatal("Duplicate static-file definition for \"%s\".", path);
|
||||
}
|
||||
addToHashMap(externalFiles, path, file);
|
||||
} else if (!idx--) {
|
||||
// Group
|
||||
if (runAsGroup >= 0) {
|
||||
fatal("Duplicate --group option.");
|
||||
}
|
||||
runAsGroup = parseGroup(optarg, NULL);
|
||||
} else if (!idx--) {
|
||||
// Numeric
|
||||
numericHosts = 1;
|
||||
} else if (!idx--) {
|
||||
// Port
|
||||
port = strtoint(optarg, 1, 65535);
|
||||
} else if (!idx--) {
|
||||
// Service
|
||||
struct Service *service;
|
||||
service = newService(optarg);
|
||||
if (getRefFromHashMap(services, service->path)) {
|
||||
fatal("Duplicate service description for \"%s\".", service->path);
|
||||
}
|
||||
addToHashMap(services, service->path, (char *)service);
|
||||
} else if (!idx--) {
|
||||
// Disable SSL
|
||||
if (!hasSSL) {
|
||||
warn("Ignoring disable-ssl option, as SSL support is unavailable");
|
||||
}
|
||||
enableSSL = 0;
|
||||
} else if (!idx--) {
|
||||
// Quiet
|
||||
if (!logIsDefault() && !logIsQuiet()) {
|
||||
fatal("--quiet is mutually exclusive with --debug and --verbose.");
|
||||
}
|
||||
verbosity = MSG_QUIET;
|
||||
logSetLogLevel(verbosity);
|
||||
} else if (!idx--) {
|
||||
// User
|
||||
if (runAsUser >= 0) {
|
||||
fatal("Duplicate --user option.");
|
||||
}
|
||||
runAsUser = parseUser(optarg, NULL);
|
||||
} else if (!idx--) {
|
||||
// Verbose
|
||||
if (!logIsDefault() && (!logIsInfo() || logIsDebug())) {
|
||||
fatal("--verbose is mutually exclusive with --debug and --quiet");
|
||||
}
|
||||
verbosity = MSG_INFO;
|
||||
logSetLogLevel(verbosity);
|
||||
} else if (!idx--) {
|
||||
// Version
|
||||
message("ShellInABox version " VERSION);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
if (optind != argc) {
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
char *buf = NULL;
|
||||
check(argc >= 1);
|
||||
for (int i = 0; i < argc; i++) {
|
||||
buf = stringPrintf(buf, " %s", argv[i]);
|
||||
}
|
||||
info("Command line:%s", buf);
|
||||
free(buf);
|
||||
|
||||
// If the user did not register any services, provide the default service
|
||||
if (!getHashmapSize(services)) {
|
||||
addToHashMap(services, "/", (char *)newService(":LOGIN"));
|
||||
}
|
||||
enumerateServices(services);
|
||||
deleteHashMap(services);
|
||||
|
||||
if (demonize) {
|
||||
pid_t pid;
|
||||
check((pid = fork()) >= 0);
|
||||
if (pid) {
|
||||
_exit(0);
|
||||
}
|
||||
setsid();
|
||||
if (pidfile) {
|
||||
int fd = NOINTR(open(pidfile,
|
||||
O_WRONLY|O_TRUNC|O_LARGEFILE|O_CREAT,
|
||||
0644));
|
||||
if (fd >= 0) {
|
||||
char buf[40];
|
||||
NOINTR(write(fd, buf, snprintf(buf, 40, "%d", (int)getpid())));
|
||||
NOINTR(close(fd));
|
||||
}
|
||||
}
|
||||
}
|
||||
free((char *)pidfile);
|
||||
}
|
||||
|
||||
int main(int argc, char * const argv[]) {
|
||||
// Disable core files
|
||||
prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
|
||||
|
||||
// Parse command line arguments
|
||||
parseArgs(argc, argv);
|
||||
|
||||
// Fork the launcher process, allowing us to drop privileges in the main
|
||||
// process.
|
||||
forkLauncher();
|
||||
dropPrivileges();
|
||||
|
||||
// Make sure that our timestamps will print in the standard format
|
||||
setlocale(LC_TIME, "POSIX");
|
||||
|
||||
// Create a new web server
|
||||
Server *server;
|
||||
check(server = newServer(port));
|
||||
serverEnableSSL(server, enableSSL);
|
||||
|
||||
// Enable SSL support (if available)
|
||||
if (enableSSL) {
|
||||
check(serverSupportsSSL());
|
||||
if (certificateDir) {
|
||||
char *tmp;
|
||||
if (strchr(certificateDir, '%')) {
|
||||
fatal("Invalid certificate directory name \"%s\".", certificateDir);
|
||||
}
|
||||
check(tmp = stringPrintf(NULL, "%s/certificate%%s.pem", certificateDir));
|
||||
serverSetCertificate(server, tmp, 1);
|
||||
free(tmp);
|
||||
} else {
|
||||
serverSetCertificate(server, "certificate%s.pem", 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Set log file format
|
||||
serverSetNumericHosts(server, numericHosts);
|
||||
|
||||
// Disable /quit handler
|
||||
serverRegisterHttpHandler(server, "/quit", NULL, NULL);
|
||||
|
||||
// Register HTTP handler(s)
|
||||
for (int i = 0; i < numServices; i++) {
|
||||
serverRegisterHttpHandler(server, services[i]->path,
|
||||
shellInABoxHttpHandler, services[i]);
|
||||
}
|
||||
|
||||
// Register handlers for external files
|
||||
iterateOverHashMap(externalFiles, registerExternalFiles, server);
|
||||
|
||||
// Start the server
|
||||
serverLoop(server);
|
||||
|
||||
// Clean up
|
||||
deleteServer(server);
|
||||
finishAllSessions();
|
||||
deleteHashMap(externalFiles);
|
||||
for (int i = 0; i < numServices; i++) {
|
||||
deleteService(services[i]);
|
||||
}
|
||||
free(services);
|
||||
free(certificateDir);
|
||||
info("Done");
|
||||
exit(0);
|
||||
}
|
560
shellinabox/shellinaboxd.man.in
Executable file
560
shellinabox/shellinaboxd.man.in
Executable file
|
@ -0,0 +1,560 @@
|
|||
'\" t
|
||||
.\" shellinaboxd.man -- Make command line applications available as AJAX web applications
|
||||
.\" Copyright (C) 2008 Markus Gutschke <markus@shellinabox.com>
|
||||
.\"
|
||||
.\" This program is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the GNU General Public License version 2 as
|
||||
.\" published by the Free Software Foundation.
|
||||
.\"
|
||||
.\" 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
.\"
|
||||
.\" In addition to these license terms, the author grants the following
|
||||
.\" additional rights:
|
||||
.\"
|
||||
.\" If you modify this program, or any covered work, by linking or
|
||||
.\" combining it with the OpenSSL project's OpenSSL library (or a
|
||||
.\" modified version of that library), containing parts covered by the
|
||||
.\" terms of the OpenSSL or SSLeay licenses, the author
|
||||
.\" grants you additional permission to convey the resulting work.
|
||||
.\" Corresponding Source for a non-source form of such a combination
|
||||
.\" shall include the source code for the parts of OpenSSL used as well
|
||||
.\" as that of the covered work.
|
||||
.\"
|
||||
.\" You may at your option choose to remove this additional permission from
|
||||
.\" the work, or from any part of it.
|
||||
.\"
|
||||
.\" It is possible to build this program in a way that it loads OpenSSL
|
||||
.\" libraries at run-time. If doing so, the following notices are required
|
||||
.\" by the OpenSSL and SSLeay licenses:
|
||||
.\"
|
||||
.\" This product includes software developed by the OpenSSL Project
|
||||
.\" for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
.\"
|
||||
.\" This product includes cryptographic software written by Eric Young
|
||||
.\" (eay@cryptsoft.com)
|
||||
.\"
|
||||
.\"
|
||||
.\" The most up-to-date version of this program is always available from
|
||||
.\" http://shellinabox.com
|
||||
.\"
|
||||
.TH SHELLINABOXD 1 "Dec 25, 2008"
|
||||
.SH NAME
|
||||
shellinaboxd \- publish command line shell through AJAX interface
|
||||
.SH SYNOPSIS
|
||||
.TP
|
||||
.B shellinaboxd
|
||||
[\ \fB-b\fP\ | \fB--background\fP[\fB=\fP\fIpidfile\fP]\ ]
|
||||
[\ \fB-c\fP\ | \fB--cert=\fP\fIcertdir\fP\ ]
|
||||
[\ \fB-d\fP\ | \fB--debug\fP\ ]
|
||||
[\ \fB-f\fP\ | \fB--static-file=\fP\fIurl\fP:\fIfile\fP\ ]
|
||||
[\ \fB-g\fP\ | \fB--group=\fP\fIgid\fP\ ]
|
||||
[\ \fB-h\fP\ | \fB--help\fP\ ]
|
||||
[\ \fB-n\fP\ | \fB--numeric\fP\ ]
|
||||
[\ \fB-p\fP\ | \fB--port=\fP\fIport\fP\ ]
|
||||
[\ \fB-s\fP\ | \fB--service=\fP\fIservice\fP\ ]
|
||||
[\ \fB-t\fP\ | \fB--disable-ssl\fP\ ]
|
||||
[\ \fB-q\fP\ | \fB--quiet\fP\ ]
|
||||
[\ \fB-u\fP\ | \fB--user\fP\ ]
|
||||
[\ \fB-v\fP\ | \fB--verbose\fP\ ]
|
||||
[\ \fB--version\fP\ ]
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.B shellinaboxd
|
||||
daemon implements a webserver that listens on the specified
|
||||
.IR port .
|
||||
The web server publishes one or more
|
||||
.I services
|
||||
that will be displayed in a VT100 emulator implemented as an AJAX web
|
||||
application. By default, the port is 4200 and the default service URL is
|
||||
.IR http://localhost:4200/ .
|
||||
.P
|
||||
If no particular
|
||||
.I service
|
||||
was requested, the server launches
|
||||
.B /bin/login
|
||||
querying the user for their username and password. It then starts the
|
||||
user's default login shell.
|
||||
.P
|
||||
Any modern JavaScript and CSS enabled browser will be able to access the
|
||||
published
|
||||
.I service
|
||||
without requiring additional plugins.
|
||||
.SH OPTIONS
|
||||
The following command line parameters control the operation of the daemon:
|
||||
.TP \w'\-b\ |\ 'u
|
||||
\fB-b\fP\ | \fB--background\fP[\fB=\fP\fIpidfile\fP]
|
||||
Launch
|
||||
.B shellinaboxd
|
||||
as a background daemon process. Optionally, write the process id to
|
||||
.IR pidfile .
|
||||
.TP
|
||||
\fB-c\fP\ |\ \fB--cert\fP\ \fIcertdir\fP
|
||||
If built with SSL/TLS support enabled, the daemon will look in
|
||||
.I certdir
|
||||
for any certificates. If unspecified, this defaults to the current
|
||||
working directory.
|
||||
|
||||
If the browser negotiated a
|
||||
.B Server Name Identification
|
||||
the daemon will look for a matching
|
||||
.I certificate-\f[BI]SERVERNAME\fP.pem
|
||||
file. This allows for virtual hosting of multiple server names on the
|
||||
same IP address and port.
|
||||
|
||||
If no
|
||||
.B SNI
|
||||
handshake took place, it falls back on using the certificate in the
|
||||
.I certificate.pem
|
||||
file.
|
||||
|
||||
The administrator should make sure that there are matching
|
||||
certificates for each of the virtual hosts on this server, and that
|
||||
there is a generic
|
||||
.I certificate.pem
|
||||
file.
|
||||
|
||||
If no suitable certificate is installed,
|
||||
.B shellinaboxd
|
||||
will attempt to invoke
|
||||
.B /usr/bin/openssl
|
||||
and create a new self-signed certificate. This only succeeds if, after
|
||||
dropping privileges,
|
||||
.B shell\%ina\%boxd
|
||||
has write permissions for
|
||||
.IR certdir .
|
||||
|
||||
Most browsers show a warning message when encountering a self-signed
|
||||
certificate and then allow the user the option of accepting the
|
||||
certificate. Due to this usability problem, and due to the perceived
|
||||
security implications, the use of auto-generated self-signed
|
||||
certificates is intended for testing or in intranet deployments, only.
|
||||
.TP
|
||||
\fB-d\fP\ |\ \fB--debug\fP
|
||||
enables debugging mode, resulting in lots of log messages on
|
||||
.IR stderr .
|
||||
This option is mutually exclusive with
|
||||
.B --quiet
|
||||
and
|
||||
.BR --verbose .
|
||||
.TP
|
||||
\fB-f\fP\ |\ \fB--static-file=\fP\fIurl\fP:\fIfile\fP
|
||||
The daemon serves various built-in resources from URLs underneath the
|
||||
.I service
|
||||
mount points. One or more
|
||||
.B --static-file
|
||||
options allow for overriding these resources with customized externally
|
||||
provided
|
||||
.IR files .
|
||||
The
|
||||
.I url
|
||||
can either be an absolute or a relative path. In the former case, it overrides
|
||||
exactly one built-in resource for one specific
|
||||
.IR service ,
|
||||
whereas in the latter case it overrides resources for each defined
|
||||
.IR service .
|
||||
|
||||
The following resources are available for customization:
|
||||
.RS
|
||||
.TP \w'ShellInABox.js\ \ \ 'u
|
||||
.B beep.wav
|
||||
audio sample that gets played whenever the terminal BEL is sounded.
|
||||
.TP
|
||||
.B favicon.ico
|
||||
favicon image file that is displayed in the browser's navigation bar.
|
||||
.TP
|
||||
.B ShellInABox.js
|
||||
JavaScript file implementing the AJAX terminal emulator.
|
||||
.TP
|
||||
.B styles.css
|
||||
CSS style file that controls the visual appearance of the terminal.
|
||||
.P
|
||||
It is not recommended to override the root HTML page for a particular
|
||||
.IR service .
|
||||
Instead, move the service to an anonymous URL and serve a
|
||||
.I static-file
|
||||
that references the
|
||||
.I service
|
||||
in an
|
||||
.IR <iframe> .
|
||||
|
||||
Instead of a
|
||||
.IR file ,
|
||||
it is possible to provide the name of a directory. This turns
|
||||
.B shellinaboxd
|
||||
into a simple web server that publishes all of the files in that
|
||||
particular directory. This option can be helpful when publishing a more
|
||||
complex root HTML page.
|
||||
.RE
|
||||
.TP
|
||||
\fB-g\fP\ |\ \fB--group=\fP\fIgid\fP
|
||||
When started as
|
||||
.BR root ,
|
||||
the server drops most privileges at start up. Unless overridden by the
|
||||
.B --group
|
||||
option, it switches to
|
||||
.BR nogroup .
|
||||
|
||||
When already running as an unprivileged user, the group remains
|
||||
unchanged, unless a change was explicitly requested with the
|
||||
.B --group
|
||||
option. On most UNIX systems, the latter is only possible, if the
|
||||
binary has been made
|
||||
.BR setuid-root .
|
||||
This is a non-standard configuration.
|
||||
|
||||
If running with SSL/TLS support enabled, the certificates must be
|
||||
accessible to the unprivileged user and/or group that the daemon
|
||||
runs as.
|
||||
.TP
|
||||
\fB-h\fP\ |\ \fB--help\fP
|
||||
Display a brief usage message showing the valid command line parameters.
|
||||
.TP
|
||||
\fB-n\fP\ |\ \fB--numeric\fP
|
||||
When running in
|
||||
.B --verbose
|
||||
mode, the daemon prints an
|
||||
.IR Apache -style
|
||||
log file to
|
||||
.IR stderr .
|
||||
By default, host names of peers get resolved
|
||||
before logging them. As DNS look-ups can be expensive, it is possible
|
||||
to request logging of numeric IP addresses, instead.
|
||||
.TP
|
||||
\fB-p\fP\ |\ \fB--port=\fP\fIport\fP
|
||||
Unless overridden by this option, the web server listens on port 4200
|
||||
for incoming HTTP and HTTPS requests.
|
||||
|
||||
.B shellinaboxd
|
||||
can distinguish between SSL/TLS requests and unencrypted requests. It
|
||||
also knows how to negotiate
|
||||
.B Server Name
|
||||
.BR Identification ,
|
||||
allowing the use of a single port for all types of requests even when
|
||||
virtual hosting.
|
||||
.TP
|
||||
\fB-s\fP\ |\ \fB--service=\fP\fIservice\fP
|
||||
One or more services can be registered on different URL paths:
|
||||
.in +4
|
||||
\fISERVICE\fP := <url-path> ':' \fIAPPLICATON\fP
|
||||
.in
|
||||
There is one pre-defined \fIapplication\fP, 'LOGIN'. It causes the
|
||||
daemon to invoke
|
||||
.B /bin/login
|
||||
to request the user's name and password, and to start his
|
||||
login shell. This is the default option, if no
|
||||
.B --service
|
||||
was defined. Starting
|
||||
.B /bin/login
|
||||
requires
|
||||
.B root
|
||||
privileges.
|
||||
|
||||
Alternatively, an \fIapplication\fP can be specified by providing a
|
||||
\fIuser\fP description, a working directory, and a command line:
|
||||
.in +4
|
||||
\fIAPPLICATION\fP := 'LOGIN' | \fIUSER\fP ':' \fICWD\fP ':' <cmdline>
|
||||
.in
|
||||
|
||||
The keyword 'AUTH' indicates that the \fIuser\fP information should
|
||||
be requested interactively, instead of being provided as part of the
|
||||
\fIservice\fP description:
|
||||
.in +4
|
||||
\fIUSER\fP := 'AUTH' | <username> ':' <groupname>
|
||||
.in
|
||||
|
||||
The working directory can either be given as an absolute path, or it
|
||||
can be the user's home directory:
|
||||
.in +4
|
||||
\fICWD\fP := 'HOME' : <dir>
|
||||
.in
|
||||
|
||||
The <cmdline> supports expansion of variables of the form ${VAR}.
|
||||
Supported variables are:
|
||||
.RS
|
||||
.TP \w'${columns}\ \ 'u
|
||||
.B ${columns}
|
||||
number of columns.
|
||||
.TP
|
||||
.B ${gid}
|
||||
numeric group id.
|
||||
.TP
|
||||
.B ${group}
|
||||
group name.
|
||||
.TP
|
||||
.B ${home}
|
||||
home directory.
|
||||
.TP
|
||||
.B ${lines}
|
||||
number of rows.
|
||||
.TP
|
||||
.B ${peer}
|
||||
name of remote peer.
|
||||
.TP
|
||||
.B ${uid}
|
||||
numeric user id.
|
||||
.TP
|
||||
.B ${user}
|
||||
user name.
|
||||
.P
|
||||
Other than the default environment variables of
|
||||
.BR $TERM ,
|
||||
.B $COLUMNS
|
||||
and
|
||||
.BR $LINES ,
|
||||
services can have environment variables passed to them, by preceding
|
||||
the <cmdline> with space separated variable assignments of the form
|
||||
.IR KEY = VALUE .
|
||||
|
||||
The <cmdline> supports single and double quotes, as well as
|
||||
backslashes for escaping characters in the familiar fashion.
|
||||
|
||||
Please note that when invoking
|
||||
.B shellinaboxd
|
||||
from a command line shell, additional quoting might be required to
|
||||
prevent the shell from expanding the variables prior to passing them
|
||||
to the daemon.
|
||||
|
||||
If no explicit
|
||||
.B --service
|
||||
has been requested,
|
||||
.B shellinaboxd
|
||||
defaults to attaching
|
||||
.B /bin/login
|
||||
to the root directory of the web server. This is equivalent to saying
|
||||
.BR --service=/:LOGIN .
|
||||
.RE
|
||||
.TP
|
||||
\fB-t\fP\ |\ \fB--disable-ssl\fP
|
||||
By default,
|
||||
.B shellinaboxd
|
||||
redirectes all incoming HTTP requests to their equivalent HTTPS
|
||||
URLs. If promoting of connections to encrypted SSL/TLS sessions is
|
||||
undesired, this behavior can be disabled.
|
||||
|
||||
This option is also useful during testing or for deployment in trusted
|
||||
intranets, if SSL certificates are unavailable.
|
||||
.TP
|
||||
\fB-q\fP\ |\ \fB--quiet\fP
|
||||
Surpresses all messages to
|
||||
.BR stderr .
|
||||
This option is mutually exclusive with
|
||||
.B --debug
|
||||
and
|
||||
.BR --verbose .
|
||||
.TP
|
||||
\fB-u\fP\ |\ \fB--user=\fP\fIuid\fP
|
||||
If started as
|
||||
.BR root ,
|
||||
the server drops privileges by changing to
|
||||
.BR nobody ,
|
||||
unless the
|
||||
.I uid
|
||||
has been overridden by this option.
|
||||
|
||||
For more details, refer to the description of the
|
||||
.B --group
|
||||
option.
|
||||
.TP
|
||||
\fB-v\fP\ |\ \fB--verbose\fP
|
||||
Enables logging of
|
||||
.IR Apache -style
|
||||
log file to
|
||||
.IR stderr .
|
||||
This option is mutually exclusive with
|
||||
.B --debug
|
||||
and
|
||||
.BR --quiet .
|
||||
.TP
|
||||
\fB--version\fP
|
||||
Prints the version number of the binary and exits.
|
||||
.SH CONFIGURATION
|
||||
There are no configuration files or permanent settings for
|
||||
.BR shell\%ina\%boxd .
|
||||
A small number of run-time configuration options are available from a
|
||||
context menu that becomes available when clicking the right mouse button.
|
||||
.SH EXAMPLES
|
||||
.TP \w'shellinaboxd\ 'u
|
||||
.B shellinaboxd -t
|
||||
Attaches a web-enabled login shell to
|
||||
.I http://localhost:4200/
|
||||
with SSL/TLS support disabled. This command must be run as
|
||||
.B root
|
||||
or the attempt to launch
|
||||
.I /bin/login
|
||||
will fail.
|
||||
.TP
|
||||
.B shellinaboxd -t -f beep.wav:/dev/null
|
||||
Runs all services with the audible-bell permanently disabled.
|
||||
.TP
|
||||
.B shellinaboxd -t -s /:AUTH:HOME:/bin/bash
|
||||
Interactively request the user's name and password prior to launching
|
||||
a Bourne shell. This command can be run by unprivileged users. But if
|
||||
doing so, it only allows this particular user to log in.
|
||||
.TP
|
||||
.B shellinaboxd -c certificates -g shellinaboxd
|
||||
If the
|
||||
.B certificates
|
||||
directory exists and is writable by the
|
||||
.B shellinaboxd
|
||||
group, self-signed SSL certificates will be generated in this
|
||||
directory. Running this command as
|
||||
.B root
|
||||
allows any user on the system to log in at
|
||||
.BR http://localhost:4200/ .
|
||||
Sessions will automatically be promoted to SSL/TLS.
|
||||
.TP
|
||||
.B shellinaboxd -t -s /:LOGIN -s /who:nobody:nogroup:/:w
|
||||
In addition to the login shell at
|
||||
.IR http://localhost:4200 ,
|
||||
show a list of currently logged in users when accessing
|
||||
.IR http://localhost:4200/who .
|
||||
This command must be run as
|
||||
.B root
|
||||
in order to be able to change to
|
||||
.B nobody:nogroup
|
||||
as requested by the service description.
|
||||
.TP
|
||||
.B shellinaboxd -t -s '/:root:root:/:wy60 -c /bin/login'
|
||||
Instead of the standard
|
||||
.B ANSI/VT100
|
||||
terminal, publish a
|
||||
.B Wyse 60\*(Tm
|
||||
terminal. Again, this command should be run as
|
||||
.BR root .
|
||||
.P
|
||||
.SH DIAGNOSTICS
|
||||
The daemon returns a non-zero exit code in case of failure. With the
|
||||
exception of a small number of common error cases that are handled
|
||||
explicitly, most errors result in printing a
|
||||
.I \(dqCheck failed\(dq
|
||||
message. This does not typically indicate a bug in the program but is
|
||||
instead its normal way of reporting errors.
|
||||
|
||||
Common failure conditions are reusing a port that is already in use,
|
||||
lack of sufficient privileges to run a service, failure to find
|
||||
SSL/TLS certificates, and failure to write newly generated
|
||||
certificates to the certification directory.
|
||||
.SH "SEE ALSO"
|
||||
.BR chmod (1),
|
||||
.BR last (1),
|
||||
.BR login (1),
|
||||
.BR sh (1),
|
||||
.BR shells (5),
|
||||
.BR openssl (1SSL),
|
||||
.BR w (1),
|
||||
.BR wy60 (1),
|
||||
.BR xterm (1).
|
||||
.SH SECURITY
|
||||
The daemon uses privilege separation techniques to allow it to drop
|
||||
privileges early. It is aware of setuid flags and restricts some
|
||||
operations when launched as a setuid application.
|
||||
|
||||
Despite these safety features, a bug could conceivably lead to a
|
||||
determined attacker gaining elevated privileges. It is therefore
|
||||
strongly discouraged to set the setuid flag on the binary.
|
||||
|
||||
The expected deployment would be from a system
|
||||
.I rc
|
||||
script launched by
|
||||
.BR /sbin/init .
|
||||
For extra security, the
|
||||
.B --group
|
||||
and
|
||||
.B --user
|
||||
options should be used to change to a dedicated user.
|
||||
.SH AUTHOR
|
||||
Copyright (C) 2008 by Markus Gutschke
|
||||
.RI < "markus@shellinabox.com" >.
|
||||
.P
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation.
|
||||
.P
|
||||
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.
|
||||
.P
|
||||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
USA
|
||||
.P
|
||||
In addition to these license terms, the author grants the following
|
||||
additional rights:
|
||||
.P
|
||||
If you modify this program, or any covered work, by linking or
|
||||
combining it with the OpenSSL project's OpenSSL library (or a
|
||||
modified version of that library), containing parts covered by the
|
||||
terms of the OpenSSL or SSLeay licenses, the author
|
||||
grants you additional permission to convey the resulting work.
|
||||
Corresponding Source for a non-source form of such a combination
|
||||
shall include the source code for the parts of OpenSSL used as well
|
||||
as that of the covered work.
|
||||
.P
|
||||
You may at your option choose to remove this additional permission from
|
||||
the work, or from any part of it.
|
||||
.P
|
||||
If you would like to negotiate different licensing terms that are
|
||||
compatible for integration with other projects, please contact the
|
||||
author.
|
||||
#ifdef HAVE_OPENSSL
|
||||
.P
|
||||
If the OpenSSL
|
||||
system libraries can be found at run-time, they will be invoked by
|
||||
.B shellinaboxd
|
||||
to provide SSL/TLS support. The OpenSSL and SSLeay
|
||||
licenses require the following notices:
|
||||
.P
|
||||
This product includes software developed by the OpenSSL Project
|
||||
for use in the OpenSSL Toolkit. (http://www.openssl.org/)
|
||||
.P
|
||||
This product includes cryptographic software written by Eric Young
|
||||
(eay@cryptsoft.com)
|
||||
#endif
|
||||
.SH BUGS
|
||||
Due to browser limitations, some features might not be available to
|
||||
users of all browers.
|
||||
.P
|
||||
Konqueror does not allow for reliable interception of
|
||||
.I CTRL
|
||||
keys. If you press a key together with the
|
||||
.I CTRL
|
||||
modifier, it continues performing the browser's predefined behavior for
|
||||
this particular key combination. In most cases, it also fails to report
|
||||
the correct key to the terminal emulator. As a work-around, pressing
|
||||
both the
|
||||
.I CTRL
|
||||
and the
|
||||
.I WINDOWS
|
||||
key sometimes works.
|
||||
.P
|
||||
Some browsers, most notably IE on Windows, disallow interception of
|
||||
.I ALT
|
||||
keys and always interpret these keys as menu accelerators. As a
|
||||
work-around, many UNIX applications allow pressing
|
||||
.IR ESC ,
|
||||
instead of
|
||||
.IR ALT .
|
||||
.P
|
||||
When using non-US keyboard layouts, some browser do not allow for
|
||||
reliably determining shifted
|
||||
.I ALT
|
||||
keys. Please report these cases if they turn out to be a problem, as
|
||||
work-arounds might be possible.
|
||||
.P
|
||||
Access to the native clipboard is typically not possible. Instead, an
|
||||
internal clipboard accessible from the right-button context menu is used
|
||||
for all but IE.
|
||||
.P
|
||||
Some browsers restrict the number of concurrent connections to a
|
||||
server. This restricts how many AJAX terminals can be opened
|
||||
simultaneously. If this becomes a problem, users can typically
|
||||
reconfigure their browsers to raise the limit.
|
138
shellinabox/styles.css
Normal file
138
shellinabox/styles.css
Normal file
|
@ -0,0 +1,138 @@
|
|||
#vt100 #reconnect {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#vt100 #reconnect input {
|
||||
padding: 1ex;
|
||||
font-weight: bold;
|
||||
font-size: x-large;
|
||||
}
|
||||
|
||||
#vt100 pre {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
#vt100 #scrollable {
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
position: relative;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
#vt100 #console, #vt100 #alt_console, #vt100 #cursor {
|
||||
font-family: "DejaVu Sans Mono", "Everson Mono", FreeMono, "Andale Mono", "Lucida Console", monospace;
|
||||
}
|
||||
|
||||
#vt100 #cursor {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
overflow: hidden;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#vt100 #console div, #vt100 #alt_console div {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#vt100 #cursor.bright {
|
||||
background-color: #e60000;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#vt100 #cursor.dim {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#vt100 #cursor.inactive {
|
||||
border: 1px solid #e60000;
|
||||
margin: -1px;
|
||||
}
|
||||
|
||||
#vt100 #padding {
|
||||
visibility: hidden;
|
||||
width: 1px;
|
||||
height: 0px;
|
||||
}
|
||||
|
||||
#vt100 .hidden {
|
||||
position: absolute;
|
||||
top: -10000px;
|
||||
left: -10000px;
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
}
|
||||
|
||||
#vt100 #menu {
|
||||
overflow: visible;
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
#vt100 #menu .popup {
|
||||
background-color: #EEEEEE;
|
||||
border: 1px solid black;
|
||||
font-family: sans-serif;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#vt100 #menu .popup ul {
|
||||
list-style-type: none;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
min-width: 10em;
|
||||
}
|
||||
|
||||
#vt100 #menu .popup li {
|
||||
padding: 3px 0.5ex 3px 0.5ex;
|
||||
}
|
||||
|
||||
#vt100 #menu .popup li.hover {
|
||||
background-color: #444444;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#vt100 #menu .popup li.disabled {
|
||||
color: #AAAAAA;
|
||||
}
|
||||
|
||||
#vt100 #menu .popup hr {
|
||||
margin: 0.5ex 0px 0.5ex 0px;
|
||||
}
|
||||
|
||||
#vt100 #ansi0 { background-color: #000000; }
|
||||
#vt100 #ansi1 { background-color: #cd0000; }
|
||||
#vt100 #ansi2 { background-color: #00cd00; }
|
||||
#vt100 #ansi3 { background-color: #cdcd00; }
|
||||
#vt100 #ansi4 { background-color: #0000ee; }
|
||||
#vt100 #ansi5 { background-color: #cd00cd; }
|
||||
#vt100 #ansi6 { background-color: #00cdcd; }
|
||||
#vt100 #ansi7 { background-color: #e5e5e5; }
|
||||
#vt100 #ansi8 { background-color: #7f7f7f; }
|
||||
#vt100 #ansi9 { background-color: #ff0000; }
|
||||
#vt100 #ansi10 { background-color: #00ff00; }
|
||||
#vt100 #ansi11 { background-color: #e8e800; }
|
||||
#vt100 #ansi12 { background-color: #5c5cff; }
|
||||
#vt100 #ansi13 { background-color: #ff00ff; }
|
||||
#vt100 #ansi14 { background-color: #00ffff; }
|
||||
#vt100 #ansi15 { background-color: #ffffff; }
|
||||
|
||||
@media print {
|
||||
#vt100 .scrollback {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#vt100 #reconnect, #vt100 #cursor, #vt100 #menu {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#vt100 #scrollable {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#vt100 #console, #vt100 #alt_console {
|
||||
overflow: hidden;
|
||||
width: 1000000ex;
|
||||
}
|
||||
}
|
3063
shellinabox/vt100.js
Normal file
3063
shellinabox/vt100.js
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue