Compare commits

..

No commits in common. "master" and "v2.15" have entirely different histories.

91 changed files with 382 additions and 967 deletions

1
.gitignore vendored Executable file → Normal file
View file

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

0
AUTHORS Executable file → Normal file
View file

0
CHANGELOG.md Executable file → Normal file
View file

0
COPYING Executable file → Normal file
View file

0
GPL-2 Executable file → Normal file
View file

2
INSTALL.Debian Executable file → Normal 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
both the private key and the public certificate in PEM (i.e. ASCII) format.
Additionally, you should create files for each of the publicly visible
Additionally, you should create files for each of the publically visible
hostnames that your machine has. These are named "certificate-${FQDN}.pem".
You probably also want "certificate-localhost.pem".

11
Makefile.am Executable file → Normal file
View file

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

0
NEWS Executable file → Normal file
View file

0
README Executable file → Normal file
View file

0
README.Fedora Executable file → Normal file
View file

59
README.md Executable file → Normal 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)
This is an unofficial fork of the project **Shell In A Box**. The fork was created because
the original project was not maintained anymore and we cannot contact the original
This is unofficial fork of project **Shell In A Box**. Fork was created because
original project is not maintained anymore and we cannot contact original
repository owners.
Our aim is to continue with maintenance of the shellinabox project. For a list of
recent changes, please see [CHANGELOG.md](/CHANGELOG.md).
Our aim is to continue with maintanince of shellinabox project. For list of
recent changes please see [CHANGELOG.md](/CHANGELOG.md).
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 the original project
[issue #261](https://code.google.com/p/shellinabox/issues/detail?id=261) discussion
If you have any questions, issues or patches, please fell free to submit pull
request or report an issue. You can also drop an email to original project
[issue #261](https://code.google.com/p/shellinabox/issues/detail?id=261) discusion
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
browser plugins.
![Shell In A Box preview](/misc/preview.gif?raw=true)
![Shell In A Box preview](/misc/preview.png?raw=true)
More information:
@ -42,17 +42,34 @@ Build
For building **shellinabox** from source on Debian or RHEL based systems use commands
listed below. This will create executable file `shellinaboxd` in project directory.
1. Clone the autotools
1. Install dependencies
```
wget https://raw.githubusercontent.com/simono41/shellinabox/master/auto.sh
apt-get install git libssl-dev libpam0g-dev zlib1g-dev dh-autoreconf
```
or
```
yum install git openssl-devel pam-devel zlib-devel autoconf automake libtool
```
3. Run autotools
2. Clone source files and move to project directory
```
chmod +x auto.sh
./auto.sh
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
@ -86,19 +103,5 @@ created from our sources. In new issue report please include following things:
* Version of shellinabox
* Steps to reproduce the problem
Also feel free to post any questions or comments in [shellinabox chat room](https://gitter.im/shellinabox/shellinabox)
Also feel free to post any questions or comments in [shellianbox chat room](https://gitter.im/shellinabox/shellinabox)
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 Executable file → Normal file
View file

56
auto.sh
View file

@ -1,56 +0,0 @@
#!/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 Executable file → Normal file
View file

@ -1,8 +1,7 @@
AC_PREREQ(2.57)
dnl This is one of the locations where the authoritative version
dnl number is stored. The other is in the debian/changelog.
AC_INIT(shellinabox, 2.20, markus@shellinabox.com)
dnl This is the one location where the authoritative version number is stored
AC_INIT(shellinabox, 2.15, markus@shellinabox.com)
if test -e .git; then
VCS_REVISION=" (revision `cd $srcdir && git log -1 --format=format:%h`)"
else
@ -17,8 +16,6 @@ CFLAGS="${CFLAGS:--Os}"
AM_INIT_AUTOMAKE([subdir-objects])
AM_CONFIG_HEADER(config.h)
AC_PROG_CC
dnl Added this for compatibility with older versions of autoconf/automake
AM_PROG_CC_C_O
AC_LANG_WERROR
AC_PROG_INSTALL
AC_PROG_LIBTOOL
@ -27,12 +24,9 @@ AC_C_CONST
AC_PROG_GCC_TRADITIONAL
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
AC_CHECK_HEADERS([libutil.h pthread.h pty.h strings.h syslog.h sys/prctl.h \
sys/uio.h util.h])
AC_CHECK_HEADERS([libutil.h pthread.h pty.h strings.h sys/prctl.h sys/uio.h \
util.h utmp.h utmpx.h])
dnl Most systems require linking against libutil.so in order to get login_tty()
AC_CHECK_FUNCS(login_tty, [],
@ -43,9 +37,6 @@ AC_CHECK_FUNCS(login_tty, [],
dnl Use strlcat() instead of strncat() to avoid spurious warnings
AC_CHECK_FUNCS([strlcat])
dnl Use vsyslog() for logging important error messages
AC_CHECK_FUNCS([vsyslog])
dnl Prefer thread-safe functions, if available
AC_CHECK_FUNCS([getgrgid_r getgrnam_r gethostbyname_r getpwnam_r getpwuid_r \
openpty strcasestr getresuid getresgid setresuid setresgid ])
@ -75,6 +66,15 @@ AC_TRY_LINK([#include <math.h>],
[AC_DEFINE(HAVE_ISNAN, 1,
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
AC_TRY_LINK([void x(void) { };
@ -138,28 +138,6 @@ AC_ARG_ENABLE(runtime-loading,
these libraries into the binary, thus making them a
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
if test "x$enable_ssl" != xno; then
AC_CHECK_HEADERS([openssl/bio.h openssl/err.h openssl/ssl.h])

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

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

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

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

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

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

@ -1,68 +1,3 @@
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
* Improved clean-liness so that source archive is clean.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2
debian/watch vendored Normal file
View file

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -61,7 +61,6 @@
#undef HAVE_OPENSSL
typedef struct BIO BIO;
typedef struct BIO_METHOD BIO_METHOD;
typedef struct EC_KEY EC_KEY;
typedef struct SSL SSL;
typedef struct SSL_CTX SSL_CTX;
typedef struct SSL_METHOD SSL_METHOD;
@ -70,35 +69,23 @@ typedef struct X509 X509;
#define SSL_ERROR_WANT_WRITE 3
#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)
extern long (*x_BIO_ctrl)(BIO *, int, long, void *);
extern BIO_METHOD *(*x_BIO_f_buffer)(void);
extern void (*x_BIO_free_all)(BIO *);
extern BIO *(*x_BIO_new)(BIO_METHOD *);
extern BIO *(*x_BIO_new_socket)(int, int);
extern BIO *(*x_BIO_next)(BIO *);
extern BIO *(*x_BIO_pop)(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 unsigned long (*x_ERR_peek_error)(void);
extern unsigned long (*x_ERR_peek_error)(void);
extern long (*x_SSL_CTX_callback_ctrl)(SSL_CTX *, int, void (*)(void));
extern int (*x_SSL_CTX_check_private_key)(const SSL_CTX *);
extern long (*x_SSL_CTX_ctrl)(SSL_CTX *, int, long, void *);
extern void (*x_SSL_CTX_free)(SSL_CTX *);
extern SSL_CTX*(*x_SSL_CTX_new)(SSL_METHOD *);
extern int (*x_SSL_CTX_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_ASN1)(int, SSL_CTX *,
const unsigned char *, long);
@ -124,6 +111,7 @@ extern int (*x_SSL_write)(SSL *, const void *, int);
extern SSL_METHOD *(*x_SSLv23_server_method)(void);
extern X509 * (*x_d2i_X509)(X509 **px, const unsigned char **in, int len);
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_SSL_COMP_get_compression_methods)(void);
@ -132,20 +120,17 @@ extern void *(*x_SSL_COMP_get_compression_methods)(void);
#define BIO_free_all x_BIO_free_all
#define BIO_new x_BIO_new
#define BIO_new_socket x_BIO_new_socket
#define BIO_next x_BIO_next
#define BIO_pop x_BIO_pop
#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_peek_error x_ERR_peek_error
#define ERR_peek_error x_ERR_peek_error
#define SSL_CTX_callback_ctrl x_SSL_CTX_callback_ctrl
#define SSL_CTX_check_private_key x_SSL_CTX_check_private_key
#define SSL_CTX_ctrl x_SSL_CTX_ctrl
#define SSL_CTX_free x_SSL_CTX_free
#define SSL_CTX_new x_SSL_CTX_new
#define SSL_CTX_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_ASN1 x_SSL_CTX_use_PrivateKey_ASN1
#define SSL_CTX_use_certificate_file x_SSL_CTX_use_certificate_file
@ -169,13 +154,13 @@ extern void *(*x_SSL_COMP_get_compression_methods)(void);
#define SSLv23_server_method x_SSLv23_server_method
#define d2i_X509 x_d2i_X509
#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 SSL_COMP_get_compression_methods x_SSL_COMP_get_compression_methods
#undef BIO_set_buffer_read_data
#undef SSL_CTX_set_tlsext_servername_arg
#undef SSL_CTX_set_tlsext_servername_callback
#undef SSL_CTX_set_tmp_ecdh
#undef SSL_get_app_data
#undef SSL_set_app_data
#undef SSL_set_mode
@ -190,9 +175,6 @@ extern void *(*x_SSL_COMP_get_compression_methods)(void);
(x_SSL_CTX_callback_ctrl(ctx, \
SSL_CTRL_SET_TLSEXT_SERVERNAME_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_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))
@ -200,11 +182,9 @@ extern void *(*x_SSL_COMP_get_compression_methods)(void);
struct SSLSupport {
int enabled;
int force;
SSL_CTX *sslContext;
char *sniCertificatePattern;
int generateMissing;
int renegotiationCount;
struct Trie sniContexts;
};
@ -217,7 +197,6 @@ void sslSetCertificate(struct SSLSupport *ssl, const char *filename,
int autoGenerateMissing);
void sslSetCertificateFd(struct SSLSupport *ssl, int fd);
int sslEnable(struct SSLSupport *ssl, int enabled);
int sslForce(struct SSLSupport *ssl, int force);
void sslBlockSigPipe();
int sslUnblockSigPipe();
int sslPromoteToSSL(struct SSLSupport *ssl, SSL **sslHndl, int fd,

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

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

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

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

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

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

@ -52,19 +52,6 @@
#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 void debugMsg(int level, const char *fmt, va_list ap) {
@ -99,13 +86,6 @@ void error(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
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);
}
@ -120,14 +100,6 @@ void fatal(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
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);
_exit(1);
}

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

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

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

@ -14,7 +14,7 @@
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
URL of parent (this) window. If origin is set to '*' Shell In A Box won't check
URL of parent (this) window. If origin is set to '*' Shell In A Box won't checki
origin on received messages. This is usually unsafe option.
Command line example:
@ -25,7 +25,7 @@
# Client Side
#
Shell In A Box accepts messages formatted as JSON strings with 'type' and 'data'
Shell In A Box accepts messages formated as JSON strings with 'type' and 'data'
fields. Messages with same format can be passed back to parent (this) window.
Message example:
@ -59,9 +59,6 @@
Following types of messages can be received from shellinabox:
* ready
signals that shellinabox is ready to send and receive messages
* output
data field contains terminal output
@ -143,6 +140,10 @@
var output = document.getElementById("output");
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() {
// Send input to shellinabox
var message = JSON.stringify({
@ -208,15 +209,6 @@
// Handle response according to response type
var decoded = JSON.parse(message.data);
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" :
// Append new output
output.innerHTML = output.innerHTML + decoded.data;
@ -228,9 +220,6 @@
}
}, false);
// Add url to our iframe after the event listener is installed.
iframe.src = url;
</script>
</body>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 930 KiB

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

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

22
sgit
View file

@ -1,22 +0,0 @@
#!/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

View file

@ -1,9 +0,0 @@
[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 Executable file → Normal file
View file

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

@ -1,16 +0,0 @@
#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 Executable file → Normal file
View file

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

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

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

Before

Width:  |  Height:  |  Size: 847 B

After

Width:  |  Height:  |  Size: 847 B

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

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

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

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

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

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

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -406,9 +406,6 @@ 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) {
@ -467,17 +464,16 @@ ShellInABox.prototype.messageReplay = function(type, data) {
};
ShellInABox.prototype.about = function() {
alert("Shell In A Box " + VERSION +
"\n\n" +
"Copyright 2008-2015 by Markus Gutschke. For more information visit\n" +
"http://shellinabox.com or http://github.com/shellinabox/." +
alert("Shell In A Box version " + VERSION +
"\nCopyright 2008-2010 by Markus Gutschke\n" +
"For more information check http://shellinabox.com" +
(typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ?
"\n\n" +
"This product includes software developed by the OpenSSL Project for\n" +
"use in the OpenSSL Toolkit. (http://www.openssl.org/)" +
"\n\n" +
"This product includes cryptographic software written by Eric Young\n" +
"(eay@cryptsoft.com)" :
"This product includes software developed by the OpenSSL Project\n" +
"for use in the OpenSSL Toolkit. (http://www.openssl.org/)\n" +
"\n" +
"This product includes cryptographic software written by " +
"Eric Young\n(eay@cryptsoft.com)" :
""));
};

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

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

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

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

View file

@ -243,12 +243,6 @@
[else DEFINES_COLORS]
/* SYSTEM colors */
#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 .ansi1 { color: #cd0000; }
@ -511,13 +505,6 @@
/* SYSTEM colors */
#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 .bgAnsi1 { background-color: #cd0000; }

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

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

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

View file

@ -83,7 +83,6 @@
#define EStitle 17
#define ESss2 18
#define ESss3 19
#define ESVTEtitle 20
#define ATTR_DEFAULT 0x60F0
#define ATTR_REVERSE 0x0100
@ -298,12 +297,6 @@ VT100.prototype.getUserSettings = function() {
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) {
this.signature = Math.floor(16807*this.signature + 1) %
((1 << 31) - 1);
@ -1205,8 +1198,8 @@ VT100.prototype.resizer = function() {
document.documentElement.clientHeight ||
document.body.clientHeight))-1;
// Prevent ever growing console on some iOS clients.
if (navigator.userAgent.match(/iPad|iPhone|iPod/i) != null) {
// Prevent ever growing consoles on iPad.
if (navigator.userAgent.match(/iPad/i) != null) {
height -= 1;
}
@ -1436,25 +1429,6 @@ VT100.prototype.mouseEvent = function(event, type) {
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) {
try {
event.shiftKey = false;
@ -1604,10 +1578,7 @@ VT100.prototype.updateWidth = function() {
this.terminalWidth = Math.floor(this.console[this.currentScreen].offsetWidth/this.cursorWidth*this.scale);
}
} 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);
}
return this.terminalWidth;
@ -2844,7 +2815,7 @@ VT100.prototype.handleKey = function(event) {
ch = part1 +
((event.shiftKey ? 1 : 0) +
(event.altKey|event.metaKey ? 2 : 0) +
(event.ctrlKey ? 4 : 0) + 1) +
(event.ctrlKey ? 4 : 0)) +
part2;
} else if (ch.length == 1 && (event.altKey || event.metaKey)
&& !this.disableAlt) {
@ -2914,9 +2885,9 @@ VT100.prototype.fixEvent = function(event) {
// 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
// only works for US keyboard layouts.
var u = undefined;
var s = undefined;
if (event.shiftKey) {
var u = undefined;
var s = undefined;
switch (this.lastNormalKeyDownEvent.keyCode) {
case 39: /* ' -> " */ u = 39; s = 34; break;
case 44: /* , -> < */ u = 44; s = 60; break;
@ -2957,23 +2928,17 @@ VT100.prototype.fixEvent = function(event) {
case 222: /* ' -> " */ u = 39; s = 34; 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)) {
var fake = [ ];
fake.charCode = s;
fake.keyCode = event.keyCode;
fake.ctrlKey = event.ctrlKey;
fake.shiftKey = event.shiftKey;
fake.altKey = event.altKey;
fake.metaKey = event.metaKey;
return fake;
}
}
if (s && (event.charCode == u || event.charCode == 0)) {
var fake = [ ];
fake.charCode = s;
fake.keyCode = event.keyCode;
fake.ctrlKey = event.ctrlKey;
fake.shiftKey = event.shiftKey;
fake.altKey = event.altKey;
fake.metaKey = event.metaKey;
return fake;
}
return event;
};
@ -3058,8 +3023,10 @@ VT100.prototype.keyDown = function(event) {
} else {
fake.charCode = 0;
fake.keyCode = event.keyCode;
if (!alphNumKey && event.shiftKey) {
fake = this.fixEvent(fake);
}
}
fake = this.fixEvent(fake);
this.handleKey(fake);
this.lastNormalKeyDownEvent = undefined;
@ -3174,9 +3141,9 @@ VT100.prototype.keyUp = function(event) {
} else {
fake.charCode = 0;
fake.keyCode = event.keyCode;
}
if (event.ctrlKey || event.altKey || event.metaKey) {
fake = this.fixEvent(fake);
if (!alphNumKey && (event.ctrlKey || event.altKey || event.metaKey)) {
fake = this.fixEvent(fake);
}
}
this.lastNormalKeyDownEvent = undefined;
this.handleKey(fake);
@ -3384,50 +3351,44 @@ VT100.prototype.respondSecondaryDA = function() {
VT100.prototype.updateStyle = function() {
var fg = '';
var bg = '';
this.style = '';
this.style = '';
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) {
this.style += 'text-decoration: blink;';
this.style = 'text-decoration: blink;';
}
// Forground color
// Default colors
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) {
// 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;';
}
fg = this.attrFg;
}
// Background color
if (this.attrBg) {
// 256 color mode
bg = this.attrBg
} else if (this.attr & ATTR_DEF_BG) {
bg = 'Def';
} else {
bg = (this.attr >> 4) & 0xF;
bg = this.attrBg;
}
// 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;
};
VT100.prototype.setAttrColors = function(attr) {
@ -3908,7 +3869,7 @@ VT100.prototype.csim = function() {
case 27: this.attr &= ~ ATTR_REVERSE; break;
case 38:
if (this.npar >= (i+2) && this.par[i+1] == 5) {
// Foreground color for extended color mode (256 colors). Escape code is formatted
// Foreground color for extended color mode (256 colors). Escape code is formated
// as: ESC 38; 5; 0-255. Last parameter is color code in range [0-255]. This is
// not VT100 standard.
this.attrFg = (this.par[i+2] >= 0 && this.par[i+2] <= 255) ? this.par[i+2] : false;
@ -3924,7 +3885,7 @@ VT100.prototype.csim = function() {
break;
case 48:
if (this.npar >= (i+2) && this.par[i+1] == 5) {
// Background color for extended color mode (256 colors). Escape code is formatted
// Background color for extended color mode (256 colors). Escape code is formated
// as: ESC 48; 5; 0-255. Last parameter is color code in range [0-255]. This is
// not VT100 standard.
this.attrBg = (this.par[i+2] >= 0 && this.par[i+2] <= 255) ? this.par[i+2] : false;
@ -4054,8 +4015,6 @@ VT100.prototype.doControl = function(ch) {
/*0*/ case 0x30:
/*1*/ case 0x31:
/*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 ];
this.isEsc = ESpalette; break;
/*R*/ case 0x52: // Palette support is not implemented
@ -4283,13 +4242,6 @@ VT100.prototype.doControl = function(ch) {
this.lastCharacter = String.fromCharCode(ch);
lineBuf += this.lastCharacter;
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:
this.isEsc = ESnormal; break;
}

View file

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

View file

@ -1,32 +0,0 @@
#!/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