mirror of
https://github.com/sonertari/SSLproxy
synced 2024-11-18 03:25:31 +00:00
Merge branch 'logspec_path_support' of git://github.com/fix-macosx/sslsplit into issue/55
This commit is contained in:
commit
81241139c7
194
log.c
194
log.c
@ -40,6 +40,8 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Centralized logging code multiplexing thread access to the logger based
|
* Centralized logging code multiplexing thread access to the logger based
|
||||||
@ -219,6 +221,7 @@ log_connect_close(void)
|
|||||||
logger_t *content_log = NULL;
|
logger_t *content_log = NULL;
|
||||||
static int content_fd = -1; /* if set, we are in single file mode */
|
static int content_fd = -1; /* if set, we are in single file mode */
|
||||||
static const char *content_basedir = NULL;
|
static const char *content_basedir = NULL;
|
||||||
|
static const char *content_logspec = NULL;
|
||||||
|
|
||||||
static int
|
static int
|
||||||
log_content_open_singlefile(const char *logfile)
|
log_content_open_singlefile(const char *logfile)
|
||||||
@ -239,6 +242,13 @@ log_content_open_logdir(const char *basedir)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
log_content_open_logspec(const char *logspec)
|
||||||
|
{
|
||||||
|
content_logspec = logspec;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
log_content_close_singlefile(void)
|
log_content_close_singlefile(void)
|
||||||
{
|
{
|
||||||
@ -248,8 +258,127 @@ log_content_close_singlefile(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate a log path based on the given log spec.
|
||||||
|
* Returns an allocated buffer which must be freed by caller, or NULL on error.
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
log_content_format_pathspec(const char *logspec, char *srcaddr, char *dstaddr,
|
||||||
|
char *exec_path, char *user, char *group)
|
||||||
|
{
|
||||||
|
/* set up buffer to hold our generated file path */
|
||||||
|
size_t path_buflen = 1024;
|
||||||
|
char *path_buf = malloc(path_buflen);
|
||||||
|
if (path_buf == NULL) {
|
||||||
|
log_err_printf("failed to allocate path buffer\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* initialize the buffer as an empty C string */
|
||||||
|
path_buf[0] = '\0';
|
||||||
|
|
||||||
|
/* iterate over format specifiers */
|
||||||
|
size_t path_len = 0;
|
||||||
|
for (const char *p = logspec; *p != '\0'; p++) {
|
||||||
|
const char *elem = NULL;
|
||||||
|
size_t elem_len = 0;
|
||||||
|
|
||||||
|
const char iso8601[] = "%Y%m%dT%H%M%SZ";
|
||||||
|
char timebuf[24]; // sized for ISO 8601 format
|
||||||
|
|
||||||
|
/* parse the format string and generate the next path element */
|
||||||
|
switch (*p) {
|
||||||
|
case '%':
|
||||||
|
p++;
|
||||||
|
/* handle format specifiers. */
|
||||||
|
switch (*p) {
|
||||||
|
case '\0':
|
||||||
|
/* unexpected eof; backtrack and discard invalid format spec */
|
||||||
|
p--;
|
||||||
|
elem_len = 0;
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
elem = p;
|
||||||
|
elem_len = 1;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
elem = dstaddr;
|
||||||
|
elem_len = strlen(dstaddr);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
elem = srcaddr;
|
||||||
|
elem_len = strlen(srcaddr);
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
if (exec_path) {
|
||||||
|
char *match = exec_path;
|
||||||
|
while ((match = strchr(match, '/')) != NULL) {
|
||||||
|
match++;
|
||||||
|
elem = match;
|
||||||
|
}
|
||||||
|
elem_len = strlen(elem);
|
||||||
|
} else {
|
||||||
|
elem_len = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
elem = exec_path;
|
||||||
|
elem_len = exec_path ? strlen(exec_path) : 0;
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
elem = user;
|
||||||
|
elem_len = user ? strlen(user) : 0;
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
elem = group;
|
||||||
|
elem_len = group ? strlen(group) : 0;
|
||||||
|
break;
|
||||||
|
case 'T': {
|
||||||
|
time_t epoch;
|
||||||
|
struct tm *utc;
|
||||||
|
|
||||||
|
time(&epoch);
|
||||||
|
utc = gmtime(&epoch);
|
||||||
|
strftime(timebuf, sizeof(timebuf), iso8601, utc);
|
||||||
|
|
||||||
|
elem = timebuf;
|
||||||
|
elem_len = sizeof(timebuf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
elem = p;
|
||||||
|
elem_len = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* growing the buffer to fit elem_len + terminating \0 */
|
||||||
|
if (path_buflen - path_len < elem_len + 1) {
|
||||||
|
/* grow in 1024 chunks. note that the use of `1024' provides our gauranteed space for a trailing '\0' */
|
||||||
|
path_buflen += elem_len + 1024;
|
||||||
|
char *newbuf = realloc(path_buf, path_buflen);
|
||||||
|
if (newbuf == NULL) {
|
||||||
|
log_err_printf("failed to reallocate path buffer");
|
||||||
|
free(path_buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
path_buf = newbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
strncat(path_buf, elem, elem_len);
|
||||||
|
path_len += elem_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* apply terminating NUL */
|
||||||
|
assert(path_buflen > path_len);
|
||||||
|
path_buf[path_len] = '\0';
|
||||||
|
return path_buf;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
log_content_open(log_content_ctx_t *ctx, char *srcaddr, char *dstaddr)
|
log_content_open(log_content_ctx_t *ctx, char *srcaddr, char *dstaddr,
|
||||||
|
char *exec_path, char *user, char *group)
|
||||||
{
|
{
|
||||||
if (ctx->open)
|
if (ctx->open)
|
||||||
return;
|
return;
|
||||||
@ -258,6 +387,58 @@ log_content_open(log_content_ctx_t *ctx, char *srcaddr, char *dstaddr)
|
|||||||
ctx->fd = content_fd;
|
ctx->fd = content_fd;
|
||||||
asprintf(&ctx->header_in, "%s -> %s", srcaddr, dstaddr);
|
asprintf(&ctx->header_in, "%s -> %s", srcaddr, dstaddr);
|
||||||
asprintf(&ctx->header_out, "%s -> %s", dstaddr, srcaddr);
|
asprintf(&ctx->header_out, "%s -> %s", dstaddr, srcaddr);
|
||||||
|
} else if (content_logspec) {
|
||||||
|
char *filename;
|
||||||
|
|
||||||
|
filename = log_content_format_pathspec(content_logspec,
|
||||||
|
srcaddr, dstaddr,
|
||||||
|
exec_path, user,
|
||||||
|
group);
|
||||||
|
|
||||||
|
/* statefully create parent directories by iteratively rewriting
|
||||||
|
* the path at each directory seperator */
|
||||||
|
char parent[strlen(filename)+1];
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
memcpy(parent, filename, sizeof(parent));
|
||||||
|
|
||||||
|
/* skip leading '/' characters */
|
||||||
|
p = parent;
|
||||||
|
while (*p == '/') p++;
|
||||||
|
|
||||||
|
while ((p = strchr(p, '/')) != NULL) {
|
||||||
|
/* overwrite '/' to terminate the string at the next parent directory */
|
||||||
|
*p = '\0';
|
||||||
|
|
||||||
|
struct stat sbuf;
|
||||||
|
if (stat(parent, &sbuf) != 0) {
|
||||||
|
if (mkdir(parent, 0755) != 0) {
|
||||||
|
log_err_printf("Could not create directory '%s': %s\n",
|
||||||
|
parent, strerror(errno));
|
||||||
|
ctx->fd = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (!S_ISDIR(sbuf.st_mode)) {
|
||||||
|
log_err_printf("Failed to open '%s': %s is not a directory\n",
|
||||||
|
filename, parent);
|
||||||
|
ctx->fd = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* replace the overwritten slash */
|
||||||
|
*p = '/';
|
||||||
|
p++;
|
||||||
|
|
||||||
|
/* skip leading '/' characters */
|
||||||
|
while (*p == '/') p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->fd = open(filename, O_WRONLY|O_APPEND|O_CREAT, 0660);
|
||||||
|
if (ctx->fd == -1) {
|
||||||
|
log_err_printf("Failed to open '%s': %s\n",
|
||||||
|
filename, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
char filename[1024];
|
char filename[1024];
|
||||||
char timebuf[24];
|
char timebuf[24];
|
||||||
@ -383,12 +564,15 @@ int
|
|||||||
log_preinit(opts_t *opts)
|
log_preinit(opts_t *opts)
|
||||||
{
|
{
|
||||||
if (opts->contentlog) {
|
if (opts->contentlog) {
|
||||||
if (!opts->contentlogdir) {
|
if (opts->contentlogdir) {
|
||||||
if (log_content_open_singlefile(opts->contentlog)
|
if (log_content_open_logdir(opts->contentlog) == -1)
|
||||||
== -1)
|
goto out;
|
||||||
|
} else if (opts->contentlogspec) {
|
||||||
|
if (log_content_open_logspec(opts->contentlog) == -1)
|
||||||
goto out;
|
goto out;
|
||||||
} else {
|
} else {
|
||||||
if (log_content_open_logdir(opts->contentlog) == -1)
|
if (log_content_open_singlefile(opts->contentlog)
|
||||||
|
== -1)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (!(content_log = logger_new(log_content_writecb))) {
|
if (!(content_log = logger_new(log_content_writecb))) {
|
||||||
|
3
log.h
3
log.h
@ -64,7 +64,8 @@ typedef struct log_content_ctx {
|
|||||||
char *header_in;
|
char *header_in;
|
||||||
char *header_out;
|
char *header_out;
|
||||||
} log_content_ctx_t;
|
} log_content_ctx_t;
|
||||||
void log_content_open(log_content_ctx_t *, char *, char *) NONNULL(1,2,3);
|
void log_content_open(log_content_ctx_t *, char *, char *, char *,
|
||||||
|
char *, char *) NONNULL(1,2,3);
|
||||||
void log_content_submit(log_content_ctx_t *, logbuf_t *, int) NONNULL(1,2);
|
void log_content_submit(log_content_ctx_t *, logbuf_t *, int) NONNULL(1,2);
|
||||||
void log_content_close(log_content_ctx_t *) NONNULL(1);
|
void log_content_close(log_content_ctx_t *) NONNULL(1);
|
||||||
|
|
||||||
|
29
main.c
29
main.c
@ -139,7 +139,21 @@ main_usage(void)
|
|||||||
" -p pidfile write pid to pidfile (default: no pid file)\n"
|
" -p pidfile write pid to pidfile (default: no pid file)\n"
|
||||||
" -l logfile connect log: log one line summary per connection to logfile\n"
|
" -l logfile connect log: log one line summary per connection to logfile\n"
|
||||||
" -L logfile content log: full data to file or named pipe (excludes -S)\n"
|
" -L logfile content log: full data to file or named pipe (excludes -S)\n"
|
||||||
" -S logdir content log: full data to separate files in dir (excludes -L)\n"
|
" -S logdir content log: full data to separate files in dir (excludes -L/-F)\n"
|
||||||
|
" -F pathspec content log: full data to separate files with the given path spec (excludes -L/-S):\n"
|
||||||
|
" pathspec = <paths and format directives>\n"
|
||||||
|
" The following directives are supported.\n"
|
||||||
|
" %%d - dest address:port\n"
|
||||||
|
" %%s - source address:port\n"
|
||||||
|
" %%x - base name of local process. if unavailable, will be skipped.\n"
|
||||||
|
" %%X - full path to local process; . if unavailable, will be skipped.\n"
|
||||||
|
" %%u - username or uid of local process. if unavailable, will be skipped\n"
|
||||||
|
" %%g - group or gid of local process. if unavailable, will be skipped\n"
|
||||||
|
" %%T - initial connection time as an ISO 8601 UTC timestamp\n"
|
||||||
|
" %%%% - literal '%%'\n"
|
||||||
|
" e.g.\n"
|
||||||
|
" \"/var/log/sslsplit/%%X/%%u-%%s-%%d-%%T\"\n"
|
||||||
|
" Unknown directives are ignored, and intermediate directories will be created automatically\n"
|
||||||
" -d daemon mode: run in background, log error messages to syslog\n"
|
" -d daemon mode: run in background, log error messages to syslog\n"
|
||||||
" -D debug mode: run in foreground, log debug messages on stderr\n"
|
" -D debug mode: run in foreground, log debug messages on stderr\n"
|
||||||
" -V print version information and exit\n"
|
" -V print version information and exit\n"
|
||||||
@ -249,7 +263,7 @@ main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
while ((ch = getopt(argc, argv, OPT_g OPT_G OPT_Z
|
while ((ch = getopt(argc, argv, OPT_g OPT_G OPT_Z
|
||||||
"k:c:C:K:t:OPs:r:R:e:Eu:m:j:p:l:L:S:dDVh")) != -1) {
|
"k:c:C:K:t:OPs:r:R:e:Eu:m:j:p:l:L:S:F:dDVh")) != -1) {
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case 'c':
|
case 'c':
|
||||||
if (opts->cacrt)
|
if (opts->cacrt)
|
||||||
@ -473,6 +487,7 @@ main(int argc, char *argv[])
|
|||||||
if (!opts->contentlog)
|
if (!opts->contentlog)
|
||||||
oom_die(argv0);
|
oom_die(argv0);
|
||||||
opts->contentlogdir = 0;
|
opts->contentlogdir = 0;
|
||||||
|
opts->contentlogspec = 0;
|
||||||
break;
|
break;
|
||||||
case 'S':
|
case 'S':
|
||||||
if (opts->contentlog)
|
if (opts->contentlog)
|
||||||
@ -481,6 +496,16 @@ main(int argc, char *argv[])
|
|||||||
if (!opts->contentlog)
|
if (!opts->contentlog)
|
||||||
oom_die(argv0);
|
oom_die(argv0);
|
||||||
opts->contentlogdir = 1;
|
opts->contentlogdir = 1;
|
||||||
|
opts->contentlogspec = 0;
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
if (opts->contentlog)
|
||||||
|
free(opts->contentlog);
|
||||||
|
opts->contentlog = strdup(optarg);
|
||||||
|
if (!opts->contentlog)
|
||||||
|
oom_die(argv0);
|
||||||
|
opts->contentlogspec = 1;
|
||||||
|
opts->contentlogdir = 0;
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
opts->detach = 1;
|
opts->detach = 1;
|
||||||
|
1
opts.h
1
opts.h
@ -75,6 +75,7 @@ typedef struct opts {
|
|||||||
unsigned int passthrough : 1;
|
unsigned int passthrough : 1;
|
||||||
unsigned int deny_ocsp : 1;
|
unsigned int deny_ocsp : 1;
|
||||||
unsigned int contentlogdir : 1;
|
unsigned int contentlogdir : 1;
|
||||||
|
unsigned int contentlogspec : 1;
|
||||||
char *ciphers;
|
char *ciphers;
|
||||||
char *tgcrtdir;
|
char *tgcrtdir;
|
||||||
char *dropuser;
|
char *dropuser;
|
||||||
|
17
pxyconn.c
17
pxyconn.c
@ -1580,7 +1580,8 @@ pxy_bev_eventcb(struct bufferevent *bev, short events, void *arg)
|
|||||||
}
|
}
|
||||||
if (WANT_CONTENT_LOG(ctx)) {
|
if (WANT_CONTENT_LOG(ctx)) {
|
||||||
log_content_open(&ctx->logctx, ctx->src_str,
|
log_content_open(&ctx->logctx, ctx->src_str,
|
||||||
ctx->dst_str);
|
ctx->dst_str, ctx->exec_path,
|
||||||
|
ctx->user, ctx->group);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* log connection if we don't analyze any headers */
|
/* log connection if we don't analyze any headers */
|
||||||
@ -1970,13 +1971,13 @@ pxy_conn_setup(evutil_socket_t fd,
|
|||||||
if (!ctx->src_str)
|
if (!ctx->src_str)
|
||||||
goto memout;
|
goto memout;
|
||||||
|
|
||||||
if (ctx->pid != -1) {
|
/* fetch process info */
|
||||||
if (sys_proc_info(ctx->pid, &ctx->exec_path, &ctx->uid, &ctx->gid) == 0) {
|
if (ctx->pid != -1 && sys_proc_info(ctx->pid, &ctx->exec_path, &ctx->uid, &ctx->gid) == 0) {
|
||||||
ctx->user = sys_user_str(ctx->uid);
|
/* fetch user/group names */
|
||||||
ctx->group = sys_group_str(ctx->gid);
|
ctx->user = sys_user_str(ctx->uid);
|
||||||
if (!ctx->user || !ctx->group) {
|
ctx->group = sys_group_str(ctx->gid);
|
||||||
goto memout;
|
if (!ctx->user || !ctx->group) {
|
||||||
}
|
goto memout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
45
sslsplit.1
45
sslsplit.1
@ -116,6 +116,14 @@ returned by \fB-E\fP.
|
|||||||
List all supported NAT engines available on the system and exit. See
|
List all supported NAT engines available on the system and exit. See
|
||||||
NAT ENGINES for a list of NAT engines currently supported by SSLsplit.
|
NAT ENGINES for a list of NAT engines currently supported by SSLsplit.
|
||||||
.TP
|
.TP
|
||||||
|
.B \-F \fIlogspec\fP
|
||||||
|
Log connection content to separate log files with the given path specification
|
||||||
|
(see LOG SPECIFICATIONS below). For each connection, a log file will be
|
||||||
|
written, which will contain both directions of data as transmitted.
|
||||||
|
Information about the connection will be contained in the filename only.
|
||||||
|
If \fB-F\fP is used with \fB-j\fP, \fIlogspec\fP is relative to \fIjaildir\fP.
|
||||||
|
If \fB-F\fP is used with \fB-u\fP, \fIlogspec\fP must be writable by \fIuser\fP.
|
||||||
|
.TP
|
||||||
.B \-g \fIpemfile\fP
|
.B \-g \fIpemfile\fP
|
||||||
Use Diffie-Hellman group parameters from \fIpemfile\fP for Ephemereal
|
Use Diffie-Hellman group parameters from \fIpemfile\fP for Ephemereal
|
||||||
Diffie-Hellman (EDH/DHE) cipher suites. If \fB-g\fP is not given, SSLsplit
|
Diffie-Hellman (EDH/DHE) cipher suites. If \fB-g\fP is not given, SSLsplit
|
||||||
@ -147,8 +155,8 @@ Display help on usage and exit.
|
|||||||
.TP
|
.TP
|
||||||
.B \-j \fIjaildir\fP
|
.B \-j \fIjaildir\fP
|
||||||
Change the root directory to \fIjaildir\fP using chroot(2) after opening files.
|
Change the root directory to \fIjaildir\fP using chroot(2) after opening files.
|
||||||
Note that this has implications for both \fB-S\fP and for \fBsni\fP
|
Note that this has implications for \fB-F\fP, \fB-S\fP, and for \fBsni\fP
|
||||||
\fIproxyspecs\fP. The directory given with \fB-S\fP will be relative to
|
\fIproxyspecs\fP. The path given with \fB-S\fP or \fB-F\fP will be relative to
|
||||||
\fIjaildir\fP since the log files cannot be opened before calling chroot(2).
|
\fIjaildir\fP since the log files cannot be opened before calling chroot(2).
|
||||||
Depending on your operating system, you will need to copy files such as
|
Depending on your operating system, you will need to copy files such as
|
||||||
\fB/etc/resolv.conf\fP to \fIjaildir\fP in order for name resolution to work.
|
\fB/etc/resolv.conf\fP to \fIjaildir\fP in order for name resolution to work.
|
||||||
@ -347,6 +355,39 @@ than the NAT rules redirecting the actual connections.
|
|||||||
Note that when using \fB-j\fP with \fBsni\fP, you may need to prepare
|
Note that when using \fB-j\fP with \fBsni\fP, you may need to prepare
|
||||||
\fIjaildir\fP to make name resolution work from within the chroot directory.
|
\fIjaildir\fP to make name resolution work from within the chroot directory.
|
||||||
.LP
|
.LP
|
||||||
|
.SH "LOG SPECIFICATIONS"
|
||||||
|
Log specifications are composed of zero or more printf-style directives;
|
||||||
|
ordinary characters are included directly in the output path.
|
||||||
|
SSLsplit current supports the following directives:
|
||||||
|
.TP
|
||||||
|
.I %d
|
||||||
|
The destination address and port.
|
||||||
|
.TP
|
||||||
|
.I %s
|
||||||
|
The source address and port.
|
||||||
|
.TP
|
||||||
|
.I %x
|
||||||
|
The name of the local process. If process information is unavailable,
|
||||||
|
this directive will be omitted from the output path.
|
||||||
|
.TP
|
||||||
|
.I %X
|
||||||
|
The full path of the local process. If process information is unavailable,
|
||||||
|
this directive will be omitted from the output path.
|
||||||
|
.TP
|
||||||
|
.I %u
|
||||||
|
The username or numeric uid of the local process. If process information is unavailable,
|
||||||
|
this directive will be omitted from the output path.
|
||||||
|
.TP
|
||||||
|
.I %g
|
||||||
|
The group name or numeric gid of the local process. If process information is unavailable,
|
||||||
|
this directive will be omitted from the output path.
|
||||||
|
.TP
|
||||||
|
.I %T
|
||||||
|
The initial connection time as an ISO 8601 UTC timestamp.
|
||||||
|
.TP
|
||||||
|
.I %%
|
||||||
|
A literal '%' character.
|
||||||
|
.LP
|
||||||
.SH "NAT ENGINES"
|
.SH "NAT ENGINES"
|
||||||
SSLsplit currently supports the following NAT engines:
|
SSLsplit currently supports the following NAT engines:
|
||||||
.TP
|
.TP
|
||||||
|
Loading…
Reference in New Issue
Block a user