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:
zodiac 2008-12-29 23:57:07 +00:00
parent ed80ce65c7
commit aab20f5ed0
66 changed files with 54468 additions and 0 deletions

1
AUTHORS Normal file
View file

@ -0,0 +1 @@
markus@shellinabox.com

39
COPYING Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

3
NEWS Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

1526
config.guess vendored Normal file

File diff suppressed because it is too large Load diff

1658
config.sub vendored Normal file

File diff suppressed because it is too large Load diff

21423
configure vendored Executable file

File diff suppressed because it is too large Load diff

13
configure.ac Normal file
View 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
View 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
View 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
View file

@ -0,0 +1 @@
4

15
debian/control vendored Normal file
View 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
View 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
View file

@ -0,0 +1,7 @@
AUTHORS
COPYING
ChangeLog
INSTALL
NEWS
README
TODO

114
debian/rules vendored Executable file
View 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
View 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
View file

@ -0,0 +1,2 @@
usr/bin
usr/share/man/man1

106
debian/shellinabox.init vendored Executable file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

145
libhttp/httpconnection.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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="&quot;&#13;&#10;X: x=&quot;">
// <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
View 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
View 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
View 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__ */

6964
ltmain.sh Normal file

File diff suppressed because it is too large Load diff

367
missing Normal file
View 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

Binary file not shown.

260
shellinabox/externalfile.c Normal file
View 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;
}

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

986
shellinabox/launcher.c Normal file
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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__ */

View 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:/) ? '&#10004; ' : '') + '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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff