Compare commits

..

88 commits

Author SHA1 Message Date
simono41
82caba2f37 fix 2017-09-14 15:05:15 +02:00
simono41
e73021f2f3 fix 2017-09-14 14:57:56 +02:00
Your Name
6b5862fba1 fix 2017-06-04 01:06:16 +02:00
Your Name
50fba1b2f1 fix 2017-06-04 01:02:11 +02:00
Your Name
15de28e37b Merge branch 'master' of https://github.com/simono41/shellinabox 2017-05-24 18:55:15 +02:00
Your Name
d3a93f8081 fix 2017-05-24 18:54:50 +02:00
Your Name
39b42fb6e4 fix 2017-05-24 11:12:16 +00:00
Simon Rieger
1510811a68 bugfix 2017-01-19 16:02:54 +01:00
Simon Rieger
e7ba13e13d Update shellinabox.service 2017-01-14 11:15:16 +01:00
Simon Rieger
0edee2c12c Update README.md 2017-01-14 11:14:28 +01:00
Simon Rieger
ae78622a58 Update README.md 2017-01-14 11:06:07 +01:00
Simon Rieger
81695461b4 fixing 2017-01-14 11:02:37 +01:00
Simon Rieger
5c5c01ed10 Update auto.sh 2017-01-11 14:18:53 +01:00
Simon Rieger
08d6a40b85 webssh 2017-01-11 13:45:10 +01:00
Simon Rieger
456303fce5 auto script 2017-01-11 13:44:13 +01:00
Marc Singer
5c7fb5cde2 New release with bug fixes.
o Especially important is the OpenSSL fix.
2016-11-09 11:40:33 -08:00
Luka Krajger
05b2d3630c Issue #361: enable builds with MUSL libc
* Added configure option "--disable-utmp" which must be used when
  building with MUSL libc. This option disables login records which
  are stubed out in MUSL anyway.
* Added missing include "sys/ttydefaults.h".
2016-10-11 13:22:52 +02:00
Luka Krajger
d0d8c58882 Issue #384: compatibility with OpenSSL 1.1.0
* Direct usage of BIO struct members is removed for new versions of
  OpenSSL.
* Workaround for double BIO free in SSL_free() was updated to work
  with new and old OpenSSL versions.
* Note that this patch only fixes compatibilty when building with
  configure option "--disable-runtime-loading" (like it is done
  for Debia package.).
2016-10-08 16:10:53 +02:00
Luka Krajger
d4bd77ca45 Added "ready" event for iframe message passing
* When shellinabox is ready it sends "ready" message to parent window.
* Example file was updated with new use case.
2016-09-26 13:16:18 +02:00
Luka Krajger
8e28bb4c2a Merge pull request #391 from tomtor/freebsd
Fix a debug check on FreeBSD and probably Linux for HTTP connection termination.
2016-09-26 12:37:44 +02:00
Tom Vijlbrief
f408467088 Fix a debug check on FreeBSD and probably Linux
On FreeBSD 12.0 I get within a minute:

Check failed at libhttp/httpconnection.c:433 in destroyHttpConnection(): !close(http->fd)

See also:

https://github.com/shellinabox/shellinabox/issues/389

I assume that close() fails with ECONNRESET and that we should just check
for errno != EBADF, which should also be OK for Linux and other systems,
which might return EIO in some conditions.

Linux close(2) manual page:

	EBADF	fd isn't a valid open file descriptor.
	EINTR	The close() call was interrupted by a signal; see signal(7).
	EIO	An I/O error occurred.

FreeBSD close(2) manual page:

     [EBADF]            The fd argument is not an active descriptor.
     [EINTR]            An interrupt was received.
     [ENOSPC]           The underlying object did not fit, cached data was
                        lost.
     [ECONNRESET]       The underlying object was a stream socket that was
                        shut down by the peer before all pending data was
                        delivered.

     In case of any error except EBADF, the supplied file descriptor is
     deallocated and therefore is no longer valid.
2016-09-11 12:31:47 +02:00
Luka Krajger
e6c25e84bc Issue #381: Fixed segfaults at logging
* This patch correctly handles varargs being used two times in the
  same function.
2016-06-06 16:11:11 +02:00
Luka Krajger
cbac76e579 Merge pull request #380 from blakemcbride/master
Spelling and grammar corrections to README.md
2016-05-29 07:37:11 -04:00
Blake McBride
6e475e9686 Spelling and grammar corrections to README.md 2016-05-28 18:59:45 -05:00
KLuka
af162e282a Added shellinaboxd.pdf make target 2016-05-24 19:27:06 +02:00
Luka Krajger
78bed3070b Merge pull request #374 from blakemcbride/master
Changes to support APL characters (unicode)
2016-05-19 10:46:19 -04:00
Blake McBride
863a8d91c7 Changes to support APL characters 2016-05-13 19:15:15 -05:00
KLuka
628d41f32c Added checks and fixes code style 2016-05-06 11:21:14 +02:00
Luka Krajger
e05c6d8178 Merge pull request #372 from StefanAlexandruBogdan/master
free the sshPort to prevent memory leak and indent newly added code to preserve code styling
2016-05-05 16:18:05 -04:00
Alexandru Bogdan Stefan
fb262fb521 free the sshPort to prevent memory leak 2016-05-05 15:31:30 +03:00
Alexandru Bogdan Stefan
1558412cee Ident variable assignments to respect coding style 2016-05-05 15:28:55 +03:00
Luka Krajger
0f1e9ba31c Merge pull request #370 from StefanAlexandruBogdan/master
Add support for using ShellInABox with a custom port in SSH service.
2016-05-04 06:30:26 -04:00
Alexandru Bogdan Stefan
d34d5db9d9 Add support for using ShellInABox with a custom SSH port 2016-04-21 19:14:50 +03:00
Luka Krajger
048cecd2e7 Merge pull request #367 from a-detiste/master
typos
2016-03-09 13:36:54 -05:00
Alexandre Detiste
c8b6a3eddb typos 2016-03-09 19:30:26 +01:00
Luka Krajger
25425bad31 Merge pull request #366 from schoonc/fixtypo
fixes typo: ESVTETitle -> ESVTEtitle

Fixes the patch for #360.
2016-03-09 12:19:26 -05:00
Сорокин Александр
30e293d602 fixes typo: ESVTETitle -> ESVTEtitle 2016-03-09 19:26:25 +03:00
Luka Krajger
ee57908b49 Merge pull request #363 from tomtor/IE
adjust scale on IE

On IE 11 the computed width (nr of columns) is too large.
2016-03-01 03:22:11 -05:00
Luka Krajger
b778806356 Merge pull request #365 from mvanholsteijn/disable-peer-check
Disable peer check (issue #364)
2016-02-26 06:45:06 -05:00
Mark van Holsteijn
8fd68e147c add option --disable-peer-check to usage help text 2016-02-26 00:24:33 +01:00
Mark van Holsteijn
2034ae1ee5 option to disable the peer check when running behind AWS ELB 2016-02-26 00:01:09 +01:00
Tom Vijlbrief
490cfa0344 adjust scale on IE 2016-02-25 14:47:16 +01:00
Luka Krajger
f17bc266f8 Merge pull request #362 from druzus/master
2016-02-23 23:18 UTC+0100 Przemyslaw Czerpak (druzus/at/poczta.onet.pl)

Edit and function keys pressed with CTRL, ALT or SHIFT modifiers generated wrong key sequences.
2016-02-24 07:04:21 -05:00
Przemysław Czerpak
790d578cc2 2016-02-23 23:18 UTC+0100 Przemyslaw Czerpak (druzus/at/poczta.onet.pl)
* shellinabox/vt100.jspp
    ! fixed key modifiers encoding.
      They should be calculated as bitfield for
         SHIFT = 1
         ALT   = 2
         CTRL  = 4
      and then incremented by 1.
      ref:
         http://www.xfree86.org/4.7.0/ctlseqs.html#PC-Style%20Function%20Keys
         http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-PC-Style-Function-Keys
2016-02-23 23:18:12 +01:00
KLuka
0c8c295c1a Issue #360: ignore VTE OSC6 and OSC7 escape sequences
* This patch ignores VTE OSC6 and OSC6 escape sequences, so that the
  current directory/file are not displayed in front of the shell prompt.
2016-01-23 16:17:17 +01:00
KLuka
141e641238 Raised version from 2.19 to 2.20 2016-01-23 16:03:32 +01:00
Luka Krajger
2c93404bd0 Merge pull request #357 from KLuka/issue-222
Issue #222: LOGIN service (can't reopen tty)
2016-01-04 10:56:18 -05:00
KLuka
4911d0d39c Issue #222: LOGIN service (can't reopen tty)
* Workaround for random "Session closed" issues related to /bin/login
  closing and reopening our pty during initialization. This happens only
  on some systems like Fedora for example. Now we allow that our pty is
  closed by ignoring POLLHUP on first read. Delay is also needed so that
  login process has some time to reopen pty.
* Note that the issue may occur anyway but with this workaround we reduce
  the chances.
2015-12-28 15:24:49 +01:00
Luka Krajger
d8ef7dad3c Merge pull request #356 from triska/master
fix typo: HTTPS --> HTTP
2015-12-11 09:28:34 +01:00
Markus Triska
dd9f1f01d1 fix typo: HTTPS --> HTTP 2015-12-10 20:03:44 +01:00
Marc Singer
1a8010f2c9 Changelog update before release. 2015-12-05 10:31:36 -08:00
KLuka
8a68194da2 Updated version to 2.19 2015-12-04 18:17:22 +01:00
KLuka
e026df75b0 Fixed spelling errors reported by lintian 2015-12-04 18:14:51 +01:00
KLuka
4aa0eb97e4 Disable HTTP fallback via "/plain" URL (CVE-2015-8400)
* Disabled all methods of HTTP fallback when HTTPS is enabled. This
  is enforced on server side so that even modified client code (JS)
  can not redirect client from HTTPS to HTTP, like it was possible
  before (issue #355).
* Current solution unfortunately also disables automatic upgrade from
  HTTP to HTTPS (when available), since all non-SSL connections are
  droped immediately.
2015-12-03 17:47:26 +01:00
KLuka
aaa00551bf Issue #119, #312, #354: Soft keyboard icon
* Added logic that enables soft keyboard icon by default on some
  clients like Kindle, PS Vita, iPad, ...
2015-11-17 19:43:22 +01:00
KLuka
106bc0aa85 Issue #354: iOS client compatibility
* Added more iOS clients that should use workaround to prevent ever
  groving console.
2015-11-17 19:36:10 +01:00
KLuka
f67073d33e Issue #350: Support for middle click paste
* Added limited support for middle click pasting. For most browsers
  and operating systems middle click pasting works only for concent
  selected in current shellinabox window.
2015-10-20 20:40:20 +02:00
Marc Singer
c87588613a Update version for release. 2015-10-10 10:54:36 -07:00
KLuka
e30c33d323 Issue #347: Added dummy release in Debian changelog
* This is needed so that manualy built Debian packages will contain
  latest version.
2015-09-17 10:39:43 +02:00
KLuka
973f1527bd Updated preview image in README 2015-09-06 21:32:13 +02:00
KLuka
cde2e92378 Issue #341: Fixed reverse video rendering
* Added new CSS class for handling reverse video with default terminal
  colors. For colors given with value 0-255 background and foreground
  values are just switched.
* New CSS classes were also added to Black On White and White On Black
  color themes.
2015-09-03 19:01:48 +02:00
KLuka
7dd9d4300c Minor improvements
* Use stdout for usage and version information. Patch taken from
  issue #344.
* Removed automatic usage display when command line parsing fails.
* Added version information in debug output.
2015-09-03 19:00:16 +02:00
KLuka
b58542eb99 Added CGI session key in HTTP response header
* Session key is returned in first HTTP response if CGI mode is used.
  Header filed is named 'X-ShellInABox-Session'. This can be used by
  some special applications that need unique token.
2015-09-03 18:04:15 +02:00
KLuka
8d3c5cdc3d Raised version to 2.19 2015-09-01 13:13:13 -04:00
Benji Wiebe
09e790bb27 Added config.cache to gitignore 2015-09-01 13:06:21 -04:00
KLuka
b828574899 Issue #103: Child process cleanup under PAM session
* Added signal handling in PAM session process. Now SIGHUP signals are
  forward to child process, which is the actual service. Launcher process
  uses this type of signals to terminate service on http connection
  timeout.
2015-08-30 22:48:14 +02:00
Luka Krajger
eb2968b1d0 Merge pull request #340 from BenjiWiebe/master
Added autoconf/automake stuff for compatibility with older auto* versions
2015-08-30 19:10:04 +02:00
Benji Wiebe
36f512cc63 Added explanatory comments 2015-08-28 23:42:09 -05:00
Benji Wiebe
284265651b Added autoconf/automake stuff for compatibility with older auto* versions 2015-08-28 23:15:13 -05:00
Luka Krajger
0d522a05ca Merge pull request #339 from BenjiWiebe/master
Added --disable-utmp-logging option
2015-08-28 20:44:16 +02:00
Benji Wiebe
3ff0ad5768 Added --disable-utmp-logging option 2015-08-27 23:16:06 -05:00
Luka Krajger
6f30739e33 Merge pull request #338 from BenjiWiebe/master
Kill children with HUP instead of TERM at end of session
2015-08-27 23:49:37 +02:00
Benji Wiebe
7f5064efcd Reset sigaction for SIGHUP to default in child 2015-08-27 14:08:39 -05:00
Benji Wiebe
145abf1fcc Kill children with HUP instead of TERM at end of session 2015-08-27 13:26:42 -05:00
KLuka
48a65d6bcb Fixed handling of large HTTP requests
* Protection against large HTTP requests was fixed by adding some null
  pointer checks. Too large HTTP requests are now correctly handled by
  returning error code and closing connection.
2015-08-26 23:27:06 +02:00
KLuka
6c9f98bf34 Logging and debuging
* Added prefixes to all log messages. Prefix should describe source of
  message, like "config", "http", "ssl", "server", etc... This should
  give users more info to figure out what went wrong or what is going
  on. Prefixes also make automatic processing easier.
* Usage is not displayed by default when given command line options are
  incorrect. This way it is easier to notice actual error.
2015-08-23 19:25:36 +02:00
KLuka
d74e60b6a7 Added system logging for important errors
* Messages with "fatal" or "error" log level are now also passed to
  syslogd service with help of vsyslog() function.
* On systems that use syslog service, these messages will be available
  in default system log files like /var/log/syslog or /var/log/messages.
2015-08-21 18:08:11 +02:00
KLuka
dfd885c011 Raised version 2015-08-21 17:44:06 +02:00
Marc Singer
acba554b6b Package release commit. 2015-08-07 20:53:15 -07:00
Luka Krajger
02838e530f Merge pull request #332 from KLuka/ssl
SSL patches
2015-08-06 18:22:07 +02:00
KLuka
1f54ff5f71 Added prefix to SSL related debug messages 2015-08-06 18:11:32 +02:00
KLuka
eacb2fcb81 Disable secure client-initiated renegotiation
* In case that this SSL feature is abused it is possible to overload the
  server. Other web servers disable this feature by default, but users
  are able to change it with configuration. This is not possible with
  shellinabox as this feature is not needed.
* Solution was implemented similary as in Lighttpd web server.
2015-08-06 18:06:11 +02:00
KLuka
f0437832d3 Added support for Perfect Forward Secrecy (#331)
* Support for PFS is enabled with help of chiper suits that use ECDHE
  key exchange. OpenSSL added support for eliptic curve operations (EC)
  in version 0.9.8. Note that there are also some library distributions
  which don't support EC operations.
* Added precompiler guards for builds with OpenSSL older than 0.9.8 and
  builds with '--enable-runtime-loading' configure script option.
* Cleaned up some SSL related code.
2015-08-05 17:57:05 +02:00
KLuka
477818e088 Fixed broken visual bell style in default CSS 2015-07-27 20:08:47 +02:00
KLuka
7cc877cdd8 Clean up build and lintian warnings (#328)
* Added wrapper macros to suppress compiler warnings about unused return
  values of setres*id() functions. We don't need checks at that point
  as it does't affect our program.
* Added marco in configure.ac script to overwrite default AR_FLAGS,which
  were causing build warnings.
* Removed debian/watch file as is not needed anymore, because now this
  is native Debian package.
2015-07-27 19:57:19 +02:00
Marc Singer
8ac3a4efcf Release to guarantee upgrade. 2015-07-24 11:54:39 -07:00
Marc Singer
7794fa4f64 Merge remote-tracking branch 'refs/remotes/origin/master' 2015-07-24 09:44:50 -07:00
KLuka
9dcef5688f Added Github url in context menu "About..." popup 2015-07-24 16:08:05 +02:00
91 changed files with 969 additions and 384 deletions

1
.gitignore vendored Normal file → Executable file
View file

@ -9,6 +9,7 @@ aclocal.m4
autom4te.cache autom4te.cache
certificate.pem certificate.pem
compile compile
config.cache
config.guess config.guess
config.h config.h
config.h.in config.h.in

0
AUTHORS Normal file → Executable file
View file

0
CHANGELOG.md Normal file → Executable file
View file

0
COPYING Normal file → Executable file
View file

0
GPL-2 Normal file → Executable file
View file

2
INSTALL.Debian Normal file → Executable file
View file

@ -41,7 +41,7 @@ you point your browser to the site.
At the very least, you need a file called "certificate.pem" that includes At the very least, you need a file called "certificate.pem" that includes
both the private key and the public certificate in PEM (i.e. ASCII) format. both the private key and the public certificate in PEM (i.e. ASCII) format.
Additionally, you should create files for each of the publically visible Additionally, you should create files for each of the publicly visible
hostnames that your machine has. These are named "certificate-${FQDN}.pem". hostnames that your machine has. These are named "certificate-${FQDN}.pem".
You probably also want "certificate-localhost.pem". You probably also want "certificate-localhost.pem".

11
Makefile.am Normal file → Executable file
View file

@ -127,6 +127,8 @@ BUILT_SOURCES = shellinabox/beep.h \
shellinaboxd_LDADD = liblogging.la \ shellinaboxd_LDADD = liblogging.la \
libhttp.la libhttp.la
shellinaboxd_LDFLAGS = -static shellinaboxd_LDFLAGS = -static
## Added this for compatibility with older versions of autoconf/automake
docdir = ${datadir}/doc/${PACKAGE}
symbolname = \ symbolname = \
sed -e 's/.*\/\([^.]*\)[.].*/\1/' \ sed -e 's/.*\/\([^.]*\)[.].*/\1/' \
@ -240,7 +242,8 @@ shellinaboxd.1: ${top_srcdir}/shellinabox/shellinaboxd.man.in \
clean-local: clean-demo clean-local: clean-demo
-rm -rf shellinaboxd.1 \ -rm -rf shellinaboxd.1 \
shellinaboxd.ps shellinaboxd.ps \
shellinaboxd.pdf
-rm -rf debian/shellinabox \ -rm -rf debian/shellinabox \
debian/shellinabox*.debhelper* \ debian/shellinabox*.debhelper* \
debian/shellinabox.substvars \ debian/shellinabox.substvars \
@ -362,3 +365,9 @@ clean-demo:
"(int)sizeof($${sym}Start);"; \ "(int)sizeof($${sym}Start);"; \
} >"$@" } >"$@"
shellinaboxd.ps: shellinaboxd.1
groff -man -T ps $^ >$@
shellinaboxd.pdf: shellinaboxd.ps
ps2pdf $^ $@

0
NEWS Normal file → Executable file
View file

0
README Normal file → Executable file
View file

0
README.Fedora Normal file → Executable file
View file

59
README.md Normal file → Executable file
View file

@ -6,16 +6,16 @@ shellinabox
[![Join the chat at https://gitter.im/shellinabox/shellinabox](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/shellinabox/shellinabox?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/shellinabox/shellinabox](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/shellinabox/shellinabox?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
This is unofficial fork of project **Shell In A Box**. Fork was created because This is an unofficial fork of the project **Shell In A Box**. The fork was created because
original project is not maintained anymore and we cannot contact original the original project was not maintained anymore and we cannot contact the original
repository owners. repository owners.
Our aim is to continue with maintanince of shellinabox project. For list of Our aim is to continue with maintenance of the shellinabox project. For a list of
recent changes please see [CHANGELOG.md](/CHANGELOG.md). recent changes, please see [CHANGELOG.md](/CHANGELOG.md).
If you have any questions, issues or patches, please fell free to submit pull If you have any questions, issues, or patches, please feel free to submit a pull
request or report an issue. You can also drop an email to original project request or report an issue. You can also drop an email to the original project
[issue #261](https://code.google.com/p/shellinabox/issues/detail?id=261) discusion [issue #261](https://code.google.com/p/shellinabox/issues/detail?id=261) discussion
from where this fork started. from where this fork started.
@ -27,7 +27,7 @@ tools to a web based terminal emulator. This emulator is accessible to any
JavaScript and CSS enabled web browser and does not require any additional JavaScript and CSS enabled web browser and does not require any additional
browser plugins. browser plugins.
![Shell In A Box preview](/misc/preview.png?raw=true) ![Shell In A Box preview](/misc/preview.gif?raw=true)
More information: More information:
@ -42,34 +42,17 @@ Build
For building **shellinabox** from source on Debian or RHEL based systems use commands For building **shellinabox** from source on Debian or RHEL based systems use commands
listed below. This will create executable file `shellinaboxd` in project directory. listed below. This will create executable file `shellinaboxd` in project directory.
1. Install dependencies 1. Clone the autotools
``` ```
apt-get install git libssl-dev libpam0g-dev zlib1g-dev dh-autoreconf wget https://raw.githubusercontent.com/simono41/shellinabox/master/auto.sh
``` ```
or 3. Run autotools
``` ```
yum install git openssl-devel pam-devel zlib-devel autoconf automake libtool chmod +x auto.sh
``` ./auto.sh
2. Clone source files and move to project directory
```
git clone https://github.com/shellinabox/shellinabox.git && cd shellinabox
```
3. Run autotools in project directory
```
autoreconf -i
```
4. Run configure and make in project directory
```
./configure && make
``` ```
#### Debian package #### Debian package
@ -103,5 +86,19 @@ created from our sources. In new issue report please include following things:
* Version of shellinabox * Version of shellinabox
* Steps to reproduce the problem * Steps to reproduce the problem
Also feel free to post any questions or comments in [shellianbox chat room](https://gitter.im/shellinabox/shellinabox) Also feel free to post any questions or comments in [shellinabox chat room](https://gitter.im/shellinabox/shellinabox)
on Gitter. on Gitter.
Known Issues
------------
* The openssl package is required for HTTP/SSL support.
Shell-in-a-box may be used without SSL such that the login session
is not encrypted. To enable automatic creation of self-signed
certificates or to use a generated certificate, install openssl.
* On Debian Jessie, the default openssl package does not include the
utilities necessary for Shell-in-a-box to generate self-signed
certificates. Upgrade openssl to install a version of the tools
that support certificate creation.

0
TODO Normal file → Executable file
View file

56
auto.sh Executable file
View file

@ -0,0 +1,56 @@
#!/bin/bash
set -ex
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root" 1>&2
exit 1
fi
echo "Als root Angemeldet"
if [ -f /usr/bin/apt ]; then
apt update
apt install git libssl-dev libpam0g-dev zlib1g-dev dh-autoreconf
fi
if [ -f /usr/bin/pacman ]; then
pacman -Sy git openssl autoconf automake make gcc
fi
cd /opt/
git clone https://github.com/simono41/shellinabox.git
cd shellinabox
autoreconf -i
./configure && make
cp shellinabox.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable shellinabox.service
# adduser
echo adduser webssh
useradd webssh
mkdir /home/webssh
cp shellinabox_sshwrapper.sh /home/webssh/
chmod 770 -R /home/webssh/
chown -cR webssh:webssh /home/webssh/
passwd webssh <<EOT
webssh
webssh
EOT
systemctl start shellinabox.service &

48
configure.ac Normal file → Executable file
View file

@ -1,7 +1,8 @@
AC_PREREQ(2.57) AC_PREREQ(2.57)
dnl This is the one location where the authoritative version number is stored dnl This is one of the locations where the authoritative version
AC_INIT(shellinabox, 2.15, markus@shellinabox.com) dnl number is stored. The other is in the debian/changelog.
AC_INIT(shellinabox, 2.20, markus@shellinabox.com)
if test -e .git; then if test -e .git; then
VCS_REVISION=" (revision `cd $srcdir && git log -1 --format=format:%h`)" VCS_REVISION=" (revision `cd $srcdir && git log -1 --format=format:%h`)"
else else
@ -16,6 +17,8 @@ CFLAGS="${CFLAGS:--Os}"
AM_INIT_AUTOMAKE([subdir-objects]) AM_INIT_AUTOMAKE([subdir-objects])
AM_CONFIG_HEADER(config.h) AM_CONFIG_HEADER(config.h)
AC_PROG_CC AC_PROG_CC
dnl Added this for compatibility with older versions of autoconf/automake
AM_PROG_CC_C_O
AC_LANG_WERROR AC_LANG_WERROR
AC_PROG_INSTALL AC_PROG_INSTALL
AC_PROG_LIBTOOL AC_PROG_LIBTOOL
@ -24,9 +27,12 @@ AC_C_CONST
AC_PROG_GCC_TRADITIONAL AC_PROG_GCC_TRADITIONAL
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
dnl Overwrite default archiver flags.
AC_SUBST(AR_FLAGS, [cr])
dnl Check for header files that do not exist on all platforms dnl Check for header files that do not exist on all platforms
AC_CHECK_HEADERS([libutil.h pthread.h pty.h strings.h sys/prctl.h sys/uio.h \ AC_CHECK_HEADERS([libutil.h pthread.h pty.h strings.h syslog.h sys/prctl.h \
util.h utmp.h utmpx.h]) sys/uio.h util.h])
dnl Most systems require linking against libutil.so in order to get login_tty() dnl Most systems require linking against libutil.so in order to get login_tty()
AC_CHECK_FUNCS(login_tty, [], AC_CHECK_FUNCS(login_tty, [],
@ -37,6 +43,9 @@ AC_CHECK_FUNCS(login_tty, [],
dnl Use strlcat() instead of strncat() to avoid spurious warnings dnl Use strlcat() instead of strncat() to avoid spurious warnings
AC_CHECK_FUNCS([strlcat]) AC_CHECK_FUNCS([strlcat])
dnl Use vsyslog() for logging important error messages
AC_CHECK_FUNCS([vsyslog])
dnl Prefer thread-safe functions, if available dnl Prefer thread-safe functions, if available
AC_CHECK_FUNCS([getgrgid_r getgrnam_r gethostbyname_r getpwnam_r getpwuid_r \ AC_CHECK_FUNCS([getgrgid_r getgrnam_r gethostbyname_r getpwnam_r getpwuid_r \
openpty strcasestr getresuid getresgid setresuid setresgid ]) openpty strcasestr getresuid getresgid setresuid setresgid ])
@ -66,15 +75,6 @@ AC_TRY_LINK([#include <math.h>],
[AC_DEFINE(HAVE_ISNAN, 1, [AC_DEFINE(HAVE_ISNAN, 1,
Define to 1 if you have support for isnan)]) Define to 1 if you have support for isnan)])
dnl Even if utmpx.h exists, not all systems have support for updwtmpx()
AC_TRY_LINK([#include <utmp.h>],
[updwtmp(0, 0);],
[AC_DEFINE(HAVE_UPDWTMP, 1,
Define to 1 if you have support for updwtmp)])
AC_TRY_LINK([#include <utmpx.h>],
[updwtmpx(0, 0);],
[AC_DEFINE(HAVE_UPDWTMPX, 1,
Define to 1 if you have support for updwtmpx)])
dnl Check if the compiler supports aliasing of symbols dnl Check if the compiler supports aliasing of symbols
AC_TRY_LINK([void x(void) { }; AC_TRY_LINK([void x(void) { };
@ -138,6 +138,28 @@ AC_ARG_ENABLE(runtime-loading,
these libraries into the binary, thus making them a these libraries into the binary, thus making them a
hard dependency, then disable runtime-loading.]) hard dependency, then disable runtime-loading.])
dnl This is feature is not suported in some standard C libs. So users can use
dnl this switch to avoid compile and runtime problems. Note that utmp must
dnl disabled on systems with musl libc.
AC_ARG_ENABLE(utmp,
[ --disable-utmp Disable support for login records. Note
that for systems with musl libc utmp must
be disabled.])
if test "x$enable_utmp" != xno; then
AC_CHECK_HEADERS([utmp.h utmpx.h])
dnl Even if utmpx.h exists, not all systems have support for updwtmpx()
AC_TRY_LINK([#include <utmp.h>],
[updwtmp(0, 0);],
[AC_DEFINE(HAVE_UPDWTMP, 1,
Define to 1 if you have support for updwtmp)])
AC_TRY_LINK([#include <utmpx.h>],
[updwtmpx(0, 0);],
[AC_DEFINE(HAVE_UPDWTMPX, 1,
Define to 1 if you have support for updwtmpx)])
fi
dnl Only test for OpenSSL headers, if not explicitly disabled dnl Only test for OpenSSL headers, if not explicitly disabled
if test "x$enable_ssl" != xno; then if test "x$enable_ssl" != xno; then
AC_CHECK_HEADERS([openssl/bio.h openssl/err.h openssl/ssl.h]) AC_CHECK_HEADERS([openssl/bio.h openssl/err.h openssl/ssl.h])

0
contrib/README-siab.rb Normal file → Executable file
View file

0
contrib/siab.rb Normal file → Executable file
View file

0
debian/README.available vendored Normal file → Executable file
View file

0
debian/README.enabled vendored Normal file → Executable file
View file

0
debian/README.source vendored Normal file → Executable file
View file

65
debian/changelog vendored Normal file → Executable file
View file

@ -1,3 +1,68 @@
shellinabox (2.20) unstable; urgency=medium
* Fixed issue #222, LOGIN service
* Fixed issue #360, ignore escape sequences to fix dir listing
* Fix for function key presses
* Adjusting scale on IE
* New option to disable peer check (#364)
* Add option for custom SSH port
* Support for APL characters
* PDF documentation
* Fix for BSD build
* New ready event support
* OpenSSL 1.1 fixes (Closes: #828542)
* May build with MUSL library
* Documentation added that SSL support is optional and requires
installation of openssl on Debian. (Closes: #839067)
* Jessie requires explicit installation of openssl because the default
package does not include the necessary utilities to support creation
of certificates. (Closes: #839066)
-- Marc Singer <elf@debian.org> Wed, 09 Nov 2016 09:52:24 -0800
shellinabox (2.19) unstable; urgency=high
* Added support for middle-click paste, #350.
* Improved iOS support, #354.
* New logic to enable soft keyboard icon, #119, #321, #354.
* Disable HTTP fallback using the URL /plain. Consequently disables
automatic upgrades from HTTP to HTTPS, #355. (CVE-2015-8400).
-- Marc Singer <elf@debian.org> Sat, 05 Dec 2015 10:24:12 -0800
shellinabox (2.18) unstable; urgency=low
* Fixed reverse vide rendering, #341.
* Using stdout for version information, #344.
* Added CGI session key in HTTP response.
* Child process cleanup, #103.
* Merged #340.
* Autoconf updates.
* Disabled utmp logging.
* Merge #338, killing children with HUP.
* Fixed handling of large HTTP requests.
* Enhanced logging clarity with prefix.
-- Marc Singer <elf@debian.org> Sat, 10 Oct 2015 10:53:38 -0700
shellinabox (2.17) unstable; urgency=medium
* Generally, SSL updates.
* Disabling secure client-initiated renegotiation, an abused and
unnecessary feature.
* Added support for PFS using ECDHE.
* Guarding build against old versions of SSL.
-- Marc Singer <elf@debian.org> Fri, 07 Aug 2015 20:48:33 -0700
shellinabox (2.16) unstable; urgency=low
* New release to guarantee upgrades for people who had installed a
2.15-1 release candidate.
* Update to About dialog to use GitHub URLs.
-- Marc Singer <elf@debian.org> Fri, 24 Jul 2015 11:45:22 -0700
shellinabox (2.15) unstable; urgency=low shellinabox (2.15) unstable; urgency=low
* Improved clean-liness so that source archive is clean. * Improved clean-liness so that source archive is clean.

0
debian/compat vendored Normal file → Executable file
View file

6
debian/control vendored Normal file → Executable file
View file

@ -16,5 +16,7 @@ Suggests: openssl
Description: publish command line shell through AJAX interface Description: publish command line shell through AJAX interface
Shellinabox can export arbitrary command line programs to any JavaScript Shellinabox can export arbitrary command line programs to any JavaScript
enabled web browser. By default, it prompts for username and password enabled web browser. By default, it prompts for username and password
and then exports a SSL/TLS encrypted login shell. Shellinabox provides and then exports a login shell. Shellinabox provides a VT100
a VT100 compatible terminal emulator that runs within any modern browser. compatible terminal emulator that runs within any modern
browser. Support for HTTPS protocol (SSL/TLS encryption) is optional
and requires installation of openssl.

0
debian/copyright vendored Normal file → Executable file
View file

0
debian/docs vendored Normal file → Executable file
View file

0
debian/shellinabox.default vendored Normal file → Executable file
View file

0
debian/shellinabox.dirs vendored Normal file → Executable file
View file

0
debian/shellinabox.examples vendored Normal file → Executable file
View file

0
debian/shellinabox.lintian-overrides vendored Normal file → Executable file
View file

0
debian/shellinabox.preinst vendored Normal file → Executable file
View file

0
debian/shellinabox.prerm vendored Normal file → Executable file
View file

0
debian/source/format vendored Normal file → Executable file
View file

0
debian/source/include-binaries vendored Normal file → Executable file
View file

0
debian/source/options vendored Normal file → Executable file
View file

2
debian/watch vendored
View file

@ -1,2 +0,0 @@
version=3
https://github.com/shellinabox/shellinabox/tags .*/v?(\d\S*)\.tar\.gz

0
demo/demo.html Normal file → Executable file
View file

0
demo/demo.jspp Normal file → Executable file
View file

0
demo/demo.xml Normal file → Executable file
View file

0
demo/keyboard.html Normal file → Executable file
View file

0
etc-pam.d-shellinabox-example Normal file → Executable file
View file

0
libhttp/hashmap.c Normal file → Executable file
View file

0
libhttp/hashmap.h Normal file → Executable file
View file

2
libhttp/http.h Normal file → Executable file
View file

@ -102,7 +102,7 @@ short serverConnectionSetEvents(Server *server, ServerConnection *connection,
void serverExitLoop(Server *server, int exitAll); void serverExitLoop(Server *server, int exitAll);
void serverLoop(Server *server); void serverLoop(Server *server);
int serverSupportsSSL(); int serverSupportsSSL();
void serverEnableSSL(Server *server, int flag); void serverSetupSSL(Server *server, int enable, int force);
void serverSetCertificate(Server *server, const char *filename, void serverSetCertificate(Server *server, const char *filename,
int autoGenerateMissing); int autoGenerateMissing);
void serverSetCertificateFd(Server *server, int fd); void serverSetCertificateFd(Server *server, int fd);

41
libhttp/httpconnection.c Normal file → Executable file
View file

@ -88,7 +88,7 @@
static int httpPromoteToSSL(struct HttpConnection *http, const char *buf, static int httpPromoteToSSL(struct HttpConnection *http, const char *buf,
int len) { int len) {
if (http->ssl->enabled && !http->sslHndl) { if (http->ssl->enabled && !http->sslHndl) {
debug("Switching to SSL (replaying %d+%d bytes)", debug("[ssl] Switching to SSL (replaying %d+%d bytes)...",
http->partialLength, len); http->partialLength, len);
if (http->partial && len > 0) { if (http->partial && len > 0) {
check(http->partial = realloc(http->partial, check(http->partial = realloc(http->partial,
@ -102,6 +102,8 @@ static int httpPromoteToSSL(struct HttpConnection *http, const char *buf,
http->partial ? http->partialLength : len); http->partial ? http->partialLength : len);
if (http->sslHndl) { if (http->sslHndl) {
check(!rc); check(!rc);
// Reset renegotiations count for connections promoted to SSL.
http->ssl->renegotiationCount = 0;
SSL_set_app_data(http->sslHndl, http); SSL_set_app_data(http->sslHndl, http);
} }
free(http->partial); free(http->partial);
@ -138,6 +140,13 @@ static ssize_t httpRead(struct HttpConnection *http, char *buf, ssize_t len) {
break; break;
} }
dcheck(!ERR_peek_error()); dcheck(!ERR_peek_error());
// Shutdown SSL connection, if client initiated renegotiation.
if (http->ssl->renegotiationCount > 1) {
debug("[ssl] Connection shutdown due to client initiated renegotiation!");
rc = 0;
errno = EINVAL;
}
} else { } else {
rc = NOINTR(read(http->fd, buf, len)); rc = NOINTR(read(http->fd, buf, len));
} }
@ -264,7 +273,7 @@ static int httpFinishCommand(struct HttpConnection *http) {
*lengthBuf = '\000'; *lengthBuf = '\000';
strncat(lengthBuf, "-", sizeof(lengthBuf)-1); strncat(lengthBuf, "-", sizeof(lengthBuf)-1);
} }
info("%s - - %s \"%s %s %s\" %d %s", info("[http] %s - - %s \"%s %s %s\" %d %s",
http->peerName, timeBuf, http->method, http->path, http->version, http->peerName, timeBuf, http->method, http->path, http->version,
http->code, lengthBuf); http->code, lengthBuf);
} }
@ -396,7 +405,7 @@ void initHttpConnection(struct HttpConnection *http, struct Server *server,
http->sslHndl = NULL; http->sslHndl = NULL;
http->lastError = 0; http->lastError = 0;
if (logIsInfo()) { if (logIsInfo()) {
debug("Accepted connection from %s:%d", debug("[http] Accepted connection from %s:%d",
http->peerName ? http->peerName : "???", http->peerPort); http->peerName ? http->peerName : "???", http->peerPort);
} }
} }
@ -417,11 +426,11 @@ void destroyHttpConnection(struct HttpConnection *http) {
} }
httpSetState(http, COMMAND); httpSetState(http, COMMAND);
if (logIsInfo()) { if (logIsInfo()) {
debug("Closing connection to %s:%d", debug("[http] Closing connection to %s:%d",
http->peerName ? http->peerName : "???", http->peerPort); http->peerName ? http->peerName : "???", http->peerPort);
} }
httpShutdown(http, http->closed ? SHUT_WR : SHUT_RDWR); httpShutdown(http, http->closed ? SHUT_WR : SHUT_RDWR);
dcheck(!close(http->fd)); dcheck(!close(http->fd) || errno != EBADF);
free(http->peerName); free(http->peerName);
free(http->url); free(http->url);
free(http->method); free(http->method);
@ -600,7 +609,7 @@ void httpTransfer(struct HttpConnection *http, char *msg, int len) {
// Found the end of the headers. // Found the end of the headers.
// Check that we don't send any data with HEAD requests // Check that we don't send any data with HEAD requests
int isHead = !strcmp(http->method, "HEAD"); int isHead = http->method && !strcmp(http->method, "HEAD");
check(l == 2 || !isHead); check(l == 2 || !isHead);
#ifdef HAVE_ZLIB #ifdef HAVE_ZLIB
@ -652,7 +661,7 @@ void httpTransfer(struct HttpConnection *http, char *msg, int len) {
31, 8, Z_DEFAULT_STRATEGY) == Z_OK) { 31, 8, Z_DEFAULT_STRATEGY) == Z_OK) {
if (deflate(&strm, Z_FINISH) == Z_STREAM_END) { if (deflate(&strm, Z_FINISH) == Z_STREAM_END) {
// Compression was successful and resulted in reduction in size // Compression was successful and resulted in reduction in size
debug("Compressed response from %d to %d", len, len-strm.avail_out); debug("[http] Compressed response from %d to %d", len, len-strm.avail_out);
free(msg); free(msg);
msg = compressed; msg = compressed;
len -= strm.avail_out; len -= strm.avail_out;
@ -748,7 +757,7 @@ void httpTransfer(struct HttpConnection *http, char *msg, int len) {
if (!http->isPartialReply) { if (!http->isPartialReply) {
if (http->expecting < 0) { if (http->expecting < 0) {
// If we do not know the length of the content, close the connection. // If we do not know the length of the content, close the connection.
debug("Closing previously suspended connection"); debug("[http] Closing previously suspended connection!");
httpCloseRead(http); httpCloseRead(http);
httpSetState(http, DISCARD_PAYLOAD); httpSetState(http, DISCARD_PAYLOAD);
} else if (http->expecting == 0) { } else if (http->expecting == 0) {
@ -777,7 +786,7 @@ void httpTransferPartialReply(struct HttpConnection *http, char *msg, int len){
static int httpHandleCommand(struct HttpConnection *http, static int httpHandleCommand(struct HttpConnection *http,
const struct Trie *handlers) { const struct Trie *handlers) {
debug("Handling \"%s\" \"%s\"", http->method, http->path); debug("[http] Handling \"%s\" \"%s\"", http->method, http->path);
const char *contentLength = getFromHashMap(&http->header, const char *contentLength = getFromHashMap(&http->header,
"content-length"); "content-length");
if (contentLength != NULL && *contentLength) { if (contentLength != NULL && *contentLength) {
@ -892,7 +901,7 @@ static int httpHandleCommand(struct HttpConnection *http,
protocol ? protocol : "", protocol ? protocol : "",
protocol ? "\r\n" : ""); protocol ? "\r\n" : "");
free(port); free(port);
debug("Switching to WebSockets"); debug("[http] Switching to WebSockets");
httpTransfer(http, response, strlen(response)); httpTransfer(http, response, strlen(response));
if (http->expecting < 0) { if (http->expecting < 0) {
http->expecting = 0; http->expecting = 0;
@ -1149,7 +1158,7 @@ static int httpParseHeaders(struct HttpConnection *http,
} }
value[j] = '\000'; value[j] = '\000';
if (getRefFromHashMap(&http->header, http->key)) { if (getRefFromHashMap(&http->header, http->key)) {
debug("Dropping duplicate header \"%s\"", http->key); debug("[http] Dropping duplicate header \"%s\"", http->key);
free(http->key); free(http->key);
free(value); free(value);
http->key = NULL; http->key = NULL;
@ -1412,6 +1421,7 @@ int httpHandleConnection(struct ServerConnection *connection, void *http_,
if (bytes > 0) { if (bytes > 0) {
http->headerLength += bytes; http->headerLength += bytes;
if (http->headerLength > MAX_HEADER_LENGTH) { if (http->headerLength > MAX_HEADER_LENGTH) {
debug("[http] Connection closed due to exceeded header size!");
httpSendReply(http, 413, "Header too big", NO_MSG); httpSendReply(http, 413, "Header too big", NO_MSG);
bytes = 0; bytes = 0;
eof = 1; eof = 1;
@ -1470,6 +1480,13 @@ int httpHandleConnection(struct ServerConnection *connection, void *http_,
*events |= POLLIN; *events |= POLLIN;
continue; continue;
} }
} else {
if (http->ssl && http->ssl->enabled && http->ssl->force) {
debug("[http] Non-SSL connections not allowed!");
httpCloseRead(http);
bytes = 0;
eof = 1;
}
} }
} }
@ -1773,7 +1790,7 @@ void httpSendReply(struct HttpConnection *http, int code,
code != 200 ? "Connection: close\r\n" : "", code != 200 ? "Connection: close\r\n" : "",
(long)strlen(body)); (long)strlen(body));
} }
int isHead = !strcmp(http->method, "HEAD"); int isHead = http->method && !strcmp(http->method, "HEAD");
if (!isHead) { if (!isHead) {
response = stringPrintf(response, "%s", body); response = stringPrintf(response, "%s", body);
} }

0
libhttp/httpconnection.h Normal file → Executable file
View file

0
libhttp/libhttp.sym Normal file → Executable file
View file

22
libhttp/server.c Normal file → Executable file
View file

@ -309,19 +309,20 @@ void initServer(struct Server *server, int localhostOnly, int portMin,
+ strlen(unixDomainPath); + strlen(unixDomainPath);
if (bind(server->serverFd, (struct sockaddr *)&serverAddr, servlen)) { if (bind(server->serverFd, (struct sockaddr *)&serverAddr, servlen)) {
fatal("Failed to bind to unix socket! [%d: %s]", errno, strerror(errno)); fatal("[server] Failed to bind to unix socket! [%d: %s]",
errno, strerror(errno));
} }
if (chown(unixDomainPath, unixDomainUser, unixDomainGroup)) { if (chown(unixDomainPath, unixDomainUser, unixDomainGroup)) {
fatal("Unable to change ownership on unix socket! [%d: %s]", fatal("[server] Unable to change ownership on unix socket! [%d: %s]",
errno, strerror(errno)); errno, strerror(errno));
} }
if (chmod(unixDomainPath, unixDomainChmod)) { if (chmod(unixDomainPath, unixDomainChmod)) {
fatal("Unable to change premission on unix socket! [%d: %s)", fatal("[server] Unable to change permission on unix socket! [%d: %s]",
errno, strerror(errno)); errno, strerror(errno));
} }
check(!listen(server->serverFd, SOMAXCONN)); check(!listen(server->serverFd, SOMAXCONN));
info("Listening on unix domain socket %s", unixDomainPath); info("[server] Listening on unix domain socket %s...", unixDomainPath);
check(server->pollFds = malloc(sizeof(struct pollfd))); check(server->pollFds = malloc(sizeof(struct pollfd)));
server->pollFds->fd = server->serverFd; server->pollFds->fd = server->serverFd;
server->pollFds->events = POLLIN; server->pollFds->events = POLLIN;
@ -362,7 +363,7 @@ void initServer(struct Server *server, int localhostOnly, int portMin,
serverAddr.sin_port = 0; serverAddr.sin_port = 0;
} }
if (!serverAddr.sin_port) { if (!serverAddr.sin_port) {
fatal("Failed to find any available port"); fatal("[server] Failed to find any available port!");
} }
} }
@ -372,7 +373,7 @@ void initServer(struct Server *server, int localhostOnly, int portMin,
&socklen)); &socklen));
check(socklen == sizeof(serverAddr)); check(socklen == sizeof(serverAddr));
server->port = ntohs(serverAddr.sin_port); server->port = ntohs(serverAddr.sin_port);
info("Listening on port %d", server->port); info("[server] Listening on port %d...", server->port);
check(server->pollFds = malloc(sizeof(struct pollfd))); check(server->pollFds = malloc(sizeof(struct pollfd)));
server->pollFds->fd = server->serverFd; server->pollFds->fd = server->serverFd;
@ -386,7 +387,7 @@ void initServer(struct Server *server, int localhostOnly, int portMin,
void destroyServer(struct Server *server) { void destroyServer(struct Server *server) {
if (server) { if (server) {
if (server->serverFd >= 0) { if (server->serverFd >= 0) {
info("Shutting down server"); info("[server] Shutting down server");
NOINTR(close(server->serverFd)); NOINTR(close(server->serverFd));
} }
for (int i = 0; i < server->numConnections; i++) { for (int i = 0; i < server->numConnections; i++) {
@ -669,11 +670,12 @@ void serverLoop(struct Server *server) {
server->looping = loopDepth - 1; server->looping = loopDepth - 1;
} }
void serverEnableSSL(struct Server *server, int flag) { void serverSetupSSL(struct Server *server, int enable, int force) {
if (flag) { if (enable) {
check(serverSupportsSSL()); check(serverSupportsSSL());
} }
sslEnable(&server->ssl, flag); sslEnable(&server->ssl, enable);
sslForce(&server->ssl, force);
} }
void serverSetCertificate(struct Server *server, const char *filename, void serverSetCertificate(struct Server *server, const char *filename,

2
libhttp/server.h Normal file → Executable file
View file

@ -118,7 +118,7 @@ short serverConnectionSetEvents(struct Server *server,
short events); short events);
void serverExitLoop(struct Server *server, int exitAll); void serverExitLoop(struct Server *server, int exitAll);
void serverLoop(struct Server *server); void serverLoop(struct Server *server);
void serverEnableSSL(struct Server *server, int flag); void serverSetupSSL(struct Server *server, int enable, int force);
void serverSetCertificate(struct Server *server, const char *filename, void serverSetCertificate(struct Server *server, const char *filename,
int autoGenerateMissing); int autoGenerateMissing);
void serverSetCertificateFd(struct Server *server, int fd); void serverSetCertificateFd(struct Server *server, int fd);

155
libhttp/ssl.c Normal file → Executable file
View file

@ -100,17 +100,23 @@ BIO_METHOD * (*BIO_f_buffer)(void);
void (*BIO_free_all)(BIO *); void (*BIO_free_all)(BIO *);
BIO * (*BIO_new)(BIO_METHOD *); BIO * (*BIO_new)(BIO_METHOD *);
BIO * (*BIO_new_socket)(int, int); BIO * (*BIO_new_socket)(int, int);
BIO * (*BIO_next)(BIO *);
BIO * (*BIO_pop)(BIO *); BIO * (*BIO_pop)(BIO *);
BIO * (*BIO_push)(BIO *, BIO *); BIO * (*BIO_push)(BIO *, BIO *);
#if defined(HAVE_OPENSSL_EC)
void (*EC_KEY_free)(EC_KEY *);
EC_KEY * (*EC_KEY_new_by_curve_name)(int);
#endif
void (*ERR_clear_error)(void); void (*ERR_clear_error)(void);
void (*ERR_clear_error)(void);
unsigned long (*ERR_peek_error)(void);
unsigned long (*ERR_peek_error)(void); unsigned long (*ERR_peek_error)(void);
long (*SSL_CTX_callback_ctrl)(SSL_CTX *, int, void (*)(void)); long (*SSL_CTX_callback_ctrl)(SSL_CTX *, int, void (*)(void));
int (*SSL_CTX_check_private_key)(const SSL_CTX *); int (*SSL_CTX_check_private_key)(const SSL_CTX *);
long (*SSL_CTX_ctrl)(SSL_CTX *, int, long, void *); long (*SSL_CTX_ctrl)(SSL_CTX *, int, long, void *);
void (*SSL_CTX_free)(SSL_CTX *); void (*SSL_CTX_free)(SSL_CTX *);
SSL_CTX * (*SSL_CTX_new)(SSL_METHOD *); SSL_CTX * (*SSL_CTX_new)(SSL_METHOD *);
int (*SSL_CTX_set_cipher_list)(SSL_CTX *, const char *);
void (*SSL_CTX_set_info_callback)(SSL_CTX *,
void (*)(const SSL *, int, int));
int (*SSL_CTX_use_PrivateKey_file)(SSL_CTX *, const char *, int); int (*SSL_CTX_use_PrivateKey_file)(SSL_CTX *, const char *, int);
int (*SSL_CTX_use_PrivateKey_ASN1)(int, SSL_CTX *, int (*SSL_CTX_use_PrivateKey_ASN1)(int, SSL_CTX *,
const unsigned char *, long); const unsigned char *, long);
@ -136,7 +142,6 @@ int (*SSL_write)(SSL *, const void *, int);
SSL_METHOD * (*SSLv23_server_method)(void); SSL_METHOD * (*SSLv23_server_method)(void);
X509 * (*d2i_X509)(X509 **px, const unsigned char **in, int len); X509 * (*d2i_X509)(X509 **px, const unsigned char **in, int len);
void (*X509_free)(X509 *a); void (*X509_free)(X509 *a);
int (*x_SSL_CTX_set_cipher_list)(SSL_CTX *ctx, const char *str);
void (*x_sk_zero)(void *st); void (*x_sk_zero)(void *st);
void * (*x_SSL_COMP_get_compression_methods)(void); void * (*x_SSL_COMP_get_compression_methods)(void);
#endif #endif
@ -163,9 +168,11 @@ struct SSLSupport *newSSL(void) {
void initSSL(struct SSLSupport *ssl) { void initSSL(struct SSLSupport *ssl) {
ssl->enabled = serverSupportsSSL(); ssl->enabled = serverSupportsSSL();
ssl->force = 0;
ssl->sslContext = NULL; ssl->sslContext = NULL;
ssl->sniCertificatePattern = NULL; ssl->sniCertificatePattern = NULL;
ssl->generateMissing = 0; ssl->generateMissing = 0;
ssl->renegotiationCount = 0;
initTrie(&ssl->sniContexts, sslDestroyCachedContext, ssl); initTrie(&ssl->sniContexts, sslDestroyCachedContext, ssl);
} }
@ -274,17 +281,24 @@ static void loadSSL(void) {
{ { &BIO_free_all }, "BIO_free_all" }, { { &BIO_free_all }, "BIO_free_all" },
{ { &BIO_new }, "BIO_new" }, { { &BIO_new }, "BIO_new" },
{ { &BIO_new_socket }, "BIO_new_socket" }, { { &BIO_new_socket }, "BIO_new_socket" },
{ { &BIO_next }, "BIO_next" },
{ { &BIO_pop }, "BIO_pop" }, { { &BIO_pop }, "BIO_pop" },
{ { &BIO_push }, "BIO_push" }, { { &BIO_push }, "BIO_push" },
{ { &ERR_clear_error }, "ERR_clear_error" }, { { &ERR_clear_error }, "ERR_clear_error" },
{ { &ERR_clear_error }, "ERR_clear_error" }, { { &ERR_clear_error }, "ERR_clear_error" },
{ { &ERR_peek_error }, "ERR_peek_error" }, { { &ERR_peek_error }, "ERR_peek_error" },
{ { &ERR_peek_error }, "ERR_peek_error" }, { { &ERR_peek_error }, "ERR_peek_error" },
#ifdef HAVE_OPENSSL_EC
{ { &EC_KEY_free }, "EC_KEY_free" },
{ { &EC_KEY_new_by_curve_name }, "EC_KEY_new_by_curve_name" },
#endif
{ { &SSL_CTX_callback_ctrl }, "SSL_CTX_callback_ctrl" }, { { &SSL_CTX_callback_ctrl }, "SSL_CTX_callback_ctrl" },
{ { &SSL_CTX_check_private_key }, "SSL_CTX_check_private_key" }, { { &SSL_CTX_check_private_key }, "SSL_CTX_check_private_key" },
{ { &SSL_CTX_ctrl }, "SSL_CTX_ctrl" }, { { &SSL_CTX_ctrl }, "SSL_CTX_ctrl" },
{ { &SSL_CTX_free }, "SSL_CTX_free" }, { { &SSL_CTX_free }, "SSL_CTX_free" },
{ { &SSL_CTX_new }, "SSL_CTX_new" }, { { &SSL_CTX_new }, "SSL_CTX_new" },
{ { &SSL_CTX_set_cipher_list }, "SSL_CTX_set_cipher_list" },
{ { &SSL_CTX_set_info_callback }, "SSL_CTX_set_info_callback" },
{ { &SSL_CTX_use_PrivateKey_file }, "SSL_CTX_use_PrivateKey_file" }, { { &SSL_CTX_use_PrivateKey_file }, "SSL_CTX_use_PrivateKey_file" },
{ { &SSL_CTX_use_PrivateKey_ASN1 }, "SSL_CTX_use_PrivateKey_ASN1" }, { { &SSL_CTX_use_PrivateKey_ASN1 }, "SSL_CTX_use_PrivateKey_ASN1" },
{ { &SSL_CTX_use_certificate_file },"SSL_CTX_use_certificate_file"}, { { &SSL_CTX_use_certificate_file },"SSL_CTX_use_certificate_file"},
@ -312,12 +326,11 @@ static void loadSSL(void) {
{ { &SSLv23_server_method }, "SSLv23_server_method" }, { { &SSLv23_server_method }, "SSLv23_server_method" },
{ { &d2i_X509 }, "d2i_X509" }, { { &d2i_X509 }, "d2i_X509" },
{ { &X509_free }, "X509_free" }, { { &X509_free }, "X509_free" },
{ { &x_SSL_CTX_set_cipher_list }, "SSL_CTX_set_cipher_list" },
{ { &x_sk_zero }, "sk_zero" } { { &x_sk_zero }, "sk_zero" }
}; };
for (unsigned i = 0; i < sizeof(symbols)/sizeof(symbols[0]); i++) { for (unsigned i = 0; i < sizeof(symbols)/sizeof(symbols[0]); i++) {
if (!(*symbols[i].var = loadSymbol(path_libssl, symbols[i].fn))) { if (!(*symbols[i].var = loadSymbol(path_libssl, symbols[i].fn))) {
debug("Failed to load SSL support. Could not find \"%s\"", debug("[ssl] Failed to load SSL support. Could not find \"%s\"!",
symbols[i].fn); symbols[i].fn);
for (unsigned j = 0; j < sizeof(symbols)/sizeof(symbols[0]); j++) { for (unsigned j = 0; j < sizeof(symbols)/sizeof(symbols[0]); j++) {
*symbols[j].var = NULL; *symbols[j].var = NULL;
@ -329,9 +342,10 @@ static void loadSSL(void) {
x_SSL_COMP_get_compression_methods = loadSymbol(path_libssl, "SSL_COMP_get_compression_methods"); x_SSL_COMP_get_compression_methods = loadSymbol(path_libssl, "SSL_COMP_get_compression_methods");
// ends // ends
SSL_library_init(); SSL_library_init();
dcheck(!ERR_peek_error()); dcheck(!ERR_peek_error());
debug("Loaded SSL suppport"); debug("[ssl] Loaded SSL suppport...");
} }
#endif #endif
@ -368,12 +382,12 @@ int serverSupportsSSL(void) {
#if defined(HAVE_OPENSSL) #if defined(HAVE_OPENSSL)
static void sslGenerateCertificate(const char *certificate, static void sslGenerateCertificate(const char *certificate,
const char *serverName) { const char *serverName) {
debug("Auto-generating missing certificate \"%s\" for \"%s\"", info("[ssl] Auto-generating missing certificate \"%s\" for \"%s\"...",
certificate, serverName); certificate, serverName);
pid_t pid = fork(); pid_t pid = fork();
if (pid == -1) { if (pid == -1) {
warn("Failed to generate self-signed certificate \"%s\"", certificate); warn("[ssl] Failed to generate self-signed certificate \"%s\"!", certificate);
} else if (pid == 0) { } else if (pid == 0) {
int fd = NOINTR(open("/dev/null", O_RDONLY)); int fd = NOINTR(open("/dev/null", O_RDONLY));
check(fd != -1); check(fd != -1);
@ -390,14 +404,16 @@ static void sslGenerateCertificate(const char *certificate,
if (execlp("openssl", "openssl", "req", "-x509", "-nodes", "-days", "7300", if (execlp("openssl", "openssl", "req", "-x509", "-nodes", "-days", "7300",
"-newkey", "rsa:2048", "-keyout", certificate, "-out", certificate, "-newkey", "rsa:2048", "-keyout", certificate, "-out", certificate,
"-subj", subject, (char *)NULL) < 0) { "-subj", subject, (char *)NULL) < 0) {
warn("Failed to generate self-signed certificate \"%s\"", certificate); warn("[ssl] Failed to generate self-signed certificate \"%s\"!", certificate);
free(subject); free(subject);
} }
} else { } else {
int status; int status;
check(NOINTR(waitpid(pid, &status, 0)) == pid); check(NOINTR(waitpid(pid, &status, 0)) == pid);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
warn("Failed to generate self-signed certificate \"%s\"", certificate); warn("[ssl] Failed to generate self-signed certificate \"%s\"!", certificate);
} else {
info("[ssl] Certificate successfully generated.");
} }
} }
} }
@ -595,29 +611,73 @@ static int sslSetCertificateFromFile(SSL_CTX *context,
return rc; return rc;
} }
static void sslInfoCallback(const SSL *sslHndl, int type, int val) {
// Count the number of renegotiations for each SSL session.
if (type & SSL_CB_HANDSHAKE_START) {
struct HttpConnection *http =
(struct HttpConnection *) SSL_get_app_data(sslHndl);
http->ssl->renegotiationCount += 1;
}
}
static SSL_CTX *sslMakeContext(void) { static SSL_CTX *sslMakeContext(void) {
SSL_CTX *context; SSL_CTX *context;
check(context = SSL_CTX_new(SSLv23_server_method())); check(context = SSL_CTX_new(SSLv23_server_method()));
SSL_CTX_set_options(context, SSL_OP_ALL);
SSL_CTX_set_options(context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); long options = SSL_OP_ALL;
options |= SSL_OP_NO_SSLv2;
options |= SSL_OP_NO_SSLv3;
options |= SSL_OP_SINGLE_DH_USE;
#ifdef SSL_OP_NO_COMPRESSION #ifdef SSL_OP_NO_COMPRESSION
SSL_CTX_set_options(context, SSL_OP_NO_COMPRESSION); options |= SSL_OP_NO_COMPRESSION;
#endif #endif
#if defined(HAVE_DLOPEN)
#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
options |= SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
#endif
// Set default SSL options.
SSL_CTX_set_options(context, options);
// Workaround for SSL_OP_NO_COMPRESSION with older OpenSSL versions.
#ifdef HAVE_DLOPEN
if (SSL_COMP_get_compression_methods) { if (SSL_COMP_get_compression_methods) {
sk_SSL_COMP_zero(SSL_COMP_get_compression_methods()); sk_SSL_COMP_zero(SSL_COMP_get_compression_methods());
} }
#elif OPENSSL_VERSION_NUMBER >= 0x00908000L #elif OPENSSL_VERSION_NUMBER >= 0x00908000L
sk_SSL_COMP_zero(SSL_COMP_get_compression_methods()); sk_SSL_COMP_zero(SSL_COMP_get_compression_methods());
#endif #endif
SSL_CTX_set_options(context, SSL_OP_SINGLE_DH_USE);
#ifdef SSL_OP_SINGLE_ECDH_USE // For Perfect Forward Secrecy (PFS) support we need to enable some additional
// SSL options, provide eliptic curve key object for handshake and add chipers
// suits with ECDHE handshake on top of the ciper list.
#ifdef HAVE_OPENSSL_EC
SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE); SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE);
SSL_CTX_set_options(context, SSL_OP_CIPHER_SERVER_PREFERENCE);
EC_KEY *ecKey;
check(ecKey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
SSL_CTX_set_tmp_ecdh(context, ecKey);
EC_KEY_free(ecKey);
debug("[ssl] Support for PFS enabled...");
#endif #endif
#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
SSL_CTX_set_options(context, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); check(SSL_CTX_set_cipher_list(context,
#endif "ECDHE-RSA-AES256-GCM-SHA384:"
check(SSL_CTX_set_cipher_list(context, "HIGH:MEDIUM:!aNULL:!MD5")); "ECDHE-RSA-AES128-GCM-SHA256:"
"ECDHE-RSA-AES256-SHA384:"
"ECDHE-RSA-AES128-SHA256:"
"ECDHE-RSA-AES256-SHA:"
"ECDHE-RSA-AES128-SHA:"
"ECDHE-RSA-DES-CBC3-SHA:"
"HIGH:MEDIUM:!RC4:!aNULL:!MD5"));
SSL_CTX_set_info_callback(context, sslInfoCallback);
debug("[ssl] Server context successfully initialized...");
return context; return context;
} }
#endif #endif
@ -634,7 +694,7 @@ static int sslSNICallback(SSL *sslHndl, int *al ATTR_UNUSED,
} }
struct HttpConnection *http = struct HttpConnection *http =
(struct HttpConnection *)SSL_get_app_data(sslHndl); (struct HttpConnection *)SSL_get_app_data(sslHndl);
debug("Received SNI callback for virtual host \"%s\" from \"%s:%d\"", debug("[ssl] Received SNI callback for virtual host \"%s\" from \"%s:%d\"...",
name, httpGetPeerName(http), httpGetPort(http)); name, httpGetPeerName(http), httpGetPort(http));
char *serverName; char *serverName;
check(serverName = malloc(strlen(name)+2)); check(serverName = malloc(strlen(name)+2));
@ -671,7 +731,7 @@ static int sslSNICallback(SSL *sslHndl, int *al ATTR_UNUSED,
// the default certificate, instead. // the default certificate, instead.
sslSetCertificateFromFile(context, certificate); sslSetCertificateFromFile(context, certificate);
} else { } else {
warn("Could not find matching certificate \"%s\" for \"%s\"", warn("[ssl] Could not find matching certificate \"%s\" for \"%s\"",
certificate, serverName + 1); certificate, serverName + 1);
SSL_CTX_free(context); SSL_CTX_free(context);
context = ssl->sslContext; context = ssl->sslContext;
@ -748,7 +808,7 @@ void sslSetCertificate(struct SSLSupport *ssl, const char *filename,
sslGenerateCertificate(defaultCertificate, he->h_name); sslGenerateCertificate(defaultCertificate, he->h_name);
} else { } else {
if (h_err) { if (h_err) {
warn("Error getting host information: \"%s\".", hstrerror(h_err)); warn("[ssl] Error getting host information: \"%s\".", hstrerror(h_err));
} }
sslGenerateCertificate(defaultCertificate, hostname); sslGenerateCertificate(defaultCertificate, hostname);
} }
@ -757,7 +817,7 @@ void sslSetCertificate(struct SSLSupport *ssl, const char *filename,
} }
} }
if (sslSetCertificateFromFile(ssl->sslContext, defaultCertificate) < 0) { if (sslSetCertificateFromFile(ssl->sslContext, defaultCertificate) < 0) {
fatal("Cannot read valid certificate from \"%s\". " fatal("[ssl] Cannot read valid certificate from \"%s\"! "
"Check file permissions and file format.", defaultCertificate); "Check file permissions and file format.", defaultCertificate);
} }
valid_certificate: valid_certificate:
@ -823,7 +883,7 @@ void sslSetCertificateFd(struct SSLSupport *ssl, int fd) {
ssl->sslContext = sslMakeContext(); ssl->sslContext = sslMakeContext();
char *filename = sslFdToFilename(fd); char *filename = sslFdToFilename(fd);
if (!sslSetCertificateFromFd(ssl->sslContext, fd)) { if (!sslSetCertificateFromFd(ssl->sslContext, fd)) {
fatal("Cannot read valid certificate from %s. Check file format.", fatal("[ssl] Cannot read valid certificate from %s. Check file format.",
filename); filename);
} }
free(filename); free(filename);
@ -837,6 +897,12 @@ int sslEnable(struct SSLSupport *ssl, int enabled) {
return old; return old;
} }
int sslForce(struct SSLSupport *ssl, int force) {
int old = ssl->force;
ssl->force = force;
return old;
}
void sslBlockSigPipe(void) { void sslBlockSigPipe(void) {
sigset_t set; sigset_t set;
sigemptyset(&set); sigemptyset(&set);
@ -949,6 +1015,14 @@ int sslPromoteToSSL(struct SSLSupport *ssl, SSL **sslHndl, int fd,
#endif #endif
} }
BIO *sslGetNextBIO(BIO *b) {
#if OPENSSL_VERSION_NUMBER <= 0x10100000L
return b->next_bio;
#else
return BIO_next(b);
#endif
}
void sslFreeHndl(SSL **sslHndl) { void sslFreeHndl(SSL **sslHndl) {
#if defined(HAVE_OPENSSL) #if defined(HAVE_OPENSSL)
if (*sslHndl) { if (*sslHndl) {
@ -956,24 +1030,23 @@ void sslFreeHndl(SSL **sslHndl) {
// BIOs. This is particularly a problem if an SSL connection has two // 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 // different BIOs for the read and the write end, with one being a stacked
// derivative of the other. Unfortunately, this is exactly the scenario // derivative of the other. Unfortunately, this is exactly the scenario
// that we set up. // that we set up with call to "BIO_push(readBIO, writeBIO)" in function
// "sslPromoteToSSL()".
// As a work-around, we un-stack the BIOs prior to freeing the SSL // As a work-around, we un-stack the BIOs prior to freeing the SSL
// connection. // connection.
debug("[ssl] Freeing SSL handle.");
ERR_clear_error(); ERR_clear_error();
BIO *writeBIO, *readBIO; BIO *writeBIO, *readBIO;
check(writeBIO = SSL_get_wbio(*sslHndl)); check(writeBIO = SSL_get_wbio(*sslHndl));
check(readBIO = SSL_get_rbio(*sslHndl)); check(readBIO = SSL_get_rbio(*sslHndl));
if (writeBIO != readBIO) { if (writeBIO != readBIO) {
if (readBIO->next_bio == writeBIO) { if (sslGetNextBIO(readBIO) == writeBIO) {
// OK, that's exactly the bug we are looking for. We know how to // OK, that's exactly the bug we are looking for. We know that
// fix it. // writeBIO needs to be removed from readBIO chain.
debug("[ssl] Removing stacked write BIO!");
check(BIO_pop(readBIO) == writeBIO); check(BIO_pop(readBIO) == writeBIO);
check(readBIO->references == 1); check(!sslGetNextBIO(readBIO));
check(writeBIO->references == 1); } else if (sslGetNextBIO(readBIO) == sslGetNextBIO(writeBIO)) {
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 // Things get even more confused, if the SSL handshake is aborted
// prematurely. // prematurely.
// OpenSSL appears to internally stack a BIO onto the read end that // OpenSSL appears to internally stack a BIO onto the read end that
@ -982,21 +1055,19 @@ void sslFreeHndl(SSL **sslHndl) {
// reading and one for writing). In this case, not only is the // reading and one for writing). In this case, not only is the
// reference count wrong, but the chain of next_bio/prev_bio pairs // reference count wrong, but the chain of next_bio/prev_bio pairs
// is corrupted, too. // is corrupted, too.
warn("[ssl] Removing stacked socket BIO!");
BIO *sockBIO; BIO *sockBIO;
check(sockBIO = BIO_pop(readBIO)); check(sockBIO = BIO_pop(readBIO));
check(sockBIO == BIO_pop(writeBIO)); check(sockBIO == BIO_pop(writeBIO));
check(readBIO->references == 1); check(!sslGetNextBIO(readBIO));
check(writeBIO->references == 1); check(!sslGetNextBIO(writeBIO));
check(sockBIO->references == 1); check(!sslGetNextBIO(sockBIO));
check(!readBIO->next_bio);
check(!writeBIO->next_bio);
check(!sockBIO->prev_bio);
BIO_free_all(sockBIO); BIO_free_all(sockBIO);
} else { } else {
// We do not know, how to fix this situation. Something must have // We do not know, how to fix this situation. Something must have
// changed in the OpenSSL internals. Either, this is a new bug, or // changed in the OpenSSL internals. Either, this is a new bug, or
// somebody fixed the code in a way that we did not anticipate. // somebody fixed the code in a way that we did not anticipate.
fatal("Unexpected corruption of OpenSSL data structures"); fatal("[ssl] Unexpected corruption of OpenSSL data structures");
} }
} }
SSL_free(*sslHndl); SSL_free(*sslHndl);

33
libhttp/ssl.h Normal file → Executable file
View file

@ -61,6 +61,7 @@
#undef HAVE_OPENSSL #undef HAVE_OPENSSL
typedef struct BIO BIO; typedef struct BIO BIO;
typedef struct BIO_METHOD BIO_METHOD; typedef struct BIO_METHOD BIO_METHOD;
typedef struct EC_KEY EC_KEY;
typedef struct SSL SSL; typedef struct SSL SSL;
typedef struct SSL_CTX SSL_CTX; typedef struct SSL_CTX SSL_CTX;
typedef struct SSL_METHOD SSL_METHOD; typedef struct SSL_METHOD SSL_METHOD;
@ -69,23 +70,35 @@ typedef struct X509 X509;
#define SSL_ERROR_WANT_WRITE 3 #define SSL_ERROR_WANT_WRITE 3
#endif #endif
// EC support was added to OpenSSL in 0.9.8, but it can be disabled in some
// distributions.
#if OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_EC)
# define HAVE_OPENSSL_EC
#endif
#if defined(HAVE_DLOPEN) #if defined(HAVE_DLOPEN)
extern long (*x_BIO_ctrl)(BIO *, int, long, void *); extern long (*x_BIO_ctrl)(BIO *, int, long, void *);
extern BIO_METHOD *(*x_BIO_f_buffer)(void); extern BIO_METHOD *(*x_BIO_f_buffer)(void);
extern void (*x_BIO_free_all)(BIO *); extern void (*x_BIO_free_all)(BIO *);
extern BIO *(*x_BIO_new)(BIO_METHOD *); extern BIO *(*x_BIO_new)(BIO_METHOD *);
extern BIO *(*x_BIO_new_socket)(int, int); extern BIO *(*x_BIO_new_socket)(int, int);
extern BIO *(*x_BIO_next)(BIO *);
extern BIO *(*x_BIO_pop)(BIO *); extern BIO *(*x_BIO_pop)(BIO *);
extern BIO *(*x_BIO_push)(BIO *, BIO *); extern BIO *(*x_BIO_push)(BIO *, BIO *);
#if defined(HAVE_OPENSSL_EC)
extern void (*x_EC_KEY_free)(EC_KEY *);
extern EC_KEY *(*x_EC_KEY_new_by_curve_name)(int);
#endif
extern void (*x_ERR_clear_error)(void); 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 unsigned long (*x_ERR_peek_error)(void);
extern long (*x_SSL_CTX_callback_ctrl)(SSL_CTX *, int, void (*)(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 int (*x_SSL_CTX_check_private_key)(const SSL_CTX *);
extern long (*x_SSL_CTX_ctrl)(SSL_CTX *, int, long, void *); extern long (*x_SSL_CTX_ctrl)(SSL_CTX *, int, long, void *);
extern void (*x_SSL_CTX_free)(SSL_CTX *); extern void (*x_SSL_CTX_free)(SSL_CTX *);
extern SSL_CTX*(*x_SSL_CTX_new)(SSL_METHOD *); extern SSL_CTX*(*x_SSL_CTX_new)(SSL_METHOD *);
extern int (*x_SSL_CTX_set_cipher_list)(SSL_CTX *, const char *);
extern void (*x_SSL_CTX_set_info_callback)(SSL_CTX *,
void (*)(const SSL *, int, int));
extern int (*x_SSL_CTX_use_PrivateKey_file)(SSL_CTX *, const char *, int); extern int (*x_SSL_CTX_use_PrivateKey_file)(SSL_CTX *, const char *, int);
extern int (*x_SSL_CTX_use_PrivateKey_ASN1)(int, SSL_CTX *, extern int (*x_SSL_CTX_use_PrivateKey_ASN1)(int, SSL_CTX *,
const unsigned char *, long); const unsigned char *, long);
@ -111,7 +124,6 @@ extern int (*x_SSL_write)(SSL *, const void *, int);
extern SSL_METHOD *(*x_SSLv23_server_method)(void); extern SSL_METHOD *(*x_SSLv23_server_method)(void);
extern X509 * (*x_d2i_X509)(X509 **px, const unsigned char **in, int len); extern X509 * (*x_d2i_X509)(X509 **px, const unsigned char **in, int len);
extern void (*x_X509_free)(X509 *a); extern void (*x_X509_free)(X509 *a);
extern int (*x_SSL_CTX_set_cipher_list)(SSL_CTX *ctx, const char *str);
extern void (*x_sk_zero)(void *st); extern void (*x_sk_zero)(void *st);
extern void *(*x_SSL_COMP_get_compression_methods)(void); extern void *(*x_SSL_COMP_get_compression_methods)(void);
@ -120,17 +132,20 @@ extern void *(*x_SSL_COMP_get_compression_methods)(void);
#define BIO_free_all x_BIO_free_all #define BIO_free_all x_BIO_free_all
#define BIO_new x_BIO_new #define BIO_new x_BIO_new
#define BIO_new_socket x_BIO_new_socket #define BIO_new_socket x_BIO_new_socket
#define BIO_next x_BIO_next
#define BIO_pop x_BIO_pop #define BIO_pop x_BIO_pop
#define BIO_push x_BIO_push #define BIO_push x_BIO_push
#define EC_KEY_free x_EC_KEY_free
#define EC_KEY_new_by_curve_name x_EC_KEY_new_by_curve_name
#define ERR_clear_error x_ERR_clear_error #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 ERR_peek_error x_ERR_peek_error
#define SSL_CTX_callback_ctrl x_SSL_CTX_callback_ctrl #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_check_private_key x_SSL_CTX_check_private_key
#define SSL_CTX_ctrl x_SSL_CTX_ctrl #define SSL_CTX_ctrl x_SSL_CTX_ctrl
#define SSL_CTX_free x_SSL_CTX_free #define SSL_CTX_free x_SSL_CTX_free
#define SSL_CTX_new x_SSL_CTX_new #define SSL_CTX_new x_SSL_CTX_new
#define SSL_CTX_set_cipher_list x_SSL_CTX_set_cipher_list
#define SSL_CTX_set_info_callback x_SSL_CTX_set_info_callback
#define SSL_CTX_use_PrivateKey_file x_SSL_CTX_use_PrivateKey_file #define SSL_CTX_use_PrivateKey_file x_SSL_CTX_use_PrivateKey_file
#define SSL_CTX_use_PrivateKey_ASN1 x_SSL_CTX_use_PrivateKey_ASN1 #define SSL_CTX_use_PrivateKey_ASN1 x_SSL_CTX_use_PrivateKey_ASN1
#define SSL_CTX_use_certificate_file x_SSL_CTX_use_certificate_file #define SSL_CTX_use_certificate_file x_SSL_CTX_use_certificate_file
@ -154,13 +169,13 @@ extern void *(*x_SSL_COMP_get_compression_methods)(void);
#define SSLv23_server_method x_SSLv23_server_method #define SSLv23_server_method x_SSLv23_server_method
#define d2i_X509 x_d2i_X509 #define d2i_X509 x_d2i_X509
#define X509_free x_X509_free #define X509_free x_X509_free
#define SSL_CTX_set_cipher_list x_SSL_CTX_set_cipher_list
#define sk_zero x_sk_zero #define sk_zero x_sk_zero
#define SSL_COMP_get_compression_methods x_SSL_COMP_get_compression_methods #define SSL_COMP_get_compression_methods x_SSL_COMP_get_compression_methods
#undef BIO_set_buffer_read_data #undef BIO_set_buffer_read_data
#undef SSL_CTX_set_tlsext_servername_arg #undef SSL_CTX_set_tlsext_servername_arg
#undef SSL_CTX_set_tlsext_servername_callback #undef SSL_CTX_set_tlsext_servername_callback
#undef SSL_CTX_set_tmp_ecdh
#undef SSL_get_app_data #undef SSL_get_app_data
#undef SSL_set_app_data #undef SSL_set_app_data
#undef SSL_set_mode #undef SSL_set_mode
@ -175,6 +190,9 @@ extern void *(*x_SSL_COMP_get_compression_methods)(void);
(x_SSL_CTX_callback_ctrl(ctx, \ (x_SSL_CTX_callback_ctrl(ctx, \
SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, \ SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, \
(void (*)(void))cb)) (void (*)(void))cb))
#define SSL_CTX_set_tmp_ecdh(ctx, ecdh) \
(x_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TMP_ECDH, \
0, (char *)ecdh))
#define SSL_get_app_data(s) (x_SSL_get_ex_data(s, 0)) #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_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)) #define SSL_set_mode(ssl, op) (x_SSL_ctrl((ssl), SSL_CTRL_MODE, (op), NULL))
@ -182,9 +200,11 @@ extern void *(*x_SSL_COMP_get_compression_methods)(void);
struct SSLSupport { struct SSLSupport {
int enabled; int enabled;
int force;
SSL_CTX *sslContext; SSL_CTX *sslContext;
char *sniCertificatePattern; char *sniCertificatePattern;
int generateMissing; int generateMissing;
int renegotiationCount;
struct Trie sniContexts; struct Trie sniContexts;
}; };
@ -197,6 +217,7 @@ void sslSetCertificate(struct SSLSupport *ssl, const char *filename,
int autoGenerateMissing); int autoGenerateMissing);
void sslSetCertificateFd(struct SSLSupport *ssl, int fd); void sslSetCertificateFd(struct SSLSupport *ssl, int fd);
int sslEnable(struct SSLSupport *ssl, int enabled); int sslEnable(struct SSLSupport *ssl, int enabled);
int sslForce(struct SSLSupport *ssl, int force);
void sslBlockSigPipe(); void sslBlockSigPipe();
int sslUnblockSigPipe(); int sslUnblockSigPipe();
int sslPromoteToSSL(struct SSLSupport *ssl, SSL **sslHndl, int fd, int sslPromoteToSSL(struct SSLSupport *ssl, SSL **sslHndl, int fd,

0
libhttp/trie.c Normal file → Executable file
View file

0
libhttp/trie.h Normal file → Executable file
View file

14
libhttp/url.c Normal file → Executable file
View file

@ -82,10 +82,10 @@ static char *urlUnescape(char *s) {
ch = (ch << 4) + c2 - (c2 > '9' ? 'A' - 10 : '0'); ch = (ch << 4) + c2 - (c2 > '9' ? 'A' - 10 : '0');
++u; ++u;
} else if (!warned++) { } else if (!warned++) {
warn("Malformed URL encoded data \"%s\"", r); warn("[http] Malformed URL encoded data \"%s\"!", r);
} }
} else if (!warned++) { } else if (!warned++) {
warn("Malformed URL encoded data \"%s\"", r); warn("[http] Malformed URL encoded data \"%s\"!", r);
} }
} }
*s++ = ch; *s++ = ch;
@ -297,7 +297,7 @@ static void urlParsePostBody(struct URL *url,
urlParsePart(url, lastPart, ptr - lastPart); urlParsePart(url, lastPart, ptr - lastPart);
} else { } else {
if (ptr != buf) { if (ptr != buf) {
info("Ignoring prologue before \"multipart/form-data\""); info("[http] Ignoring prologue before \"multipart/form-data\"!");
} }
} }
lastPart = part; lastPart = part;
@ -307,18 +307,18 @@ static void urlParsePostBody(struct URL *url,
urlParsePart(url, lastPart, ptr - lastPart); urlParsePart(url, lastPart, ptr - lastPart);
lastPart = NULL; lastPart = NULL;
if (len > 0) { if (len > 0) {
info("Ignoring epilogue past end of \"multipart/" info("[http] Ignoring epilogue past end of \"multipart/"
"form-data\""); "form-data\"!");
} }
} }
} }
} }
} }
if (lastPart) { if (lastPart) {
warn("Missing final \"boundary\" for \"multipart/form-data\""); warn("[http] Missing final \"boundary\" for \"multipart/form-data\"!");
} }
} else { } else {
warn("Missing \"boundary\" information for \"multipart/form-data\""); warn("[http] Missing \"boundary\" information for \"multipart/form-data\"!");
} }
} }
destroyHashMap(&contentType); destroyHashMap(&contentType);

0
libhttp/url.h Normal file → Executable file
View file

28
logging/logging.c Normal file → Executable file
View file

@ -52,6 +52,19 @@
#include "logging/logging.h" #include "logging/logging.h"
#ifdef HAVE_SYSLOG_H
# include <syslog.h>
# ifndef HAVE_VSYSLOG
static void vsyslog(int priority, const char *fmt, va_list ap) {
char *s = vStringPrintf(NULL, fmt, ap);
if (s) {
syslog(priority, "%s", s);
free(s);
}
}
# endif
#endif
static int verbosity = MSG_DEFAULT; static int verbosity = MSG_DEFAULT;
static void debugMsg(int level, const char *fmt, va_list ap) { static void debugMsg(int level, const char *fmt, va_list ap) {
@ -86,6 +99,13 @@ void error(const char *fmt, ...) {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
debugMsg(MSG_ERROR, fmt, ap); debugMsg(MSG_ERROR, fmt, ap);
#ifdef HAVE_SYSLOG_H
va_list apSyslog;
va_copy(apSyslog, ap);
va_start(apSyslog, fmt);
vsyslog(LOG_ERR, fmt, apSyslog);
va_end(apSyslog);
#endif
va_end(ap); va_end(ap);
} }
@ -100,6 +120,14 @@ void fatal(const char *fmt, ...) {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
debugMsg(MSG_QUIET, fmt, ap); debugMsg(MSG_QUIET, fmt, ap);
#ifdef HAVE_SYSLOG_H
va_list apSyslog;
va_copy(apSyslog, ap);
va_start(apSyslog, fmt);
vsyslog(LOG_CRIT, fmt, apSyslog);
va_end(apSyslog);
syslog(LOG_CRIT, "[server] Aborting...");
#endif
va_end(ap); va_end(ap);
_exit(1); _exit(1);
} }

0
logging/logging.h Normal file → Executable file
View file

0
m4/.gitignore vendored Normal file → Executable file
View file

23
misc/embedded.html Normal file → Executable file
View file

@ -14,7 +14,7 @@
For communication with Shell In A Box we need to set '-m' (messages-origin) For communication with Shell In A Box we need to set '-m' (messages-origin)
command line option with appropriate messages origin. Origin should be set to command line option with appropriate messages origin. Origin should be set to
URL of parent (this) window. If origin is set to '*' Shell In A Box won't checki URL of parent (this) window. If origin is set to '*' Shell In A Box won't check
origin on received messages. This is usually unsafe option. origin on received messages. This is usually unsafe option.
Command line example: Command line example:
@ -25,7 +25,7 @@
# Client Side # Client Side
# #
Shell In A Box accepts messages formated as JSON strings with 'type' and 'data' Shell In A Box accepts messages formatted as JSON strings with 'type' and 'data'
fields. Messages with same format can be passed back to parent (this) window. fields. Messages with same format can be passed back to parent (this) window.
Message example: Message example:
@ -59,6 +59,9 @@
Following types of messages can be received from shellinabox: Following types of messages can be received from shellinabox:
* ready
signals that shellinabox is ready to send and receive messages
* output * output
data field contains terminal output data field contains terminal output
@ -140,10 +143,6 @@
var output = document.getElementById("output"); var output = document.getElementById("output");
var session = document.getElementById("session"); var session = document.getElementById("session");
// Add url to our iframe. We do this, only that variable 'url' can be used
// throughout the whole code where needed.
iframe.src = url;
document.getElementById("execute").addEventListener("click", function() { document.getElementById("execute").addEventListener("click", function() {
// Send input to shellinabox // Send input to shellinabox
var message = JSON.stringify({ var message = JSON.stringify({
@ -209,6 +208,15 @@
// Handle response according to response type // Handle response according to response type
var decoded = JSON.parse(message.data); var decoded = JSON.parse(message.data);
switch (decoded.type) { switch (decoded.type) {
case "ready":
// Shellinabox is ready to communicate and we will enable console output
// by default.
var message = JSON.stringify({
type : 'output',
data : 'enable'
});
iframe.contentWindow.postMessage(message, url);
break;
case "output" : case "output" :
// Append new output // Append new output
output.innerHTML = output.innerHTML + decoded.data; output.innerHTML = output.innerHTML + decoded.data;
@ -220,6 +228,9 @@
} }
}, false); }, false);
// Add url to our iframe after the event listener is installed.
iframe.src = url;
</script> </script>
</body> </body>

BIN
misc/preview.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 KiB

0
misc/preview.png Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

22
sgit Executable file
View file

@ -0,0 +1,22 @@
#!/bin/bash
set -ex
if [[ "--help" == "${1}" ]]; then
echo "bash ./sgit user.email commit"
fi
if [[ -z "${2}" ]]; then
echo "Bitte email und commit angeben!!!"
exit 1
fi
git config --global user.email "${1}"
git config --global user.name "${1}"
git status
git pull
git add --all
git commit --all -m "${2}"
git show
git push
git status

9
shellinabox.service Executable file
View file

@ -0,0 +1,9 @@
[Unit]
Description=shellinabox
[Service]
Type=oneshot
ExecStart=/opt/shellinabox/shellinaboxd -t --service=/:webssh:webssh:HOME:'/home/webssh/shellinabox_sshwrapper.sh'
[Install]
WantedBy=multi-user.target

0
shellinabox/beep.wav Normal file → Executable file
View file

16
shellinabox/black-on-white.css Normal file → Executable file
View file

@ -0,0 +1,16 @@
#vt100 .ansiDefR {
color: #ffffff;
}
#vt100 .bgAnsiDefR {
background-color: #000000;
}
#vt100 #scrollable.inverted .ansiDefR {
color: #000000;
}
#vt100 #scrollable.inverted .bgAnsiDefR {
background-color: #ffffff;
}

0
shellinabox/cgi_root.html Normal file → Executable file
View file

2
shellinabox/color.css Normal file → Executable file
View file

@ -2,6 +2,7 @@
/* SYSTEM colors */ /* SYSTEM colors */
#vt100 .ansiDef { } #vt100 .ansiDef { }
#vt100 .ansiDefR { }
#vt100 .ansi0 { color: #000000; } #vt100 .ansi0 { color: #000000; }
#vt100 .ansi1 { color: #cd0000; } #vt100 .ansi1 { color: #cd0000; }
@ -264,6 +265,7 @@
/* SYSTEM colors */ /* SYSTEM colors */
#vt100 .bgAnsiDef { } #vt100 .bgAnsiDef { }
#vt100 .bgAnsiDefR { }
#vt100 .bgAnsi0 { background-color: #000000; } #vt100 .bgAnsi0 { background-color: #000000; }
#vt100 .bgAnsi1 { background-color: #cd0000; } #vt100 .bgAnsi1 { background-color: #cd0000; }

0
shellinabox/enabled.gif Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 847 B

After

Width:  |  Height:  |  Size: 847 B

0
shellinabox/externalfile.c Normal file → Executable file
View file

0
shellinabox/externalfile.h Normal file → Executable file
View file

0
shellinabox/favicon.ico Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

0
shellinabox/keyboard-layout.html Normal file → Executable file
View file

0
shellinabox/keyboard.png Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

97
shellinabox/launcher.c Normal file → Executable file
View file

@ -63,6 +63,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/ttydefaults.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/utsname.h> #include <sys/utsname.h>
@ -128,6 +129,8 @@ typedef struct pam_handle pam_handle_t;
#define UNUSED(x) do { (void)(x); } while (0) #define UNUSED(x) do { (void)(x); } while (0)
#endif #endif
#define UNUSED_RETURN(x) do { (void)((x)+1); } while (0)
#undef pthread_once #undef pthread_once
#undef execle #undef execle
int execle(const char *, const char *, ...); int execle(const char *, const char *, ...);
@ -170,6 +173,13 @@ static int (*x_misc_conv)(int, const struct pam_message **,
static int launcher = -1; static int launcher = -1;
static uid_t restricted; static uid_t restricted;
// From shellinabox/shellinaboxd.c
extern int enableUtmpLogging;
#if defined(HAVE_SECURITY_PAM_APPL_H)
static int pamSessionSighupFlag;
#endif
// MacOS X has a somewhat unusual definition of getgrouplist() which can // MacOS X has a somewhat unusual definition of getgrouplist() which can
// trigger a compile warning. // trigger a compile warning.
#if defined(HAVE_GETGROUPLIST_TAKES_INTS) #if defined(HAVE_GETGROUPLIST_TAKES_INTS)
@ -428,7 +438,7 @@ static void loadPAM(void) {
*symbols[i].var = (void *)my_misc_conv; *symbols[i].var = (void *)my_misc_conv;
continue; continue;
} }
debug("Failed to load PAM support. Could not find \"%s\"", debug("[server] Failed to load PAM support. Could not find \"%s\"!",
symbols[i].fn); symbols[i].fn);
for (unsigned j = 0; j < sizeof(symbols)/sizeof(symbols[0]); j++) { for (unsigned j = 0; j < sizeof(symbols)/sizeof(symbols[0]); j++) {
*symbols[j].var = NULL; *symbols[j].var = NULL;
@ -436,7 +446,7 @@ static void loadPAM(void) {
return; return;
} }
} }
debug("Loaded PAM suppport"); debug("[server] Loaded PAM suppport");
} }
#endif #endif
@ -564,7 +574,7 @@ int terminateChild(struct Session *session) {
} }
if (session->pid < 1) { if (session->pid < 1) {
debug("Child pid for termination not valid!"); debug("[server] Child pid for termination not valid!");
return -1; return -1;
} }
@ -574,7 +584,7 @@ int terminateChild(struct Session *session) {
check(request = calloc(len, 1)); check(request = calloc(len, 1));
request->terminate = session->pid; request->terminate = session->pid;
if (NOINTR(write(launcher, request, len)) != len) { if (NOINTR(write(launcher, request, len)) != len) {
debug("Child %d termination request failed!", request->terminate); debug("[server] Child %d termination request failed!", request->terminate);
free(request); free(request);
return -1; return -1;
} }
@ -668,9 +678,10 @@ void destroyUtmp(struct Utmp *utmp) {
uid_t r_gid, e_gid, s_gid; uid_t r_gid, e_gid, s_gid;
check(!getresuid(&r_uid, &e_uid, &s_uid)); check(!getresuid(&r_uid, &e_uid, &s_uid));
check(!getresgid(&r_gid, &e_gid, &s_gid)); check(!getresgid(&r_gid, &e_gid, &s_gid));
setresuid(0, 0, 0); UNUSED_RETURN(setresuid(0, 0, 0));
setresgid(0, 0, 0); UNUSED_RETURN(setresgid(0, 0, 0));
if (enableUtmpLogging) {
setutxent(); setutxent();
pututxline(&utmp->utmpx); pututxline(&utmp->utmpx);
endutxent(); endutxent();
@ -680,6 +691,7 @@ void destroyUtmp(struct Utmp *utmp) {
updwtmpx("/var/log/wtmp", &utmp->utmpx); updwtmpx("/var/log/wtmp", &utmp->utmpx);
} }
#endif #endif
}
// Switch back to the lower privileges // Switch back to the lower privileges
check(!setresgid(r_gid, e_gid, s_gid)); check(!setresgid(r_gid, e_gid, s_gid));
@ -848,7 +860,7 @@ static int forkPty(int *pty, int useLogin, struct Utmp **utmp,
ptyPath[5] = 't'; ptyPath[5] = 't';
} }
if ((slave = NOINTR(open(ptyPath, O_RDWR|O_NOCTTY))) >= 0) { if ((slave = NOINTR(open(ptyPath, O_RDWR|O_NOCTTY))) >= 0) {
debug("Opened old-style pty: %s", ptyPath); debug("[server] Opened old-style pty: %s", ptyPath);
goto success; goto success;
} }
NOINTR(close(*pty)); NOINTR(close(*pty));
@ -1228,7 +1240,7 @@ static pam_handle_t *internalLogin(struct Service *service, struct Utmp *utmp,
// Update utmp/wtmp entries // Update utmp/wtmp entries
#ifdef HAVE_UTMPX_H #ifdef HAVE_UTMPX_H
if (service->authUser != 2 /* SSH */) { if (enableUtmpLogging && service->authUser != 2 /* SSH */) {
memset(&utmp->utmpx.ut_user, 0, sizeof(utmp->utmpx.ut_user)); memset(&utmp->utmpx.ut_user, 0, sizeof(utmp->utmpx.ut_user));
strncat(&utmp->utmpx.ut_user[0], service->user, strncat(&utmp->utmpx.ut_user[0], service->user,
sizeof(utmp->utmpx.ut_user) - 1); sizeof(utmp->utmpx.ut_user) - 1);
@ -1488,6 +1500,17 @@ void setWindowSize(int pty, int width, int height) {
} }
} }
#if defined(HAVE_SECURITY_PAM_APPL_H)
static void pamSessionSighupHandler(int sig ATTR_UNUSED,
siginfo_t *info ATTR_UNUSED,
void *unused ATTR_UNUSED) {
UNUSED(sig);
UNUSED(info);
UNUSED(unused);
pamSessionSighupFlag = 1;
}
#endif
static void childProcess(struct Service *service, int width, int height, static void childProcess(struct Service *service, int width, int height,
struct Utmp *utmp, const char *peerName, const char *realIP, struct Utmp *utmp, const char *peerName, const char *realIP,
const char *url) { const char *url) {
@ -1543,10 +1566,12 @@ static void childProcess(struct Service *service, int width, int height,
cfsetospeed(&tt, B38400); cfsetospeed(&tt, B38400);
tcsetattr(0, TCSAFLUSH, &tt); tcsetattr(0, TCSAFLUSH, &tt);
// Assert root privileges in order to update utmp entry. // Assert root privileges in order to update utmp entry. We can only do that,
setresuid(0, 0, 0); // if we have root permissions otherwise this fails.
setresgid(0, 0, 0); UNUSED_RETURN(setresuid(0, 0, 0));
UNUSED_RETURN(setresgid(0, 0, 0));
#ifdef HAVE_UTMPX_H #ifdef HAVE_UTMPX_H
if (enableUtmpLogging) {
setutxent(); setutxent();
struct utmpx utmpx = utmp->utmpx; struct utmpx utmpx = utmp->utmpx;
if (service->useLogin || service->authUser) { if (service->useLogin || service->authUser) {
@ -1563,6 +1588,7 @@ static void childProcess(struct Service *service, int width, int height,
updwtmpx("/var/log/wtmp", &utmpx); updwtmpx("/var/log/wtmp", &utmpx);
} }
#endif #endif
}
#endif #endif
// Create session. We might have to fork another process as PAM wants us // Create session. We might have to fork another process as PAM wants us
@ -1575,7 +1601,7 @@ static void childProcess(struct Service *service, int width, int height,
#if defined(HAVE_SECURITY_PAM_APPL_H) #if defined(HAVE_SECURITY_PAM_APPL_H)
if (pam && !geteuid()) { if (pam && !geteuid()) {
if (pam_open_session(pam, PAM_SILENT) != PAM_SUCCESS) { if (pam_open_session(pam, PAM_SILENT) != PAM_SUCCESS) {
fprintf(stderr, "Access denied.\n"); fprintf(stderr, "[server] Unable to open PAM session!\n");
_exit(1); _exit(1);
} }
pid_t pid = fork(); pid_t pid = fork();
@ -1585,9 +1611,26 @@ static void childProcess(struct Service *service, int width, int height,
case 0: case 0:
break; break;
default:; default:;
// Finish all pending PAM operations. // This process is used for finishing all pending PAM operations.
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
sa.sa_sigaction = pamSessionSighupHandler;
check(!sigaction(SIGHUP, &sa, NULL));
int status, rc; int status, rc;
check(NOINTR(waitpid(pid, &status, 0)) == pid); while (1) {
pamSessionSighupFlag = 0;
int val = waitpid(pid, &status, 0);
if (val < 0 && errno == EINTR) {
if (pamSessionSighupFlag) {
// If SIGHUP signal is received it needs to be forwarded to child
// process, which is acctual service process.
kill(pid, SIGHUP);
}
} else {
break;
}
}
rc = pam_close_session(pam, PAM_SILENT); rc = pam_close_session(pam, PAM_SILENT);
pam_end(pam, rc | PAM_DATA_SILENT); pam_end(pam, rc | PAM_DATA_SILENT);
_exit(WIFEXITED(status) ? WEXITSTATUS(status) : -WTERMSIG(status)); _exit(WIFEXITED(status) ? WEXITSTATUS(status) : -WTERMSIG(status));
@ -1626,6 +1669,10 @@ static void childProcess(struct Service *service, int width, int height,
} }
} }
// Reset the sigaction for HUP to the default, so the child does not inherit the action of SIG_IGN from us.
// We need the child to be able to get HUP's because we send HUP if the session times out/closes.
signal(SIGHUP, SIG_DFL);
// Finally, launch the child process. // Finally, launch the child process.
if (service->useLogin == 1) { if (service->useLogin == 1) {
// At login service launch, we try to pass real IP in '-h' parameter. Real // At login service launch, we try to pass real IP in '-h' parameter. Real
@ -1669,7 +1716,7 @@ static void launcherDaemon(int fd) {
int len = read(fd, &request, sizeof(request)); int len = read(fd, &request, sizeof(request));
if (len != sizeof(request) && errno != EINTR) { if (len != sizeof(request) && errno != EINTR) {
if (len) { if (len) {
debug("Failed to read launch request"); debug("[server] Failed to read launch request!");
} }
break; break;
} }
@ -1679,7 +1726,8 @@ static void launcherDaemon(int fd) {
int status; int status;
pid_t pid; pid_t pid;
while (NOINTR(pid = waitpid(-1, &status, WNOHANG)) > 0) { while (NOINTR(pid = waitpid(-1, &status, WNOHANG)) > 0) {
debug("Child %d exited with exit code %d", pid, WEXITSTATUS(status)); debug("[server] Child %d exited with exit code %d.",
pid, WEXITSTATUS(status));
if (WIFEXITED(status) || WIFSIGNALED(status)) { if (WIFEXITED(status) || WIFSIGNALED(status)) {
char key[32]; char key[32];
snprintf(&key[0], sizeof(key), "%d", pid); snprintf(&key[0], sizeof(key), "%d", pid);
@ -1696,10 +1744,10 @@ static void launcherDaemon(int fd) {
errno = 0; errno = 0;
NOINTR(pid = waitpid(request.terminate, &status, WNOHANG)); NOINTR(pid = waitpid(request.terminate, &status, WNOHANG));
if (pid == 0 && errno == 0) { if (pid == 0 && errno == 0) {
if (kill(request.terminate, SIGTERM) == 0) { if (kill(request.terminate, SIGHUP) == 0) {
debug("Terminating child %d (kill)", request.terminate); debug("[server] Terminating child %d! [HUP]", request.terminate);
} else { } else {
debug("Terminating child %d failed [%s]", request.terminate, debug("[server] Terminating child %d failed! [%s]", request.terminate,
strerror(errno)); strerror(errno));
} }
} }
@ -1711,12 +1759,13 @@ static void launcherDaemon(int fd) {
readURL: readURL:
len = read(fd, url, request.urlLength + 1); len = read(fd, url, request.urlLength + 1);
if (len != request.urlLength + 1 && errno != EINTR) { if (len != request.urlLength + 1 && errno != EINTR) {
debug("Failed to read URL"); debug("[server] Failed to read URL!");
free(url); free(url);
break; break;
} }
while (NOINTR(pid = waitpid(-1, &status, WNOHANG)) > 0) { while (NOINTR(pid = waitpid(-1, &status, WNOHANG)) > 0) {
debug("Child %d exited with exit code %d", pid, WEXITSTATUS(status)); debug("[server] Child %d exited with exit code %d.", pid,
WEXITSTATUS(status));
if (WIFEXITED(status) || WIFSIGNALED(status)) { if (WIFEXITED(status) || WIFSIGNALED(status)) {
char key[32]; char key[32];
snprintf(&key[0], sizeof(key), "%d", pid); snprintf(&key[0], sizeof(key), "%d", pid);
@ -1773,7 +1822,7 @@ static void launcherDaemon(int fd) {
childProcesses = newHashMap(destroyUtmpHashEntry, NULL); childProcesses = newHashMap(destroyUtmpHashEntry, NULL);
} }
addToHashMap(childProcesses, utmp->pid, (char *)utmp); addToHashMap(childProcesses, utmp->pid, (char *)utmp);
debug("Child %d launched", pid); debug("[server] Child %d launched", pid);
} else { } else {
int fds[2]; int fds[2];
if (!pipe(fds)) { if (!pipe(fds)) {
@ -1827,9 +1876,9 @@ int forkLauncher(void) {
lowerPrivileges(); lowerPrivileges();
closeAllFds((int []){ pair[1], 2 }, 2); closeAllFds((int []){ pair[1], 2 }, 2);
launcherDaemon(pair[1]); launcherDaemon(pair[1]);
fatal("exit() failed!"); fatal("[server] Launcher exit() failed!");
case -1: case -1:
fatal("fork() failed!"); fatal("[server] Launcher fork() failed!");
default: default:
NOINTR(close(pair[1])); NOINTR(close(pair[1]));
launcher = pair[0]; launcher = pair[0];

0
shellinabox/launcher.h Normal file → Executable file
View file

0
shellinabox/monochrome.css Normal file → Executable file
View file

0
shellinabox/print-styles.css Normal file → Executable file
View file

21
shellinabox/privileges.c Normal file → Executable file
View file

@ -59,10 +59,11 @@
#include "shellinabox/privileges.h" #include "shellinabox/privileges.h"
#include "logging/logging.h" #include "logging/logging.h"
#define UNUSED_RETURN(x) do { (void)((x)+1); } while (0)
int runAsUser = -1; int runAsUser = -1;
int runAsGroup = -1; int runAsGroup = -1;
#ifndef HAVE_GETRESUID #ifndef HAVE_GETRESUID
int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) { int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) {
*ruid = getuid(); *ruid = getuid();
@ -109,7 +110,7 @@ static void removeGroupPrivileges(int showError) {
if ((ru && runAsGroup != (int)rg) || if ((ru && runAsGroup != (int)rg) ||
setresgid(runAsGroup, runAsGroup, runAsGroup)) { setresgid(runAsGroup, runAsGroup, runAsGroup)) {
if (showError) { if (showError) {
fatal("Only privileged users can change their group memberships"); fatal("[server] Only privileged users can change their group membership!");
} else { } else {
_exit(1); _exit(1);
} }
@ -136,12 +137,12 @@ void lowerPrivileges(void) {
// Temporarily lower user privileges. If we used to have "root" privileges, // Temporarily lower user privileges. If we used to have "root" privileges,
// we can later still regain them. // we can later still regain them.
setresuid(-1, -1, 0); UNUSED_RETURN(setresuid(-1, -1, 0));
if (runAsUser >= 0) { if (runAsUser >= 0) {
// Try to switch to the user-provided user id. // Try to switch to the user-provided user id.
if (r && runAsUser != (int)r) { if (r && runAsUser != (int)r) {
fatal("Only privileged users can change their user id"); fatal("[server] Only privileged users can change their user id!");
} }
check(!setresuid(runAsUser, runAsUser, -1)); check(!setresuid(runAsUser, runAsUser, -1));
} else { } else {
@ -168,7 +169,7 @@ void dropPrivileges(void) {
// Try to switch to the user-provided user id. // Try to switch to the user-provided user id.
if ((r && runAsUser != (int)r) || if ((r && runAsUser != (int)r) ||
setresuid(runAsUser, runAsUser, runAsUser)) { setresuid(runAsUser, runAsUser, runAsUser)) {
fatal("Only privileged users can change their user id."); fatal("[server] Only privileged users can change their user id!");
} }
} else { } else {
if (r) { if (r) {
@ -267,7 +268,7 @@ uid_t getUserId(const char *name) {
#endif #endif
check(buf = malloc(len)); check(buf = malloc(len));
if (getpwnam_r(name, &pwbuf, buf, len, &pw) || !pw) { if (getpwnam_r(name, &pwbuf, buf, len, &pw) || !pw) {
fatal("Cannot look up user id \"%s\"", name); fatal("[server] Cannot look up user id \"%s\"!", name);
} }
uid_t uid = pw->pw_uid; uid_t uid = pw->pw_uid;
free(buf); free(buf);
@ -401,16 +402,16 @@ gid_t getGroupId(const char *name) {
check(buf = realloc(buf, pw_len)); check(buf = realloc(buf, pw_len));
} }
if (!getpwnam_r("nobody", &pwbuf, buf, pw_len, &pw) && pw) { if (!getpwnam_r("nobody", &pwbuf, buf, pw_len, &pw) && pw) {
debug("Substituting \"nobody's\" primary group for \"nogroup\""); debug("[server] Substituting \"nobody\"'s primary group for \"nogroup\"");
gid_t gid = pw->pw_gid; gid_t gid = pw->pw_gid;
free(buf); free(buf);
return gid; return gid;
} }
} }
} }
if(ret && errno == ERANGE) { if (ret && errno == ERANGE) {
if ((gr_len + gr_baselen) < gr_len || (gr_len + gr_baselen) > gr_max) { if ((gr_len + gr_baselen) < gr_len || (gr_len + gr_baselen) > gr_max) {
fatal("Cannot look up group \"%s\": buffer limit reached", name); fatal("[server] Cannot look up group \"%s\"! [buffer limit reached]", name);
break; break;
} }
// grow the buffer by 'gr_baselen' each time getgrnam_r fails // grow the buffer by 'gr_baselen' each time getgrnam_r fails
@ -419,7 +420,7 @@ gid_t getGroupId(const char *name) {
buf = temp; buf = temp;
continue; continue;
} }
fatal("Cannot look up group \"%s\"", name); fatal("[server] Cannot look up group \"%s\"", name);
} }
gid_t gid = gr->gr_gid; gid_t gid = gr->gr_gid;
free(buf); free(buf);

0
shellinabox/privileges.h Normal file → Executable file
View file

0
shellinabox/root_page.html Normal file → Executable file
View file

46
shellinabox/service.c Normal file → Executable file
View file

@ -78,7 +78,7 @@ void initService(struct Service *service, const char *arg) {
char *ptr; char *ptr;
if ((ptr = strchr(arg, ':')) == NULL) { if ((ptr = strchr(arg, ':')) == NULL) {
error: error:
fatal("Syntax error in service description \"%s\".", desc); fatal("[config] Syntax error in service description \"%s\"!", desc);
} }
service->id = -1; service->id = -1;
check(service->path = malloc(ptr - arg + 2)); check(service->path = malloc(ptr - arg + 2));
@ -95,8 +95,7 @@ void initService(struct Service *service, const char *arg) {
// application definition. // application definition.
if (!strcmp(arg, "LOGIN")) { if (!strcmp(arg, "LOGIN")) {
if (geteuid()) { if (geteuid()) {
fatal("Must be \"root\" to invoke \"/bin/login\". Maybe, change " fatal("[config] Must be \"root\" to invoke LOGIN service!");
"--service definitions?");
} }
service->useLogin = 1; service->useLogin = 1;
service->useHomeDir = 0; service->useHomeDir = 0;
@ -122,18 +121,29 @@ void initService(struct Service *service, const char *arg) {
service->group = NULL; service->group = NULL;
check(service->cwd = strdup("/")); check(service->cwd = strdup("/"));
char *host; char *host;
char *sshPort;
check(host = strdup("localhost")); check(host = strdup("localhost"));
check(sshPort = strdup("22"));
if ((ptr = strchr(arg, ':')) != NULL) { if ((ptr = strchr(arg, ':')) != NULL) {
check(ptr = strdup(ptr + 1)); ptr = ptr + 1;
char *end;
if ((end = strchr(ptr, ':')) != NULL) {
*end = '\000';
}
if (*ptr) { if (*ptr) {
char *tmp = strchr(ptr, ':');
if (tmp == NULL) {
// If the second ":" is not found, keep as host whatever is after first ":".
free(host); free(host);
host = ptr; check(host = strdup(ptr));
} else { } else {
free(ptr); // If we find a second ":", keep as a host whatever is in between first ":"
// and second ":" and as sshPort whatever is after second ":".
int size = (tmp - ptr + 1);
free(host);
free(sshPort);
check(host = malloc(size));
memset(host, 0, size);
memcpy(host, ptr, size - 1);
check(sshPort = strdup(tmp + 1));
}
} }
} }
@ -145,7 +155,16 @@ void initService(struct Service *service, const char *arg) {
(ch >= 'A' && ch <= 'Z') || (ch >= 'A' && ch <= 'Z') ||
(ch >= 'a' && ch <= 'z') || (ch >= 'a' && ch <= 'z') ||
ch == '-' || ch == '.')) { ch == '-' || ch == '.')) {
fatal("Invalid hostname \"%s\" in service definition", host); fatal("[config] Invalid hostname \"%s\" in service definition!", host);
}
}
// Don't allow manipulation of the SSH command line through "creative" use
// of the port.
for (char *h = sshPort; *h; h++) {
char ch = *h;
if (!(ch >= '0' && ch <= '9')) {
fatal("[config] Invalid port \"%s\" in service definition!", sshPort);
} }
} }
@ -163,8 +182,9 @@ void initService(struct Service *service, const char *arg) {
// feature, we cannot be sure that it is available on the // feature, we cannot be sure that it is available on the
// target server. Removing it for the sake of Centos. // target server. Removing it for the sake of Centos.
// "-oVisualHostKey=no" // "-oVisualHostKey=no"
" -oLogLevel=FATAL %%s@%s", host); " -oLogLevel=FATAL -p%s %%s@%s",sshPort, host);
free(host); free(host);
free(sshPort);
} else { } else {
service->useLogin = 0; service->useLogin = 0;
@ -210,7 +230,7 @@ void initService(struct Service *service, const char *arg) {
service->cwd = NULL; service->cwd = NULL;
} else { } else {
if (*arg != '/') { if (*arg != '/') {
fatal("Working directories must have absolute paths"); fatal("[config] Working directories must have absolute paths!");
} }
service->useHomeDir = 0; service->useHomeDir = 0;
check(service->cwd = strdup(arg)); check(service->cwd = strdup(arg));

0
shellinabox/service.h Normal file → Executable file
View file

6
shellinabox/session.c Normal file → Executable file
View file

@ -116,9 +116,11 @@ void initSession(struct Session *session, const char *sessionKey,
session->http = NULL; session->http = NULL;
session->done = 0; session->done = 0;
session->pty = -1; session->pty = -1;
session->ptyFirstRead = 1;
session->width = 0; session->width = 0;
session->height = 0; session->height = 0;
session->buffered = NULL; session->buffered = NULL;
session->useLogin = 0;
session->len = 0; session->len = 0;
session->pid = 0; session->pid = 0;
session->cleanup = 0; session->cleanup = 0;
@ -224,7 +226,7 @@ struct Session *findSession(const char *sessionKey, const char *cgiSessionKey,
*sessionIsNew = 0; *sessionIsNew = 0;
} else if (!cgiSessionKey && sessionKey && *sessionKey) { } else if (!cgiSessionKey && sessionKey && *sessionKey) {
*sessionIsNew = 0; *sessionIsNew = 0;
debug("Failed to find session: %s", sessionKey); debug("[server] Failed to find session: %s", sessionKey);
} else { } else {
// First contact. Create session, now. // First contact. Create session, now.
check(sessionKey = cgiSessionKey ? strdup(cgiSessionKey) check(sessionKey = cgiSessionKey ? strdup(cgiSessionKey)
@ -232,7 +234,7 @@ struct Session *findSession(const char *sessionKey, const char *cgiSessionKey,
session = newSession(sessionKey, httpGetServer(http), session = newSession(sessionKey, httpGetServer(http),
httpGetPeerName(http)); httpGetPeerName(http));
addToHashMap(sessions, sessionKey, (const char *)session); addToHashMap(sessions, sessionKey, (const char *)session);
debug("Creating a new session: %s", sessionKey); debug("[server] Creating a new session: %s", sessionKey);
} }
} }
return session; return session;

2
shellinabox/session.h Normal file → Executable file
View file

@ -58,9 +58,11 @@ struct Session {
HttpConnection *http; HttpConnection *http;
int done; int done;
int pty; int pty;
int ptyFirstRead;
int width; int width;
int height; int height;
char *buffered; char *buffered;
int useLogin;
int len; int len;
pid_t pid; pid_t pid;
int cleanup; int cleanup;

20
shellinabox/shell_in_a_box.jspp Normal file → Executable file
View file

@ -406,6 +406,9 @@ ShellInABox.prototype.messageInit = function() {
} }
} }
// After message mechanisms are in place "ready" message is sent to parent
// window.
parent.postMessage(JSON.stringify({type : 'ready', data : ''}), '*');
}; };
ShellInABox.prototype.messageReceive = function (message) { ShellInABox.prototype.messageReceive = function (message) {
@ -464,16 +467,17 @@ ShellInABox.prototype.messageReplay = function(type, data) {
}; };
ShellInABox.prototype.about = function() { ShellInABox.prototype.about = function() {
alert("Shell In A Box version " + VERSION + alert("Shell In A Box " + VERSION +
"\nCopyright 2008-2010 by Markus Gutschke\n" + "\n\n" +
"For more information check http://shellinabox.com" + "Copyright 2008-2015 by Markus Gutschke. For more information visit\n" +
"http://shellinabox.com or http://github.com/shellinabox/." +
(typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ? (typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ?
"\n\n" + "\n\n" +
"This product includes software developed by the OpenSSL Project\n" + "This product includes software developed by the OpenSSL Project for\n" +
"for use in the OpenSSL Toolkit. (http://www.openssl.org/)\n" + "use in the OpenSSL Toolkit. (http://www.openssl.org/)" +
"\n" + "\n\n" +
"This product includes cryptographic software written by " + "This product includes cryptographic software written by Eric Young\n" +
"Eric Young\n(eay@cryptsoft.com)" : "(eay@cryptsoft.com)" :
"")); ""));
}; };

200
shellinabox/shellinaboxd.c Normal file → Executable file
View file

@ -63,7 +63,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
#include <time.h>
#include <unistd.h> #include <unistd.h>
#ifdef HAVE_SYS_PRCTL_H #ifdef HAVE_SYS_PRCTL_H
@ -110,8 +110,11 @@ static int portMax;
static int localhostOnly = 0; static int localhostOnly = 0;
static int noBeep = 0; static int noBeep = 0;
static int numericHosts = 0; static int numericHosts = 0;
static int peerCheckEnabled = 1;
static int enableSSL = 1; static int enableSSL = 1;
static int enableSSLMenu = 1; static int enableSSLMenu = 1;
static int forceSSL = 1; // TODO enable http fallback with commandline option
int enableUtmpLogging = 1;
static char *messagesOrigin = NULL; static char *messagesOrigin = NULL;
static int linkifyURLs = 1; static int linkifyURLs = 1;
static char *certificateDir; static char *certificateDir;
@ -280,7 +283,7 @@ static int completePendingRequest(struct Session *session,
static void sessionDone(void *arg) { static void sessionDone(void *arg) {
struct Session *session = (struct Session *)arg; struct Session *session = (struct Session *)arg;
debug("Session %s done", session->sessionKey); debug("[server] Session %s done.", session->sessionKey);
if (session->cleanup) { if (session->cleanup) {
terminateChild(session); terminateChild(session);
} }
@ -289,6 +292,13 @@ static void sessionDone(void *arg) {
completePendingRequest(session, "", 0, INT_MAX); completePendingRequest(session, "", 0, INT_MAX);
} }
static void delaySession(void) {
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 200 * 1000; // Delay for 0.2 ms
nanosleep(&ts, NULL);
}
static int handleSession(struct ServerConnection *connection, void *arg, static int handleSession(struct ServerConnection *connection, void *arg,
short *events, short revents) { short *events, short revents) {
struct Session *session = (struct Session *)arg; struct Session *session = (struct Session *)arg;
@ -308,7 +318,7 @@ static int handleSession(struct ServerConnection *connection, void *arg,
int timedOut = serverGetTimeout(connection) < 0; int timedOut = serverGetTimeout(connection) < 0;
if (bytes || timedOut) { if (bytes || timedOut) {
if (!session->http && timedOut) { if (!session->http && timedOut) {
debug("Timeout. Closing session."); debug("[server] Timeout. Closing session %s!", session->sessionKey);
session->cleanup = 1; session->cleanup = 1;
return 0; return 0;
} }
@ -322,8 +332,26 @@ static int handleSession(struct ServerConnection *connection, void *arg,
*events = 0; *events = 0;
} }
serverSetTimeout(connection, AJAX_TIMEOUT); serverSetTimeout(connection, AJAX_TIMEOUT);
session->ptyFirstRead = 0;
return 1; return 1;
} else { } else {
if (revents & POLLHUP) {
if (session->useLogin && session->ptyFirstRead) {
// Workaround for random "Session closed" issues related to /bin/login
// closing and reopening our pty during initialization. This happens only
// on some systems like Fedora for example.
// Here we allow that our pty is closed by ignoring POLLHUP on first read.
// Delay is also needed so that login process has some time to reopen pty.
// Note that the issue may occur anyway but with workaround we reduce the
// chances.
debug("[server] POLLHUP received on login PTY first read!");
session->ptyFirstRead = 0;
delaySession();
return 1;
}
debug("[server] POLLHUP received on PTY! Closing session %s!",
session->sessionKey);
}
return 0; return 0;
} }
} }
@ -332,7 +360,7 @@ static int invalidatePendingHttpSession(void *arg, const char *key,
char **value) { char **value) {
struct Session *session = *(struct Session **)value; struct Session *session = *(struct Session **)value;
if (session->http && session->http == (HttpConnection *)arg) { if (session->http && session->http == (HttpConnection *)arg) {
debug("Clearing pending HTTP connection for session %s", key); debug("[server] Clearing pending HTTP connection for session %s!", key);
session->http = NULL; session->http = NULL;
serverDeleteConnection(session->server, session->pty); serverDeleteConnection(session->server, session->pty);
@ -366,8 +394,8 @@ static int dataHandler(HttpConnection *http, struct Service *service,
} }
// Sanity check // Sanity check
if (!sessionIsNew && strcmp(session->peerName, httpGetPeerName(http))) { if (!sessionIsNew && peerCheckEnabled && strcmp(session->peerName, httpGetPeerName(http))) {
error("Peername changed from %s to %s", error("[server] Peername changed from %s to %s",
session->peerName, httpGetPeerName(http)); session->peerName, httpGetPeerName(http));
httpSendReply(http, 400, "Bad Request", NO_MSG); httpSendReply(http, 400, "Bad Request", NO_MSG);
return HTTP_DONE; return HTTP_DONE;
@ -400,6 +428,7 @@ static int dataHandler(HttpConnection *http, struct Service *service,
goto bad_new_session; goto bad_new_session;
} }
session->http = http; session->http = http;
session->useLogin = service->useLogin;
if (launchChild(service->id, session, if (launchChild(service->id, session,
rootURL && *rootURL ? rootURL : urlGetURL(url)) < 0) { rootURL && *rootURL ? rootURL : urlGetURL(url)) < 0) {
abandonSession(session); abandonSession(session);
@ -418,7 +447,8 @@ static int dataHandler(HttpConnection *http, struct Service *service,
// Reset window dimensions of the pseudo TTY, if changed since last time set. // Reset window dimensions of the pseudo TTY, if changed since last time set.
if (session->width > 0 && session->height > 0 && if (session->width > 0 && session->height > 0 &&
(session->width != oldWidth || session->height != oldHeight)) { (session->width != oldWidth || session->height != oldHeight)) {
debug("Window size changed to %dx%d", session->width, session->height); debug("[server] Window size changed to %dx%d", session->width,
session->height);
setWindowSize(session->pty, session->width, session->height); setWindowSize(session->pty, session->width, session->height);
} }
@ -746,11 +776,11 @@ static int shellInABoxHttpHandler(HttpConnection *http, void *arg,
static int strtoint(const char *s, int minVal, int maxVal) { static int strtoint(const char *s, int minVal, int maxVal) {
char *ptr; char *ptr;
if (!*s) { if (!*s) {
fatal("Missing numeric value."); fatal("[config] Missing numeric value!");
} }
long l = strtol(s, &ptr, 10); long l = strtol(s, &ptr, 10);
if (*ptr || l < minVal || l > maxVal) { if (*ptr || l < minVal || l > maxVal) {
fatal("Range error on numeric value \"%s\".", s); fatal("[config] Range error on numeric value \"%s\"!", s);
} }
return l; return l;
} }
@ -766,7 +796,7 @@ static void usage(void) {
const char *user = getUserName(r_uid); const char *user = getUserName(r_uid);
const char *group = getGroupName(r_gid); const char *group = getGroupName(r_gid);
message("Usage: shellinaboxd [OPTIONS]...\n" printf("Usage: shellinaboxd [OPTIONS]...\n"
"Starts an HTTP server that serves terminal emulators to AJAX " "Starts an HTTP server that serves terminal emulators to AJAX "
"enabled browsers.\n" "enabled browsers.\n"
"\n" "\n"
@ -779,7 +809,7 @@ static void usage(void) {
" -f, --static-file=URL:FILE serve static file from URL path\n" " -f, --static-file=URL:FILE serve static file from URL path\n"
" -g, --group=GID switch to this group (default: %s)\n" " -g, --group=GID switch to this group (default: %s)\n"
" -h, --help print this message\n" " -h, --help print this message\n"
" --linkify=[none|normal|agressive] default is \"normal\"\n" " --linkify=[none|normal|aggressive] default is \"normal\"\n"
" --localhost-only only listen on 127.0.0.1\n" " --localhost-only only listen on 127.0.0.1\n"
" --no-beep suppress all audio output\n" " --no-beep suppress all audio output\n"
" -n, --numeric do not resolve hostnames\n" " -n, --numeric do not resolve hostnames\n"
@ -788,12 +818,14 @@ static void usage(void) {
" -p, --port=PORT select a port (default: %d)\n" " -p, --port=PORT select a port (default: %d)\n"
" -s, --service=SERVICE define one or more services\n" " -s, --service=SERVICE define one or more services\n"
"%s" "%s"
" --disable-utmp-logging disable logging to utmp and wtmp\n"
" -q, --quiet turn off all messages\n" " -q, --quiet turn off all messages\n"
" --unixdomain-only=PATH:USER:GROUP:CHMOD listen on unix socket\n" " --unixdomain-only=PATH:USER:GROUP:CHMOD listen on unix socket\n"
" -u, --user=UID switch to this user (default: %s)\n" " -u, --user=UID switch to this user (default: %s)\n"
" --user-css=STYLES defines user-selectable CSS options\n" " --user-css=STYLES defines user-selectable CSS options\n"
" -v, --verbose enable logging messages\n" " -v, --verbose enable logging messages\n"
" --version prints version information\n" " --version prints version information\n"
" --disable-peer-check disable peer check on a session\n"
"\n" "\n"
"Debug, quiet, and verbose are mutually exclusive.\n" "Debug, quiet, and verbose are mutually exclusive.\n"
"\n" "\n"
@ -832,7 +864,8 @@ static void usage(void) {
"\n" "\n"
"OPTIONs that make up a GROUP are mutually exclusive. But " "OPTIONs that make up a GROUP are mutually exclusive. But "
"individual GROUPs are\n" "individual GROUPs are\n"
"independent of each other.\n", "independent of each other.\n"
"\n",
!serverSupportsSSL() ? "" : !serverSupportsSSL() ? "" :
" -c, --cert=CERTDIR set certificate dir " " -c, --cert=CERTDIR set certificate dir "
"(default: $PWD)\n" "(default: $PWD)\n"
@ -864,6 +897,7 @@ static void parseArgs(int argc, char * const argv[]) {
int hasSSL = serverSupportsSSL(); int hasSSL = serverSupportsSSL();
if (!hasSSL) { if (!hasSSL) {
enableSSL = 0; enableSSL = 0;
forceSSL = 0;
} }
int demonize = 0; int demonize = 0;
int cgi = 0; int cgi = 0;
@ -895,12 +929,14 @@ static void parseArgs(int argc, char * const argv[]) {
{ "service", 1, 0, 's' }, { "service", 1, 0, 's' },
{ "disable-ssl", 0, 0, 't' }, { "disable-ssl", 0, 0, 't' },
{ "disable-ssl-menu", 0, 0, 0 }, { "disable-ssl-menu", 0, 0, 0 },
{ "disable-utmp-logging", 0, 0, 0 },
{ "quiet", 0, 0, 'q' }, { "quiet", 0, 0, 'q' },
{ "unixdomain-only", 1, 0, 0, }, { "unixdomain-only", 1, 0, 0, },
{ "user", 1, 0, 'u' }, { "user", 1, 0, 'u' },
{ "user-css", 1, 0, 0 }, { "user-css", 1, 0, 0 },
{ "verbose", 0, 0, 'v' }, { "verbose", 0, 0, 'v' },
{ "version", 0, 0, 0 }, { "version", 0, 0, 0 },
{ "disable-peer-check", 0, 0, 0 },
{ 0, 0, 0, 0 } }; { 0, 0, 0, 0 } };
int idx = -1; int idx = -1;
int c = getopt_long(argc, argv, optstring, options, &idx); int c = getopt_long(argc, argv, optstring, options, &idx);
@ -916,19 +952,20 @@ static void parseArgs(int argc, char * const argv[]) {
} }
if (idx-- <= 0) { if (idx-- <= 0) {
// Help (or invalid argument) // Help (or invalid argument)
usage();
if (idx < -1) { if (idx < -1) {
fatal("Failed to parse command line"); fatal("[server] Failed to parse command line!");
} else {
usage();
} }
exit(0); exit(0);
} else if (!idx--) { } else if (!idx--) {
// Background // Background
if (cgi) { if (cgi) {
fatal("CGI and background operations are mutually exclusive"); fatal("[config] CGI and background operations are mutually exclusive!");
} }
demonize = 1; demonize = 1;
if (optarg && pidfile) { if (optarg && pidfile) {
fatal("Only one pidfile can be given"); fatal("[config] Only one pidfile can be given!");
} }
if (optarg && *optarg) { if (optarg && *optarg) {
check(pidfile = strdup(optarg)); check(pidfile = strdup(optarg));
@ -936,55 +973,55 @@ static void parseArgs(int argc, char * const argv[]) {
} else if (!idx--) { } else if (!idx--) {
// Certificate // Certificate
if (!hasSSL) { if (!hasSSL) {
warn("Ignoring certificate directory, as SSL support is unavailable"); warn("[config] Ignoring certificate directory, as SSL support is unavailable.");
} }
if (certificateFd >= 0) { if (certificateFd >= 0) {
fatal("Cannot set both a certificate directory and file handle"); fatal("[config] Cannot set both a certificate directory and file handle!");
} }
if (certificateDir) { if (certificateDir) {
fatal("Only one certificate directory can be selected"); fatal("[config] Only one certificate directory can be selected!");
} }
struct stat st; struct stat st;
if (!optarg || !*optarg || stat(optarg, &st) || !S_ISDIR(st.st_mode)) { if (!optarg || !*optarg || stat(optarg, &st) || !S_ISDIR(st.st_mode)) {
fatal("\"--cert\" expects a directory name"); fatal("[config] Option --cert expects a directory name!");
} }
check(certificateDir = strdup(optarg)); check(certificateDir = strdup(optarg));
} else if (!idx--) { } else if (!idx--) {
// Certificate file descriptor // Certificate file descriptor
if (!hasSSL) { if (!hasSSL) {
warn("Ignoring certificate directory, as SSL support is unavailable"); warn("[config] Ignoring certificate directory, as SSL support is unavailable.");
} }
if (certificateDir) { if (certificateDir) {
fatal("Cannot set both a certificate directory and file handle"); fatal("[config] Cannot set both a certificate directory and file handle!");
} }
if (certificateFd >= 0) { if (certificateFd >= 0) {
fatal("Only one certificate file handle can be provided"); fatal("[config] Only one certificate file handle can be provided!");
} }
if (!optarg || *optarg < '0' || *optarg > '9') { if (!optarg || *optarg < '0' || *optarg > '9') {
fatal("Option \"--cert-fd\" expects a valid file handle."); fatal("[config] Option --cert-fd expects a valid file handle.");
} }
int tmpFd = strtoint(optarg, 3, INT_MAX); int tmpFd = strtoint(optarg, 3, INT_MAX);
certificateFd = dup(tmpFd); certificateFd = dup(tmpFd);
if (certificateFd < 0) { if (certificateFd < 0) {
fatal("Invalid certificate file handle"); fatal("[config] Invalid certificate file handle!");
} }
check(!NOINTR(close(tmpFd))); check(!NOINTR(close(tmpFd)));
} else if (!idx--) { } else if (!idx--) {
// CSS // CSS
struct stat st; struct stat st;
if (!optarg || !*optarg || stat(optarg, &st) || !S_ISREG(st.st_mode)) { if (!optarg || !*optarg || stat(optarg, &st) || !S_ISREG(st.st_mode)) {
fatal("Option \"--css\" expects a file name."); fatal("[config] Option --css expects a file name!");
} }
FILE *css = fopen(optarg, "r"); FILE *css = fopen(optarg, "r");
if (!css) { if (!css) {
fatal("Cannot read style sheet \"%s\"", optarg); fatal("[config] Cannot read style sheet \"%s\"!", optarg);
} else { } else {
check(cssStyleSheet= realloc(cssStyleSheet, strlen(cssStyleSheet) + check(cssStyleSheet= realloc(cssStyleSheet, strlen(cssStyleSheet) +
st.st_size + 2)); st.st_size + 2));
char *newData = strrchr(cssStyleSheet, '\000'); char *newData = strrchr(cssStyleSheet, '\000');
*newData++ = '\n'; *newData++ = '\n';
if (fread(newData, st.st_size, 1, css) != 1) { if (fread(newData, st.st_size, 1, css) != 1) {
fatal("Failed to read style sheet \"%s\"", optarg); fatal("[config] Failed to read style sheet \"%s\"!", optarg);
} }
newData[st.st_size]= '\000'; newData[st.st_size]= '\000';
fclose(css); fclose(css);
@ -992,19 +1029,19 @@ static void parseArgs(int argc, char * const argv[]) {
} else if (!idx--) { } else if (!idx--) {
// CGI // CGI
if (demonize) { if (demonize) {
fatal("CGI and background operations are mutually exclusive"); fatal("[config] CGI and background operations are mutually exclusive!");
} }
if (pidfile) { if (pidfile) {
fatal("CGI operation and --pidfile= are mutually exclusive"); fatal("[config] CGI operation and --pidfile are mutually exclusive!");
} }
if (port) { if (port) {
fatal("Cannot specify a port for CGI operation"); fatal("[config] Cannot specify a port for CGI operation!");
} }
cgi = 1; cgi = 1;
if (optarg && *optarg) { if (optarg && *optarg) {
char *ptr = strchr(optarg, '-'); char *ptr = strchr(optarg, '-');
if (!ptr) { if (!ptr) {
fatal("Syntax error in port range specification"); fatal("[config] Syntax error in port range specification!");
} }
*ptr = '\000'; *ptr = '\000';
portMin = strtoint(optarg, 1, 65535); portMin = strtoint(optarg, 1, 65535);
@ -1014,40 +1051,41 @@ static void parseArgs(int argc, char * const argv[]) {
} else if (!idx--) { } else if (!idx--) {
// Debug // Debug
if (!logIsDefault() && !logIsDebug()) { if (!logIsDefault() && !logIsDebug()) {
fatal("--debug is mutually exclusive with --quiet and --verbose."); fatal("[config] Option --debug is mutually exclusive with --quiet and --verbose!");
} }
verbosity = MSG_DEBUG; verbosity = MSG_DEBUG;
logSetLogLevel(verbosity); logSetLogLevel(verbosity);
} else if (!idx--) { } else if (!idx--) {
// Static file // Static file
if (!optarg || !*optarg) { if (!optarg || !*optarg) {
fatal("Option \"--static-file\" expects an argument."); fatal("[config] Option --static-file expects an argument!");
} }
char *ptr, *path, *file; char *ptr, *path, *file;
if ((ptr = strchr(optarg, ':')) == NULL) { if ((ptr = strchr(optarg, ':')) == NULL) {
fatal("Syntax error in static-file definition \"%s\".", optarg); fatal("[config] Syntax error in static-file definition \"%s\"!",
optarg);
} }
check(path = malloc(ptr - optarg + 1)); check(path = malloc(ptr - optarg + 1));
memcpy(path, optarg, ptr - optarg); memcpy(path, optarg, ptr - optarg);
path[ptr - optarg] = '\000'; path[ptr - optarg] = '\000';
check(file = strdup(ptr + 1)); check(file = strdup(ptr + 1));
if (getRefFromHashMap(externalFiles, path)) { if (getRefFromHashMap(externalFiles, path)) {
fatal("Duplicate static-file definition for \"%s\".", path); fatal("[config] Duplicate static-file definition for \"%s\"!", path);
} }
addToHashMap(externalFiles, path, file); addToHashMap(externalFiles, path, file);
} else if (!idx--) { } else if (!idx--) {
// Group // Group
if (runAsGroup >= 0) { if (runAsGroup >= 0) {
fatal("Duplicate --group option."); fatal("[config] Duplicate --group option.");
} }
if (!optarg || !*optarg) { if (!optarg || !*optarg) {
fatal("Option \"--group\" expects a group name."); fatal("[config] Option --group expects a group name.");
} }
runAsGroup = parseGroupArg(optarg, NULL); runAsGroup = parseGroupArg(optarg, NULL);
} else if (!idx--) { } else if (!idx--) {
// Linkify // Linkify
if (!optarg || !*optarg) { if (!optarg || !*optarg) {
fatal("Option \"--linkify\" expects an argument."); fatal("[config] Option --linkify expects an argument.");
} }
if (!strcmp(optarg, "none")) { if (!strcmp(optarg, "none")) {
linkifyURLs = 0; linkifyURLs = 0;
@ -1056,8 +1094,8 @@ static void parseArgs(int argc, char * const argv[]) {
} else if (!strcmp(optarg, "aggressive")) { } else if (!strcmp(optarg, "aggressive")) {
linkifyURLs = 2; linkifyURLs = 2;
} else { } else {
fatal("Invalid argument for --linkify. Must be " fatal("[config] Invalid argument for --linkify. Must be \"none\", \"normal\", "
"\"none\", \"normal\", or \"aggressive\"."); "or \"aggressive\".");
} }
} else if (!idx--) { } else if (!idx--) {
// Localhost Only // Localhost Only
@ -1071,71 +1109,74 @@ static void parseArgs(int argc, char * const argv[]) {
} else if (!idx--) { } else if (!idx--) {
// Messages origin // Messages origin
if (messagesOrigin) { if (messagesOrigin) {
fatal("Duplicated \"--messages-origin\" option."); fatal("[config] Duplicated --messages-origin option.");
} }
if (!optarg || !*optarg) { if (!optarg || !*optarg) {
fatal("Option \"--messages-origin\" expects an argument."); fatal("[config] Option --messages-origin expects an argument.");
} }
check(messagesOrigin = strdup(optarg)); check(messagesOrigin = strdup(optarg));
} else if (!idx--) { } else if (!idx--) {
// Pidfile // Pidfile
if (cgi) { if (cgi) {
fatal("CGI operation and --pidfile= are mutually exclusive"); fatal("[config] CGI operation and --pidfile are mutually exclusive");
} }
if (!optarg || !*optarg) { if (!optarg || !*optarg) {
fatal("Must specify a filename for --pidfile= option"); fatal("[config] Must specify a filename for --pidfile option");
} }
if (pidfile) { if (pidfile) {
fatal("Only one pidfile can be given"); fatal("[config] Only one pidfile can be given");
} }
check(pidfile = strdup(optarg)); check(pidfile = strdup(optarg));
} else if (!idx--) { } else if (!idx--) {
// Port // Port
if (port) { if (port) {
fatal("Duplicate --port option"); fatal("[config] Duplicate --port option!");
} }
if (cgi) { if (cgi) {
fatal("Cannot specifiy a port for CGI operation"); fatal("[config] Cannot specifiy a port for CGI operation");
} }
if (!optarg || *optarg < '0' || *optarg > '9') { if (!optarg || *optarg < '0' || *optarg > '9') {
fatal("Option \"--port\" expects a port number."); fatal("[config] Option --port expects a port number.");
} }
port = strtoint(optarg, 1, 65535); port = strtoint(optarg, 1, 65535);
} else if (!idx--) { } else if (!idx--) {
// Service // Service
if (!optarg || !*optarg) { if (!optarg || !*optarg) {
fatal("Option \"--service\" expects an argument."); fatal("[config] Option \"--service\" expects an argument.");
} }
struct Service *service; struct Service *service;
service = newService(optarg); service = newService(optarg);
if (getRefFromHashMap(serviceTable, service->path)) { if (getRefFromHashMap(serviceTable, service->path)) {
fatal("Duplicate service description for \"%s\".", service->path); fatal("[config] Duplicate service description for \"%s\".", service->path);
} }
addToHashMap(serviceTable, service->path, (char *)service); addToHashMap(serviceTable, service->path, (char *)service);
} else if (!idx--) { } else if (!idx--) {
// Disable SSL // Disable SSL
if (!hasSSL) { if (!hasSSL) {
warn("Ignoring disable-ssl option, as SSL support is unavailable"); warn("[config] Ignoring disable-ssl option, as SSL support is unavailable.");
} }
enableSSL = 0; enableSSL = 0;
forceSSL = 0;
} else if (!idx--) { } else if (!idx--) {
// Disable SSL Menu // Disable SSL Menu
if (!hasSSL) { if (!hasSSL) {
warn("Ignoring disable-ssl-menu option, as SSL support is " warn("[config] Ignoring disable-ssl-menu option, as SSL support is unavailable.");
"unavailable");
} }
enableSSLMenu = 0; enableSSLMenu = 0;
} else if (!idx--) {
// Disable UTMP logging
enableUtmpLogging = 0;
} else if (!idx--) { } else if (!idx--) {
// Quiet // Quiet
if (!logIsDefault() && !logIsQuiet()) { if (!logIsDefault() && !logIsQuiet()) {
fatal("--quiet is mutually exclusive with --debug and --verbose."); fatal("[config] Option --quiet is mutually exclusive with --debug and --verbose!");
} }
verbosity = MSG_QUIET; verbosity = MSG_QUIET;
logSetLogLevel(verbosity); logSetLogLevel(verbosity);
} else if (!idx--) { } else if (!idx--) {
// Unix domain only // Unix domain only
if (!optarg || !*optarg) { if (!optarg || !*optarg) {
fatal("Option \"--unixdomain-only\" expects an argument."); fatal("[config] Option --unixdomain-only expects an argument!");
} }
char *ptr, *s, *tmp; char *ptr, *s, *tmp;
@ -1143,7 +1184,8 @@ static void parseArgs(int argc, char * const argv[]) {
s = optarg; s = optarg;
ptr = strchr(s, ':'); ptr = strchr(s, ':');
if (ptr == NULL || ptr == s || ptr - s >= UNIX_PATH_MAX) { if (ptr == NULL || ptr == s || ptr - s >= UNIX_PATH_MAX) {
fatal("Syntax error in unixdomain-only path definition \"%s\".", optarg); fatal("[config] Syntax error in unixdomain-only path definition \"%s\".",
optarg);
} }
check(unixDomainPath = strndup(s, ptr - s)); check(unixDomainPath = strndup(s, ptr - s));
@ -1151,7 +1193,8 @@ static void parseArgs(int argc, char * const argv[]) {
s = ptr + 1; s = ptr + 1;
ptr = strchr(s, ':'); ptr = strchr(s, ':');
if (ptr == NULL || ptr == s) { if (ptr == NULL || ptr == s) {
fatal("Syntax error in unixdomain-only user definition \"%s\".", optarg); fatal("[config] Syntax error in unixdomain-only user definition \"%s\".",
optarg);
} }
check(tmp = strndup(s, ptr - s)); check(tmp = strndup(s, ptr - s));
unixDomainUser = parseUserArg(tmp, NULL); unixDomainUser = parseUserArg(tmp, NULL);
@ -1161,7 +1204,8 @@ static void parseArgs(int argc, char * const argv[]) {
s = ptr + 1; s = ptr + 1;
ptr = strchr(s, ':'); ptr = strchr(s, ':');
if (ptr == NULL || ptr == s) { if (ptr == NULL || ptr == s) {
fatal("Syntax error in unixdomain-only group definition \"%s\".", optarg); fatal("[config] Syntax error in unixdomain-only group definition \"%s\".",
optarg);
} }
check(tmp = strndup(s, ptr - s)); check(tmp = strndup(s, ptr - s));
unixDomainGroup = parseGroupArg(tmp, NULL); unixDomainGroup = parseGroupArg(tmp, NULL);
@ -1170,48 +1214,54 @@ static void parseArgs(int argc, char * const argv[]) {
// Unix domain chmod // Unix domain chmod
s = ptr + 1; s = ptr + 1;
if (strlen(ptr) == 1) { if (strlen(ptr) == 1) {
fatal("Syntax error in unixdomain-only chmod definition \"%s\".", optarg); fatal("[config] Syntax error in unixdomain-only chmod definition \"%s\".",
optarg);
} }
unixDomainChmod = strtol(s, NULL, 8); unixDomainChmod = strtol(s, NULL, 8);
} else if (!idx--) { } else if (!idx--) {
// User // User
if (runAsUser >= 0) { if (runAsUser >= 0) {
fatal("Duplicate --user option."); fatal("[config] Duplicate --user option.");
} }
if (!optarg || !*optarg) { if (!optarg || !*optarg) {
fatal("Option \"--user\" expects a user name."); fatal("[config] Option --user expects a user name.");
} }
runAsUser = parseUserArg(optarg, NULL); runAsUser = parseUserArg(optarg, NULL);
} else if (!idx--) { } else if (!idx--) {
// User CSS // User CSS
if (!optarg || !*optarg) { if (!optarg || !*optarg) {
fatal("Option \"--user-css\" expects a list of styles sheets and labels"); fatal("[config] Option --user-css expects a list of styles sheets "
"and labels!");
} }
parseUserCSS(&userCSSList, optarg); parseUserCSS(&userCSSList, optarg);
} else if (!idx--) { } else if (!idx--) {
// Verbose // Verbose
if (!logIsDefault() && (!logIsInfo() || logIsDebug())) { if (!logIsDefault() && (!logIsInfo() || logIsDebug())) {
fatal("--verbose is mutually exclusive with --debug and --quiet"); fatal("[config] Option --verbose is mutually exclusive with --debug and --quiet!");
} }
verbosity = MSG_INFO; verbosity = MSG_INFO;
logSetLogLevel(verbosity); logSetLogLevel(verbosity);
} else if (!idx--) { } else if (!idx--) {
// Version // Version
message("ShellInABox version " VERSION VCS_REVISION); printf("ShellInABox version " VERSION VCS_REVISION "\n");
exit(0); exit(0);
} else if (!idx--) {
// disable-peer-check
peerCheckEnabled = 0;
} }
} }
if (optind != argc) { if (optind != argc) {
usage(); fatal("[config] Failed to parse command line!");
fatal("Failed to parse command line");
} }
char *buf = NULL; char *buf = NULL;
check(argc >= 1); check(argc >= 1);
info("[server] Version " VERSION VCS_REVISION);
for (int i = 0; i < argc; i++) { for (int i = 0; i < argc; i++) {
buf = stringPrintf(buf, " %s", argv[i]); buf = stringPrintf(buf, "%s ", argv[i]);
} }
info("Command line:%s", buf); info("[server] Command line: %s", buf);
free(buf); free(buf);
// If the user did not specify a port, use the default one // If the user did not specify a port, use the default one
@ -1237,7 +1287,7 @@ static void parseArgs(int argc, char * const argv[]) {
if (cgi) { if (cgi) {
for (int i = 0; i < numServices; i++) { for (int i = 0; i < numServices; i++) {
if (strcmp(services[i]->path, "/")) { if (strcmp(services[i]->path, "/")) {
fatal("Non-root service URLs are incompatible with CGI operation"); fatal("[config] Non-root service URLs are incompatible with CGI operation");
} }
} }
check(cgiSessionKey = newSessionKey()); check(cgiSessionKey = newSessionKey());
@ -1287,7 +1337,8 @@ static void removeLimits() {
} }
static void setUpSSL(Server *server) { static void setUpSSL(Server *server) {
serverEnableSSL(server, enableSSL);
serverSetupSSL(server, enableSSL, forceSSL);
// Enable SSL support (if available) // Enable SSL support (if available)
if (enableSSL) { if (enableSSL) {
@ -1297,7 +1348,7 @@ static void setUpSSL(Server *server) {
} else if (certificateDir) { } else if (certificateDir) {
char *tmp; char *tmp;
if (strchr(certificateDir, '%')) { if (strchr(certificateDir, '%')) {
fatal("Invalid certificate directory name \"%s\".", certificateDir); fatal("[ssl] Invalid certificate directory name \"%s\".", certificateDir);
} }
check(tmp = stringPrintf(NULL, "%s/certificate%%s.pem", certificateDir)); check(tmp = stringPrintf(NULL, "%s/certificate%%s.pem", certificateDir));
serverSetCertificate(server, tmp, 1); serverSetCertificate(server, tmp, 1);
@ -1359,8 +1410,9 @@ int main(int argc, char * const argv[]) {
check(port = serverGetListeningPort(server)); check(port = serverGetListeningPort(server));
printf("X-ShellInABox-Port: %d\r\n" printf("X-ShellInABox-Port: %d\r\n"
"X-ShellInABox-Pid: %d\r\n" "X-ShellInABox-Pid: %d\r\n"
"X-ShellInABox-Session: %s\r\n"
"Content-type: text/html; charset=utf-8\r\n\r\n", "Content-type: text/html; charset=utf-8\r\n\r\n",
port, getpid()); port, getpid(), cgiSessionKey);
UNUSED(cgiRootSize); UNUSED(cgiRootSize);
printfUnchecked(cgiRootStart, port, cgiSessionKey); printfUnchecked(cgiRootStart, port, cgiSessionKey);
fflush(stdout); fflush(stdout);
@ -1433,6 +1485,6 @@ int main(int argc, char * const argv[]) {
} }
free((char *)pidfile); free((char *)pidfile);
} }
info("Done"); info("[server] Done");
_exit(0); _exit(0);
} }

2
shellinabox/shellinaboxd.man.in Normal file → Executable file
View file

@ -282,7 +282,7 @@ Display a brief usage message showing the valid command line parameters.
.TP .TP
\fB--linkify\fP=[\fBnone\fP|\fBnormal\fP|\fBaggressive\fP] \fB--linkify\fP=[\fBnone\fP|\fBnormal\fP|\fBaggressive\fP]
the daemon attempts to recognize URLs in the terminal output and makes them the daemon attempts to recognize URLs in the terminal output and makes them
clickable. This is not neccessarily a fool-proof process and both false clickable. This is not necessarily a fool-proof process and both false
negatives and false positives are possible. By default, only URLs starting negatives and false positives are possible. By default, only URLs starting
with a well known protocol of with a well known protocol of
.BR http:// ,\ https:// ,\ ftp:// ,\ or\ mailto: .BR http:// ,\ https:// ,\ ftp:// ,\ or\ mailto:

View file

@ -243,6 +243,12 @@
[else DEFINES_COLORS] [else DEFINES_COLORS]
/* SYSTEM colors */ /* SYSTEM colors */
#vt100 .ansiDef { color: #000000; } #vt100 .ansiDef { color: #000000; }
#vt100 .ansiDefR { color: #ffffff; }
#vt100 #scrollable.inverted .ansiDef
{ color: #ffffff; }
#vt100 #scrollable.inverted .ansiDefR
{ color: #000000; }
#vt100 .ansi0 { color: #000000; } #vt100 .ansi0 { color: #000000; }
#vt100 .ansi1 { color: #cd0000; } #vt100 .ansi1 { color: #cd0000; }
@ -505,6 +511,13 @@
/* SYSTEM colors */ /* SYSTEM colors */
#vt100 .bgAnsiDef { background-color: #ffffff; } #vt100 .bgAnsiDef { background-color: #ffffff; }
#vt100 .bgAnsiDefR
{ background-color: #000000; }
#vt100 #scrollable.inverted .bgAnsiDef
{ background-color: #000000; }
#vt100 #scrollable.inverted .bgAnsiDefR
{ background-color: #ffffff; }
#vt100 .bgAnsi0 { background-color: #000000; } #vt100 .bgAnsi0 { background-color: #000000; }
#vt100 .bgAnsi1 { background-color: #cd0000; } #vt100 .bgAnsi1 { background-color: #cd0000; }

15
shellinabox/usercss.c Normal file → Executable file
View file

@ -80,7 +80,7 @@ static void readStylesheet(struct UserCSS *userCSS, const char *filename,
int fd = open(filename, O_RDONLY); int fd = open(filename, O_RDONLY);
struct stat st; struct stat st;
if (fd < 0 || fstat(fd, &st)) { if (fd < 0 || fstat(fd, &st)) {
fatal("Cannot access style sheet \"%s\"", filename); fatal("[config] Cannot access style sheet \"%s\"!", filename);
} }
FILE *fp; FILE *fp;
check(fp = fdopen(fd, "r")); check(fp = fdopen(fd, "r"));
@ -114,7 +114,7 @@ void initUserCSS(struct UserCSS *userCSS, const char *arg) {
for (;;) { for (;;) {
const char *colon = strchr(arg, ':'); const char *colon = strchr(arg, ':');
if (!colon) { if (!colon) {
fatal("Incomplete user CSS definition: \"%s\"", arg); fatal("[config] Incomplete user CSS definition: \"%s\"!", arg);
} }
check(userCSS->label = malloc(6*(colon - arg) + 1)); check(userCSS->label = malloc(6*(colon - arg) + 1));
@ -153,15 +153,14 @@ void initUserCSS(struct UserCSS *userCSS, const char *arg) {
break; break;
case '+': case '+':
if (hasActiveMember) { if (hasActiveMember) {
fatal("There can only be one active style option per group. Maybe " fatal("[config] Only one default active style allowed per group!");
"use ';' instead of ',' to start a new group.");
} }
hasActiveMember = 1; hasActiveMember = 1;
userCSS->isActivated = 1; userCSS->isActivated = 1;
break; break;
default: default:
fatal("Must indicate with '+' or '-' whether the style option is " fatal("[config] Must indicate with '+' or '-' whether the style option "
"active by default"); "is active by default!");
} }
readStylesheet(userCSS, filename + 1, (char **)&userCSS->style, readStylesheet(userCSS, filename + 1, (char **)&userCSS->style,
@ -188,9 +187,7 @@ void initUserCSS(struct UserCSS *userCSS, const char *arg) {
} }
} }
if (!hasActiveMember && numMembers > 1) { if (!hasActiveMember && numMembers > 1) {
fatal("Each group of style options must have exactly one style that is " fatal("[config] Only one default active style allowed per group!");
"active by\n"
"default.");
} }
} }

0
shellinabox/usercss.h Normal file → Executable file
View file

View file

@ -83,6 +83,7 @@
#define EStitle 17 #define EStitle 17
#define ESss2 18 #define ESss2 18
#define ESss3 19 #define ESss3 19
#define ESVTEtitle 20
#define ATTR_DEFAULT 0x60F0 #define ATTR_DEFAULT 0x60F0
#define ATTR_REVERSE 0x0100 #define ATTR_REVERSE 0x0100
@ -297,6 +298,12 @@ VT100.prototype.getUserSettings = function() {
this.disableAlt = true; this.disableAlt = true;
} }
// Enable soft keyboard icon on some clients by default.
if (navigator.userAgent.match(/iPad|iPhone|iPod/i) != null ||
navigator.userAgent.match(/PlayStation Vita|Kindle/i) != null) {
this.softKeyboard = true;
}
if (this.visualBell) { if (this.visualBell) {
this.signature = Math.floor(16807*this.signature + 1) % this.signature = Math.floor(16807*this.signature + 1) %
((1 << 31) - 1); ((1 << 31) - 1);
@ -1198,8 +1205,8 @@ VT100.prototype.resizer = function() {
document.documentElement.clientHeight || document.documentElement.clientHeight ||
document.body.clientHeight))-1; document.body.clientHeight))-1;
// Prevent ever growing consoles on iPad. // Prevent ever growing console on some iOS clients.
if (navigator.userAgent.match(/iPad/i) != null) { if (navigator.userAgent.match(/iPad|iPhone|iPod/i) != null) {
height -= 1; height -= 1;
} }
@ -1429,6 +1436,25 @@ VT100.prototype.mouseEvent = function(event, type) {
return this.cancelEvent(event); return this.cancelEvent(event);
} }
// Simulate middle click pasting from inside of current window. Note that
// pasting content from other programs will not work in this way, since we
// don't have access to native clipboard.
if ((event.which || event.button) == 2 && selection.length) {
if (type == MOUSE_UP) {
// Use timeout to prevent double paste on Chrome/Linux.
setTimeout(function (vt100) {
return function() {
vt100.keysPressed(selection);
vt100.input.focus();
}
}(this), 10);
}
if (type == MOUSE_DOWN) {
// Prevent middle click scroll on Windows systems.
return this.cancelEvent(event);
}
}
if (this.mouseReporting) { if (this.mouseReporting) {
try { try {
event.shiftKey = false; event.shiftKey = false;
@ -1578,6 +1604,9 @@ VT100.prototype.updateWidth = function() {
this.terminalWidth = Math.floor(this.console[this.currentScreen].offsetWidth/this.cursorWidth*this.scale); this.terminalWidth = Math.floor(this.console[this.currentScreen].offsetWidth/this.cursorWidth*this.scale);
} }
} else { } else {
if ("ActiveXObject" in window)
this.terminalWidth = Math.floor(this.console[this.currentScreen].offsetWidth/this.cursorWidth*this.scale*0.95);
else
this.terminalWidth = Math.floor(this.console[this.currentScreen].offsetWidth/this.cursorWidth*this.scale); this.terminalWidth = Math.floor(this.console[this.currentScreen].offsetWidth/this.cursorWidth*this.scale);
} }
@ -2815,7 +2844,7 @@ VT100.prototype.handleKey = function(event) {
ch = part1 + ch = part1 +
((event.shiftKey ? 1 : 0) + ((event.shiftKey ? 1 : 0) +
(event.altKey|event.metaKey ? 2 : 0) + (event.altKey|event.metaKey ? 2 : 0) +
(event.ctrlKey ? 4 : 0)) + (event.ctrlKey ? 4 : 0) + 1) +
part2; part2;
} else if (ch.length == 1 && (event.altKey || event.metaKey) } else if (ch.length == 1 && (event.altKey || event.metaKey)
&& !this.disableAlt) { && !this.disableAlt) {
@ -2885,9 +2914,9 @@ VT100.prototype.fixEvent = function(event) {
// Some browsers fail to translate keys, if both shift and alt/meta is // Some browsers fail to translate keys, if both shift and alt/meta is
// pressed at the same time. We try to translate those cases, but that // pressed at the same time. We try to translate those cases, but that
// only works for US keyboard layouts. // only works for US keyboard layouts.
if (event.shiftKey) {
var u = undefined; var u = undefined;
var s = undefined; var s = undefined;
if (event.shiftKey) {
switch (this.lastNormalKeyDownEvent.keyCode) { switch (this.lastNormalKeyDownEvent.keyCode) {
case 39: /* ' -> " */ u = 39; s = 34; break; case 39: /* ' -> " */ u = 39; s = 34; break;
case 44: /* , -> < */ u = 44; s = 60; break; case 44: /* , -> < */ u = 44; s = 60; break;
@ -2928,6 +2957,13 @@ VT100.prototype.fixEvent = function(event) {
case 222: /* ' -> " */ u = 39; s = 34; break; case 222: /* ' -> " */ u = 39; s = 34; break;
default: break; default: break;
} }
} else {
var c = this.lastNormalKeyDownEvent.keyCode;
if (c >= 65 && c <= 90) {
u = c;
s = u | 32;
}
}
if (s && (event.charCode == u || event.charCode == 0)) { if (s && (event.charCode == u || event.charCode == 0)) {
var fake = [ ]; var fake = [ ];
fake.charCode = s; fake.charCode = s;
@ -2938,7 +2974,6 @@ VT100.prototype.fixEvent = function(event) {
fake.metaKey = event.metaKey; fake.metaKey = event.metaKey;
return fake; return fake;
} }
}
return event; return event;
}; };
@ -3023,10 +3058,8 @@ VT100.prototype.keyDown = function(event) {
} else { } else {
fake.charCode = 0; fake.charCode = 0;
fake.keyCode = event.keyCode; fake.keyCode = event.keyCode;
if (!alphNumKey && event.shiftKey) { }
fake = this.fixEvent(fake); fake = this.fixEvent(fake);
}
}
this.handleKey(fake); this.handleKey(fake);
this.lastNormalKeyDownEvent = undefined; this.lastNormalKeyDownEvent = undefined;
@ -3141,9 +3174,9 @@ VT100.prototype.keyUp = function(event) {
} else { } else {
fake.charCode = 0; fake.charCode = 0;
fake.keyCode = event.keyCode; fake.keyCode = event.keyCode;
if (!alphNumKey && (event.ctrlKey || event.altKey || event.metaKey)) {
fake = this.fixEvent(fake);
} }
if (event.ctrlKey || event.altKey || event.metaKey) {
fake = this.fixEvent(fake);
} }
this.lastNormalKeyDownEvent = undefined; this.lastNormalKeyDownEvent = undefined;
this.handleKey(fake); this.handleKey(fake);
@ -3351,41 +3384,47 @@ VT100.prototype.respondSecondaryDA = function() {
VT100.prototype.updateStyle = function() { VT100.prototype.updateStyle = function() {
var fg = '';
var bg = '';
this.style = ''; this.style = '';
if (this.attr & ATTR_UNDERLINE) { if (this.attr & ATTR_UNDERLINE) {
this.style = 'text-decoration: underline;'; this.style += 'text-decoration: underline;';
}
var bg = (this.attr >> 4) & 0xF;
var fg = this.attr & 0xF;
if (this.attr & ATTR_REVERSE) {
var tmp = bg;
bg = fg;
fg = tmp;
}
if ((this.attr & (ATTR_REVERSE | ATTR_DIM)) == ATTR_DIM) {
fg = 8; // Dark grey
} else if (this.attr & ATTR_BRIGHT) {
fg |= 8;
this.style = 'font-weight: bold;';
} }
if (this.attr & ATTR_BLINK) { if (this.attr & ATTR_BLINK) {
this.style = 'text-decoration: blink;'; this.style += 'text-decoration: blink;';
} }
// Default colors // Forground color
if (this.attr & ATTR_DEF_FG) {
fg = 'Def';
}
if (this.attr & ATTR_DEF_BG) {
bg = 'Def';
}
// Extended color mode support (256 colors).
if (this.attrFg) { if (this.attrFg) {
fg = this.attrFg; // 256 color mode
fg = this.attrFg
} else if (this.attr & ATTR_DEF_FG) {
fg = 'Def';
} else {
fg = this.attr & 0xF;
if (this.attr & ATTR_BRIGHT) {
fg |= 8;
this.style += 'font-weight: bold;';
} }
}
// Background color
if (this.attrBg) { if (this.attrBg) {
bg = this.attrBg; // 256 color mode
bg = this.attrBg
} else if (this.attr & ATTR_DEF_BG) {
bg = 'Def';
} else {
bg = (this.attr >> 4) & 0xF;
}
// Reverse colors
if (this.attr & ATTR_REVERSE) {
var tmpFg = fg;
var tmpBg = bg;
fg = (tmpBg == 'Def') ? 'DefR' : tmpBg;
bg = (tmpFg == 'Def') ? 'DefR' : tmpFg;
} }
this.color = 'ansi' + fg + ' bgAnsi' + bg; this.color = 'ansi' + fg + ' bgAnsi' + bg;
@ -3869,7 +3908,7 @@ VT100.prototype.csim = function() {
case 27: this.attr &= ~ ATTR_REVERSE; break; case 27: this.attr &= ~ ATTR_REVERSE; break;
case 38: case 38:
if (this.npar >= (i+2) && this.par[i+1] == 5) { if (this.npar >= (i+2) && this.par[i+1] == 5) {
// Foreground color for extended color mode (256 colors). Escape code is formated // Foreground color for extended color mode (256 colors). Escape code is formatted
// as: ESC 38; 5; 0-255. Last parameter is color code in range [0-255]. This is // as: ESC 38; 5; 0-255. Last parameter is color code in range [0-255]. This is
// not VT100 standard. // not VT100 standard.
this.attrFg = (this.par[i+2] >= 0 && this.par[i+2] <= 255) ? this.par[i+2] : false; this.attrFg = (this.par[i+2] >= 0 && this.par[i+2] <= 255) ? this.par[i+2] : false;
@ -3885,7 +3924,7 @@ VT100.prototype.csim = function() {
break; break;
case 48: case 48:
if (this.npar >= (i+2) && this.par[i+1] == 5) { if (this.npar >= (i+2) && this.par[i+1] == 5) {
// Background color for extended color mode (256 colors). Escape code is formated // Background color for extended color mode (256 colors). Escape code is formatted
// as: ESC 48; 5; 0-255. Last parameter is color code in range [0-255]. This is // as: ESC 48; 5; 0-255. Last parameter is color code in range [0-255]. This is
// not VT100 standard. // not VT100 standard.
this.attrBg = (this.par[i+2] >= 0 && this.par[i+2] <= 255) ? this.par[i+2] : false; this.attrBg = (this.par[i+2] >= 0 && this.par[i+2] <= 255) ? this.par[i+2] : false;
@ -4015,6 +4054,8 @@ VT100.prototype.doControl = function(ch) {
/*0*/ case 0x30: /*0*/ case 0x30:
/*1*/ case 0x31: /*1*/ case 0x31:
/*2*/ case 0x32: this.isEsc = EStitle; this.titleString = ''; break; /*2*/ case 0x32: this.isEsc = EStitle; this.titleString = ''; break;
/*6*/ case 0x36: this.isEsc = ESVTEtitle; break;
/*7*/ case 0x37: this.isEsc = ESVTEtitle; break;
/*P*/ case 0x50: this.npar = 0; this.par = [ 0, 0, 0, 0, 0, 0, 0 ]; /*P*/ case 0x50: this.npar = 0; this.par = [ 0, 0, 0, 0, 0, 0, 0 ];
this.isEsc = ESpalette; break; this.isEsc = ESpalette; break;
/*R*/ case 0x52: // Palette support is not implemented /*R*/ case 0x52: // Palette support is not implemented
@ -4242,6 +4283,13 @@ VT100.prototype.doControl = function(ch) {
this.lastCharacter = String.fromCharCode(ch); this.lastCharacter = String.fromCharCode(ch);
lineBuf += this.lastCharacter; lineBuf += this.lastCharacter;
this.isEsc = ESnormal; break; this.isEsc = ESnormal; break;
case ESVTEtitle:
// Ignores VTE escape sequences for current directory (OSC6) and current
// file (OSC7).
if (ch == 0x07 || ch == 0x5C) {
this.isEsc = ESnormal;
}
break;
default: default:
this.isEsc = ESnormal; break; this.isEsc = ESnormal; break;
} }

View file

@ -24,14 +24,30 @@
color: #ffffff; color: #ffffff;
} }
#vt100 .ansiDefR {
color: #000000;
}
#vt100 .bgAnsiDef { #vt100 .bgAnsiDef {
background-color: #000000; background-color: #000000;
} }
#vt100 .bgAnsiDefR {
background-color: #ffffff;
}
#vt100 #scrollable.inverted .ansiDef { #vt100 #scrollable.inverted .ansiDef {
color: #000000; color: #000000;
} }
#vt100 #scrollable.inverted .ansiDefR {
color: #ffffff;
}
#vt100 #scrollable.inverted .bgAnsiDef { #vt100 #scrollable.inverted .bgAnsiDef {
background-color: #ffffff; background-color: #ffffff;
} }
#vt100 #scrollable.inverted .bgAnsiDefR {
background-color: #000000;
}

32
shellinabox_sshwrapper.sh Executable file
View file

@ -0,0 +1,32 @@
#!/bin/bash
#
set -ex
# 
read -p "SSH remote host (hostname or ip address) [localhost] : " host;
[[ -z "${host}" ]] && host=localhost;
#
read -p "If a puplic_key authentification?: [y/N] : " puplic;
#
read -p "SSH remote port [22] : " port;
[[ -z "${port}" ]] && port=22;
#
read -p "SSH remote username [pi] : " username;
[[ -z "${username}" ]] && username=pi;
#
if [ "$puplic" == "y" ];
then
read -p "How is your public_key?: " key;
echo $key > ~/.ssh/id_rsa.pub;
rm ~/.ssh/id_rsa;
echo "Enter your private id here and press the enter key for a new line !!!";
id=null
while [ "$id" != "" ];
do
read -p "How is your id_rsa key?: " id;
echo $id >> ~/.ssh/id_rsa;
done
exec ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p $port $username@$host;
else
exec ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p $port $username@$host;
fi