mirror of https://github.com/sonertari/SSLproxy
moved to develop branch
commit
b34336ab4b
@ -0,0 +1,20 @@
|
||||
# Authors
|
||||
|
||||
SSLsplit was written and is being maintained by
|
||||
[Daniel Roethlisberger](https://daniel.roe.ch/).
|
||||
|
||||
The following individuals have contributed to the codebase by submitting
|
||||
patches or pull requests, in chronological order of their first contribution:
|
||||
|
||||
- Steve Wills ([swills](https://github.com/swills))
|
||||
- Landon Fuller ([landonf](https://github.com/landonf))
|
||||
- Wayne Jensen ([wjjensen](https://github.com/wjjensen))
|
||||
|
||||
Many more individuals have contributed by reporting bugs or feature requests.
|
||||
See [issue tracker on Github][1], `NEWS.md` and `git log` for details.
|
||||
|
||||
[1]: https://github.com/droe/sslsplit/issues
|
||||
|
||||
All your contributions are greatly appreciated; without you, SSLsplit would not
|
||||
be what it is today.
|
||||
|
@ -0,0 +1,51 @@
|
||||
# Development
|
||||
|
||||
SSLsplit is being developed on Github as [droe/sslsplit][1].
|
||||
|
||||
[1]: https://github.com/droe/sslsplit
|
||||
|
||||
|
||||
## Reporting bugs
|
||||
|
||||
Please use the Github issue tracker for bug reports. Including the following
|
||||
information will allow faster analysis of the problem:
|
||||
|
||||
- Output of `sslsplit -V`
|
||||
- Output of `uname -a`
|
||||
- Exact command line arguments used to run SSLsplit
|
||||
- The NAT redirection rules you are using, if applicable
|
||||
|
||||
Before submitting a bug report, please make sure that running `make test` from
|
||||
a git checkout produces no failed unit tests on your system.
|
||||
|
||||
|
||||
## Contributing patches
|
||||
|
||||
For patch submissions, please send me pull requests on Github. If you have
|
||||
larger changes in mind, feel free to open an issue first to discuss
|
||||
implications. If you are interested in contributing and don't know where to
|
||||
start, take a look at the [open issues][2]. In particular, [porting features
|
||||
over to not yet supported platforms][3] is always very much appreciated. When
|
||||
submitting code, even though it is not a requirement, it is still appreciated
|
||||
if you also update the manual page and other documentation as necessary and
|
||||
include as many meaningful unit tests for your code as possible.
|
||||
|
||||
[2]: https://github.com/droe/sslsplit/issues
|
||||
[3]: https://github.com/droe/sslsplit/labels/portability
|
||||
|
||||
See `LICENSE.md` for licensing and copyright information applying to
|
||||
contributions. See `AUTHORS.md` for the list of contributors.
|
||||
|
||||
|
||||
## Branching model
|
||||
|
||||
With the 0.4.10 release as a starting point, SSLsplit is using [Vincent
|
||||
Driessen's branching model][4]. The default `master` branch points to the
|
||||
latest tagged release, while the `develop` branch is where development happens.
|
||||
When preparing a release, there may or may not be a `release/x.y.z` branch off
|
||||
`develop`, but in either case, the tagged release is merged back to `master`.
|
||||
Larger features are developed in feature branches off the `develop` branch.
|
||||
|
||||
[4]: http://nvie.com/posts/a-successful-git-branching-model/
|
||||
|
||||
|
@ -1,25 +0,0 @@
|
||||
SSLsplit - transparent and scalable SSL/TLS interception
|
||||
Copyright (c) 2009-2014, Daniel Roethlisberger <daniel@roe.ch>
|
||||
All rights reserved.
|
||||
http://www.roe.ch/SSLsplit
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice unmodified, this list of conditions, and the following
|
||||
disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -0,0 +1,60 @@
|
||||
# License and copyright
|
||||
|
||||
## Copyright
|
||||
|
||||
Copyright (c) 2009-2014, Daniel Roethlisberger and contributors.
|
||||
All rights reserved.
|
||||
Licensed under the 2-clause BSD license contained herein.
|
||||
|
||||
|
||||
## Third-party components
|
||||
|
||||
`khash.h`:
|
||||
Copyright (c) 2008, 2009, 2011, Attractive Chaos.
|
||||
All rights reserved.
|
||||
Licensed under the MIT license.
|
||||
|
||||
`xnu/xnu-*`:
|
||||
Copyright (c) 1988-2010, Apple Inc. and original copyright holders.
|
||||
All rights reserved.
|
||||
Licensed under the APSL.
|
||||
|
||||
See the respective source and/or license files for details.
|
||||
|
||||
|
||||
## Contributions
|
||||
|
||||
By contributing to the software, the contributor releases their
|
||||
contribution under the license and copyright terms herein. While
|
||||
contributors retain copyright to their contributions, they grant the
|
||||
main copyright holder of the software the irrevocable, transferable
|
||||
right to relicense the software as a whole or in part, including their
|
||||
contributions, under different open source licenses than the one
|
||||
contained herein.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice unmodified, this list of conditions, and the following
|
||||
disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* SSLsplit - transparent and scalable SSL/TLS interception
|
||||
* Copyright (c) 2009-2014, Daniel Roethlisberger <daniel@roe.ch>
|
||||
* All rights reserved.
|
||||
* http://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef DEFAULTS_H
|
||||
#define DEFAULTS_H
|
||||
|
||||
/*
|
||||
* Defaults for convenient tweaking or patching.
|
||||
*/
|
||||
|
||||
/*
|
||||
* User to drop privileges to by default.
|
||||
* Packagers may want to use a specific service user account instead of
|
||||
* overloading nobody with yet another use case. Using nobody for source
|
||||
* builds makes sense because chances are high that it exists.
|
||||
*/
|
||||
#define DFLT_DROPUSER "nobody"
|
||||
|
||||
/*
|
||||
* Default file and directory modes for newly created files and directories
|
||||
* created as part of e.g. logging. The default is to use full permissions
|
||||
* subject to the system's umask, as is the default for system utilities.
|
||||
* Use a more restrictive mode for the PID file.
|
||||
*/
|
||||
#define DFLT_DIRMODE 0777
|
||||
#define DFLT_FILEMODE 0666
|
||||
#define DFLT_PIDFMODE 0644
|
||||
|
||||
/*
|
||||
* Default cipher suite spec.
|
||||
* Use 'openssl ciphers -v spec' to see what ciphers are effectively enabled
|
||||
* by a cipher suite spec with a given version of OpenSSL.
|
||||
*/
|
||||
#define DFLT_CIPHERS "ALL:-aNULL"
|
||||
|
||||
/*
|
||||
* Default elliptic curve for EC cipher suites.
|
||||
*/
|
||||
#define DFLT_CURVE "secp160r2"
|
||||
|
||||
#endif /* !DEFAULTS_H */
|
||||
|
||||
/* vim: set noet ft=c: */
|
@ -0,0 +1,771 @@
|
||||
/*
|
||||
* SSLsplit - transparent and scalable SSL/TLS interception
|
||||
* Copyright (c) 2009-2014, Daniel Roethlisberger <daniel@roe.ch>
|
||||
* All rights reserved.
|
||||
* http://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "privsep.h"
|
||||
|
||||
#include "sys.h"
|
||||
#include "util.h"
|
||||
#include "log.h"
|
||||
#include "attrib.h"
|
||||
#include "defaults.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/wait.h>
|
||||
#include <netinet/in.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
/*
|
||||
* Privilege separation functionality.
|
||||
*
|
||||
* The server code has limitations on the internal functionality that can be
|
||||
* used, namely only those that are initialized before forking.
|
||||
*/
|
||||
|
||||
/* maximal message sizes */
|
||||
#define PRIVSEP_MAX_REQ_SIZE 512 /* arbitrary limit */
|
||||
#define PRIVSEP_MAX_ANS_SIZE (1+sizeof(int))
|
||||
/* command byte */
|
||||
#define PRIVSEP_REQ_CLOSE 0 /* closing command socket */
|
||||
#define PRIVSEP_REQ_OPENFILE 1 /* open content log file */
|
||||
#define PRIVSEP_REQ_OPENFILE_P 2 /* open content log file w/mkpath */
|
||||
#define PRIVSEP_REQ_OPENSOCK 3 /* open socket and pass fd */
|
||||
/* response byte */
|
||||
#define PRIVSEP_ANS_SUCCESS 0 /* success */
|
||||
#define PRIVSEP_ANS_UNK_CMD 1 /* unknown command */
|
||||
#define PRIVSEP_ANS_INVALID 2 /* invalid message */
|
||||
#define PRIVSEP_ANS_DENIED 3 /* request denied */
|
||||
#define PRIVSEP_ANS_SYS_ERR 4 /* system error; arg=errno */
|
||||
|
||||
/* communication with signal handler */
|
||||
static int received_sighup;
|
||||
static int received_sigint;
|
||||
static int received_sigquit;
|
||||
static int received_sigchld;
|
||||
static int received_sigusr1;
|
||||
static int selfpipe_wrfd; /* write end of pipe used for unblocking select */
|
||||
|
||||
static void
|
||||
privsep_server_signal_handler(int sig)
|
||||
{
|
||||
int saved_errno;
|
||||
|
||||
saved_errno = errno;
|
||||
switch (sig) {
|
||||
case SIGHUP:
|
||||
received_sighup = 1;
|
||||
break;
|
||||
case SIGINT:
|
||||
received_sigint = 1;
|
||||
break;
|
||||
case SIGQUIT:
|
||||
received_sigquit = 1;
|
||||
break;
|
||||
case SIGCHLD:
|
||||
received_sigchld = 1;
|
||||
break;
|
||||
case SIGUSR1:
|
||||
received_sigusr1 = 1;
|
||||
break;
|
||||
}
|
||||
if (selfpipe_wrfd != -1) {
|
||||
ssize_t n;
|
||||
|
||||
do {
|
||||
n = write(selfpipe_wrfd, "!", 1);
|
||||
} while (n == -1 && errno == EINTR);
|
||||
if (n == -1) {
|
||||
log_err_printf("Failed to write from signal handler: "
|
||||
"%s (%i)\n", strerror(errno), errno);
|
||||
/* ignore error */
|
||||
}
|
||||
}
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
static int WUNRES
|
||||
privsep_server_openfile_verify(opts_t *opts, char *fn, int mkpath)
|
||||
{
|
||||
if (mkpath && !opts->contentlog_isspec)
|
||||
return -1;
|
||||
if (!mkpath && !opts->contentlog_isdir)
|
||||
return -1;
|
||||
if (strstr(fn, mkpath ? opts->contentlog_basedir
|
||||
: opts->contentlog) != fn ||
|
||||
strstr(fn, "/../"))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WUNRES
|
||||
privsep_server_openfile(char *fn, int mkpath)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (mkpath) {
|
||||
char *filedir, *fn2;
|
||||
|
||||
fn2 = strdup(fn);
|
||||
if (!fn2) {
|
||||
log_err_printf("Could not duplicate filname: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
filedir = dirname(fn2);
|
||||
if (!filedir) {
|
||||
log_err_printf("Could not get dirname: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
free(fn2);
|
||||
return -1;
|
||||
}
|
||||
if (sys_mkpath(filedir, DFLT_DIRMODE) == -1) {
|
||||
log_err_printf("Could not create directory '%s': %s (%i)\n",
|
||||
filedir, strerror(errno), errno);
|
||||
free(fn2);
|
||||
return -1;
|
||||
}
|
||||
free(fn2);
|
||||
}
|
||||
|
||||
fd = open(fn, O_WRONLY|O_APPEND|O_CREAT, DFLT_FILEMODE);
|
||||
if (fd == -1) {
|
||||
log_err_printf("Failed to open '%s': %s (%i)\n",
|
||||
fn, strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int WUNRES
|
||||
privsep_server_opensock_verify(opts_t *opts, void *arg)
|
||||
{
|
||||
for (proxyspec_t *spec = opts->spec; spec; spec = spec->next) {
|
||||
if (spec == arg)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int WUNRES
|
||||
privsep_server_opensock(proxyspec_t *spec)
|
||||
{
|
||||
evutil_socket_t fd;
|
||||
int on = 1;
|
||||
int rv;
|
||||
|
||||
fd = socket(spec->listen_addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (fd == -1) {
|
||||
log_err_printf("Error from socket(): %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
evutil_closesocket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = evutil_make_socket_nonblocking(fd);
|
||||
if (rv == -1) {
|
||||
log_err_printf("Error making socket nonblocking: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
evutil_closesocket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&on, sizeof(on));
|
||||
if (rv == -1) {
|
||||
log_err_printf("Error from setsockopt(SO_KEEPALIVE): %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
evutil_closesocket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = evutil_make_listen_socket_reuseable(fd);
|
||||
if (rv == -1) {
|
||||
log_err_printf("Error from setsockopt(SO_REUSABLE): %s\n",
|
||||
strerror(errno));
|
||||
evutil_closesocket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (spec->natsocket && (spec->natsocket(fd) == -1)) {
|
||||
log_err_printf("Error from spec->natsocket()\n");
|
||||
evutil_closesocket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = bind(fd, (struct sockaddr *)&spec->listen_addr,
|
||||
spec->listen_addrlen);
|
||||
if (rv == -1) {
|
||||
log_err_printf("Error from bind(): %s\n", strerror(errno));
|
||||
evutil_closesocket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a single request on a readable server socket.
|
||||
* Returns 0 on success, 1 on EOF and -1 on error.
|
||||
*/
|
||||
static int WUNRES
|
||||
privsep_server_handle_req(opts_t *opts, int srvsock)
|
||||
{
|
||||
char req[PRIVSEP_MAX_REQ_SIZE];
|
||||
char ans[PRIVSEP_MAX_ANS_SIZE];
|
||||
ssize_t n;
|
||||
int mkpath = 0;
|
||||
|
||||
if ((n = sys_recvmsgfd(srvsock, req, sizeof(req),
|
||||
NULL)) == -1) {
|
||||
if (errno == EPIPE || errno == ECONNRESET) {
|
||||
/* unfriendly EOF, leave server */
|
||||
return 1;
|
||||
}
|
||||
log_err_printf("Failed to receive msg: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
if (n == 0) {
|
||||
/* EOF, leave server; will not happen for SOCK_DGRAM sockets */
|
||||
return 1;
|
||||
}
|
||||
log_dbg_printf("Received privsep req type %02x sz %zd on srvsock %i\n",
|
||||
req[0], n, srvsock);
|
||||
switch (req[0]) {
|
||||
case PRIVSEP_REQ_CLOSE: {
|
||||
/* client indicates EOF through close message */
|
||||
return 1;
|
||||
}
|
||||
case PRIVSEP_REQ_OPENFILE_P:
|
||||
mkpath = 1;
|
||||
case PRIVSEP_REQ_OPENFILE: {
|
||||
char *fn;
|
||||
int fd;
|
||||
|
||||
if (n < 2) {
|
||||
ans[0] = PRIVSEP_ANS_INVALID;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) {
|
||||
log_err_printf("Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (!(fn = malloc(n))) {
|
||||
ans[0] = PRIVSEP_ANS_SYS_ERR;
|
||||
*((int*)&ans[1]) = errno;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(int),
|
||||
-1) == -1) {
|
||||
log_err_printf("Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
memcpy(fn, req + 1, n - 1);
|
||||
fn[n - 1] = '\0';
|
||||
if (privsep_server_openfile_verify(opts, fn, mkpath) == -1) {
|
||||
free(fn);
|
||||
ans[0] = PRIVSEP_ANS_DENIED;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) {
|
||||
log_err_printf("Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if ((fd = privsep_server_openfile(fn, mkpath)) == -1) {
|
||||
free(fn);
|
||||
ans[0] = PRIVSEP_ANS_SYS_ERR;
|
||||
*((int*)&ans[1]) = errno;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(int),
|
||||
-1) == -1) {
|
||||
log_err_printf("Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
free(fn);
|
||||
ans[0] = PRIVSEP_ANS_SUCCESS;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1, fd) == -1) {
|
||||
close(fd);
|
||||
log_err_printf("Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
/* not reached */
|
||||
break;
|
||||
}
|
||||
case PRIVSEP_REQ_OPENSOCK: {
|
||||
proxyspec_t *arg;
|
||||
int s;
|
||||
|
||||
if (n != sizeof(char) + sizeof(arg)) {
|
||||
ans[0] = PRIVSEP_ANS_INVALID;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) {
|
||||
log_err_printf("Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
arg = *(proxyspec_t**)(&req[1]);
|
||||
if (privsep_server_opensock_verify(opts, arg) == -1) {
|
||||
ans[0] = PRIVSEP_ANS_DENIED;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) {
|
||||
log_err_printf("Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if ((s = privsep_server_opensock(arg)) == -1) {
|
||||
ans[0] = PRIVSEP_ANS_SYS_ERR;
|
||||
*((int*)&ans[1]) = errno;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(int),
|
||||
-1) == -1) {
|
||||
log_err_printf("Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
ans[0] = PRIVSEP_ANS_SUCCESS;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1, s) == -1) {
|
||||
evutil_closesocket(s);
|
||||
log_err_printf("Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
evutil_closesocket(s);
|
||||
return 0;
|
||||
}
|
||||
/* not reached */
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ans[0] = PRIVSEP_ANS_UNK_CMD;
|
||||
if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) {
|
||||
log_err_printf("Sending message failed: %s (%i"
|
||||
")\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Privilege separation server (main privileged monitor loop)
|
||||
*
|
||||
* sigpipe is the self-pipe trick pipe used for communicating signals to
|
||||
* the main event loop and break out of select() without race conditions.
|
||||
* srvsock[] is a dynamic array of connected privsep server sockets to serve.
|
||||
* Caller is responsible for freeing memory after returning, if necessary.
|
||||
* childpid is the pid of the child process to forward signals to.
|
||||
*
|
||||
* Returns 0 on a successful clean exit and -1 on errors.
|
||||
*/
|
||||
static int
|
||||
privsep_server(opts_t *opts, int sigpipe, int srvsock[], size_t nsrvsock,
|
||||
pid_t childpid)
|
||||
{
|
||||
int srveof[nsrvsock];
|
||||
size_t i = 0;
|
||||
|
||||
for (i = 0; i < nsrvsock; i++) {
|
||||
srveof[i] = 0;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
fd_set readfds;
|
||||
int maxfd, rv;
|
||||
|
||||
do {
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(sigpipe, &readfds);
|
||||
maxfd = sigpipe;
|
||||
for (i = 0; i < nsrvsock; i++) {
|
||||
if (!srveof[i]) {
|
||||
FD_SET(srvsock[i], &readfds);
|
||||
maxfd = util_max(maxfd, srvsock[i]);
|
||||
}
|
||||
}
|
||||
rv = select(maxfd + 1, &readfds, NULL, NULL, NULL);
|
||||
} while (rv == -1 && errno == EINTR);
|
||||
if (rv == -1) {
|
||||
log_err_printf("Select failed: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (FD_ISSET(sigpipe, &readfds)) {
|
||||
char buf[16];
|
||||
/* first drain the signal pipe, then deal with
|
||||
* all the individual signal flags */
|
||||
read(sigpipe, buf, sizeof(buf));
|
||||
if (received_sigquit) {
|
||||
kill(childpid, SIGQUIT);
|
||||
received_sigquit = 0;
|
||||
}
|
||||
if (received_sighup) {
|
||||
kill(childpid, SIGHUP);
|
||||
received_sighup = 0;
|
||||
}
|
||||
if (received_sigusr1) {
|
||||
kill(childpid, SIGUSR1);
|
||||
received_sigusr1 = 0;
|
||||
}
|
||||
if (received_sigint) {
|
||||
/* if we don't detach from the TTY, the
|
||||
* child process receives SIGINT directly */
|
||||
if (opts->detach)
|
||||
kill(childpid, SIGINT);
|
||||
received_sigint = 0;
|
||||
}
|
||||
if (received_sigchld) {
|
||||
/* break the loop; because we are using
|
||||
* SOCKET_DGRAM we don't get EOF conditions
|
||||
* on the disconnected socket ends here
|
||||
* unless we attempt to write or read, so
|
||||
* we depend on SIGCHLD to notify us of
|
||||
* our child erroring out or crashing */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < nsrvsock; i++) {
|
||||
if (FD_ISSET(srvsock[i], &readfds)) {
|
||||
int rv = privsep_server_handle_req(opts,
|
||||
srvsock[i]);
|
||||
if (rv == -1) {
|
||||
log_err_printf("Failed to handle "
|
||||
"privsep req "
|
||||
"on srvsock %i\n",
|
||||
srvsock[i]);
|
||||
return -1;
|
||||
}
|
||||
if (rv == 1)
|
||||
srveof[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* break if all server sockets received an EOF */
|
||||
i = 0;
|
||||
while (i < nsrvsock && srveof[i])
|
||||
i++;
|
||||
if (i == nsrvsock)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
privsep_client_openfile(int clisock, const char *fn, int mkpath)
|
||||
{
|
||||
char ans[PRIVSEP_MAX_ANS_SIZE];
|
||||
char req[1 + strlen(fn)];
|
||||
int fd = -1;
|
||||
ssize_t n;
|
||||
|
||||
req[0] = mkpath ? PRIVSEP_REQ_OPENFILE_P : PRIVSEP_REQ_OPENFILE;
|
||||
memcpy(req + 1, fn, sizeof(req) - 1);
|
||||
|
||||
if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((n = sys_recvmsgfd(clisock, ans, sizeof(ans), &fd)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (n < 1) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (ans[0]) {
|
||||
case PRIVSEP_ANS_SUCCESS:
|
||||
break;
|
||||
case PRIVSEP_ANS_DENIED:
|
||||
errno = EACCES;
|
||||
return -1;
|
||||
case PRIVSEP_ANS_SYS_ERR:
|
||||
if (n < (ssize_t)(1 + sizeof(int))) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
errno = *((int*)&ans[1]);
|
||||
return -1;
|
||||
case PRIVSEP_ANS_UNK_CMD:
|
||||
case PRIVSEP_ANS_INVALID:
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int
|
||||
privsep_client_opensock(int clisock, const proxyspec_t *spec)
|
||||
{
|
||||
char ans[PRIVSEP_MAX_ANS_SIZE];
|
||||
char req[1 + sizeof(spec)];
|
||||
int fd = -1;
|
||||
ssize_t n;
|
||||
|
||||
req[0] = PRIVSEP_REQ_OPENSOCK;
|
||||
*((const proxyspec_t **)&req[1]) = spec;
|
||||
|
||||
if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((n = sys_recvmsgfd(clisock, ans, sizeof(ans), &fd)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (n < 1) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (ans[0]) {
|
||||
case PRIVSEP_ANS_SUCCESS:
|
||||
break;
|
||||
case PRIVSEP_ANS_DENIED:
|
||||
errno = EACCES;
|
||||
return -1;
|
||||
case PRIVSEP_ANS_SYS_ERR:
|
||||
if (n < (ssize_t)(1 + sizeof(int))) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
errno = *((int*)&ans[1]);
|
||||
return -1;
|
||||
case PRIVSEP_ANS_UNK_CMD:
|
||||
case PRIVSEP_ANS_INVALID:
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int
|
||||
privsep_client_close(int clisock)
|
||||
{
|
||||
char req[1];
|
||||
|
||||
req[0] = PRIVSEP_REQ_CLOSE;
|
||||
|
||||
if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) {
|
||||
close(clisock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(clisock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fork and set up privilege separated monitor process.
|
||||
* Returns -1 on error before forking, 1 as parent, or 0 as child.
|
||||
* The array of clisock's will get filled with nclisock privsep client
|
||||
* sockets only for the child; on error and in the parent process it
|
||||
* will not be touched.
|
||||
*/
|
||||
int
|
||||
privsep_fork(opts_t *opts, int clisock[], size_t nclisock)
|
||||
{
|
||||
int selfpipev[2]; /* self-pipe trick: signal handler -> select */
|
||||
int chldpipev[2]; /* el cheapo interprocess sync early after fork */
|
||||
int sockcliv[nclisock][2];
|
||||
pid_t pid;
|
||||
|
||||
received_sigquit = 0;
|
||||
received_sighup = 0;
|
||||
received_sigint = 0;
|
||||
received_sigchld = 0;
|
||||
received_sigusr1 = 0;
|
||||
|
||||
if (pipe(selfpipev) == -1) {
|
||||
log_err_printf("Failed to create self-pipe: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
log_dbg_printf("Created self-pipe [r=%i,w=%i]\n",
|
||||
selfpipev[0], selfpipev[1]);
|
||||
|
||||
if (pipe(chldpipev) == -1) {
|
||||
log_err_printf("Failed to create chld-pipe: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
log_dbg_printf("Created chld-pipe [r=%i,w=%i]\n",
|
||||
chldpipev[0], chldpipev[1]);
|
||||
|
||||
for (size_t i = 0; i < nclisock; i++) {
|
||||
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockcliv[i]) == -1) {
|
||||
log_err_printf("Failed to create socket pair %zu: "
|
||||
"%s (%i)\n", i, strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
log_dbg_printf("Created socketpair %zu [p=%i,c=%i]\n",
|
||||
i, sockcliv[i][0], sockcliv[i][1]);
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
log_err_printf("Failed to fork: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
close(selfpipev[0]);
|
||||
close(selfpipev[1]);
|
||||
close(chldpipev[0]);
|
||||
close(chldpipev[1]);
|
||||
for (size_t i = 0; i < nclisock; i++) {
|
||||
close(sockcliv[i][0]);
|
||||
close(sockcliv[i][1]);
|
||||
}
|
||||
return -1;
|
||||
} else if (pid == 0) {
|
||||
/* child */
|
||||
close(selfpipev[0]);
|
||||
close(selfpipev[1]);
|
||||
for (size_t i = 0; i < nclisock; i++)
|
||||
close(sockcliv[i][0]);
|
||||
/* wait until parent has installed signal handlers,
|
||||
* intentionally ignoring errors */
|
||||
char buf[1];
|
||||
ssize_t n;
|
||||
close(chldpipev[1]);
|
||||
do {
|
||||
n = read(chldpipev[0], buf, sizeof(buf));
|
||||
} while (n == -1 && errno == EINTR);
|
||||
close(chldpipev[0]);
|
||||
|
||||
/* return the privsep client sockets */
|
||||
for (size_t i = 0; i < nclisock; i++)
|
||||
clisock[i] = sockcliv[i][1];
|
||||
return 0;
|
||||
}
|
||||
/* parent */
|
||||
for (size_t i = 0; i < nclisock; i++)
|
||||
close(sockcliv[i][1]);
|
||||
selfpipe_wrfd = selfpipev[1];
|
||||
|
||||
/* close file descriptors opened by preinit's only needed in client;
|
||||
* we still call the preinit's before forking in order to provide
|
||||
* better user feedback and less privsep complexity */
|
||||
nat_preinit_undo();
|
||||
log_preinit_undo();
|
||||
|
||||
/* If the child exits before the parent installs the signal handler
|
||||
* here, we have a race condition; this is solved by the client
|
||||
* blocking on the reading end of a pipe (chldpipev[0]). */
|
||||
if (signal(SIGHUP, privsep_server_signal_handler) == SIG_ERR) {
|
||||
log_err_printf("Failed to install SIGHUP handler: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
if (signal(SIGINT, privsep_server_signal_handler) == SIG_ERR) {
|
||||
log_err_printf("Failed to install SIGINT handler: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
if (signal(SIGQUIT, privsep_server_signal_handler) == SIG_ERR) {
|
||||
log_err_printf("Failed to install SIGQUIT handler: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
if (signal(SIGUSR1, privsep_server_signal_handler) == SIG_ERR) {
|
||||
log_err_printf("Failed to install SIGUSR1 handler: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
if (signal(SIGCHLD, privsep_server_signal_handler) == SIG_ERR) {
|
||||
log_err_printf("Failed to install SIGCHLD handler: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* unblock the child */
|
||||
close(chldpipev[0]);
|
||||
close(chldpipev[1]);
|
||||
|
||||
int socksrv[nclisock];
|
||||
for (size_t i = 0; i < nclisock; i++)
|
||||
socksrv[i] = sockcliv[i][0];
|
||||
if (privsep_server(opts, selfpipev[0], socksrv, nclisock, pid) == -1) {
|
||||
log_err_printf("Privsep server failed: %s (%i)\n",
|
||||
strerror(errno), errno);
|
||||
/* fall through */
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < nclisock; i++)
|
||||
close(sockcliv[i][0]);
|
||||
selfpipe_wrfd = -1; /* tell signal handler not to write anymore */
|
||||
close(selfpipev[0]);
|
||||
close(selfpipev[1]);
|
||||
|
||||
int status;
|
||||
wait(&status);
|
||||
if (WIFEXITED(status)) {
|
||||
if (WEXITSTATUS(status) != 0) {
|
||||
log_err_printf("Child proc %lld exited with status %d\n",
|
||||
(long long)pid, WEXITSTATUS(status));
|
||||
} else {
|
||||
log_dbg_printf("Child proc %lld exited with status %d\n",
|
||||
(long long)pid, WEXITSTATUS(status));
|
||||
}
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
log_err_printf("Child proc %lld killed by signal %d\n",
|
||||
(long long)pid, WTERMSIG(status));
|
||||
} else {
|
||||
log_err_printf("Child proc %lld neither exited nor killed\n",
|
||||
(long long)pid);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* vim: set noet ft=c: */
|
||||
|
||||
|
||||
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* SSLsplit - transparent and scalable SSL/TLS interception
|
||||
* Copyright (c) 2009-2014, Daniel Roethlisberger <daniel@roe.ch>
|
||||
* All rights reserved.
|
||||
* http://www.roe.ch/SSLsplit
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef PRIVSEP_H
|
||||
#define PRIVSEP_H
|
||||
|
||||
#include "attrib.h"
|
||||
#include "opts.h"
|
||||
|
||||
int privsep_fork(opts_t *, int[], size_t);
|
||||
|
||||
int privsep_client_openfile(int, const char *, int);
|
||||
int privsep_client_opensock(int, const proxyspec_t *spec);
|
||||
int privsep_client_close(int);
|
||||
|
||||
#endif /* !PRIVSEP_H */
|
||||
|
||||
/* vim: set noet ft=c: */
|
Loading…
Reference in New Issue