示例#1
0
文件: sys.c 项目: 0x0mar/sslsplit
/*
 * Converts an IPv4/IPv6 sockaddr into a printable string representation.
 * Returns an allocated buffer which must be freed by caller, or NULL on error.
 */
char *
sys_sockaddr_str(struct sockaddr *addr, socklen_t addrlen)
{
	char host[INET6_ADDRSTRLEN], serv[6];
	char *buf;
	int rv;
	size_t bufsz;

	bufsz = sizeof(host) + sizeof(serv) + 3;
	buf = malloc(bufsz);
	if (!buf) {
		log_err_printf("Cannot allocate memory\n");
		return NULL;
	}
	rv = getnameinfo(addr, addrlen, host, sizeof(host), serv, sizeof(serv),
	                 NI_NUMERICHOST | NI_NUMERICSERV);
	if (rv != 0) {
		log_err_printf("Cannot get nameinfo for socket address: %s\n",
		               gai_strerror(rv));
		free(buf);
		return NULL;
	}
	snprintf(buf, bufsz, "[%s]:%s", host, serv);
	return buf;
}
示例#2
0
文件: log.c 项目: Azleal/sslsplit
void
log_content_submit(log_content_ctx_t *ctx, logbuf_t *lb, int direction)
{
	logbuf_t *head;
	time_t epoch;
	struct tm *utc;
	char *header;

	if (!ctx->open) {
		log_err_printf("log_content_submit called on closed ctx\n");
		return;
	}

	if (!(header = direction ? ctx->header_out : ctx->header_in))
		goto out;

	/* prepend size tag and newline */
	head = logbuf_new_printf(lb->fd, lb, " (%zu):\n", logbuf_size(lb));
	if (!head) {
		log_err_printf("Failed to allocate memory\n");
		logbuf_free(lb);
		return;
	}
	lb = head;

	/* prepend header */
	head = logbuf_new_copy(header, strlen(header), lb->fd, lb);
	if (!head) {
		log_err_printf("Failed to allocate memory\n");
		logbuf_free(lb);
		return;
	}
	lb = head;

	/* prepend timestamp */
	head = logbuf_new_alloc(32, lb->fd, lb);
	if (!head) {
		log_err_printf("Failed to allocate memory\n");
		logbuf_free(lb);
		return;
	}
	lb = head;
	time(&epoch);
	utc = gmtime(&epoch);
	lb->sz = strftime((char*)lb->buf, lb->sz, "%Y-%m-%d %H:%M:%S UTC ",
	                  utc);

out:
	lb->fd = ctx->fd;
	logger_submit(content_log, lb);
}
示例#3
0
文件: main.c 项目: jjolla/sslsplit
/*
 * Callback to load a cert/chain/key combo from a single PEM file.
 */
static void
main_loadtgcrt(const char *filename, void *arg)
{
	opts_t *opts = arg;
	cert_t *cert;
	char **names;

	cert = cert_new_load(filename);
	if (!cert) {
		log_err_printf("Failed to load cert and key from PEM file "
		                "'%s'\n", filename);
		log_fini();
		exit(EXIT_FAILURE); /* XXX */
	}
	if (X509_check_private_key(cert->crt, cert->key) != 1) {
		log_err_printf("Cert does not match key in PEM file "
		                "'%s':\n", filename);
		ERR_print_errors_fp(stderr);
		log_fini();
		exit(EXIT_FAILURE); /* XXX */
	}

#ifdef DEBUG_CERTIFICATE
	log_dbg_printf("Loaded '%s':\n", filename);
	log_dbg_print_free(ssl_x509_to_str(cert->crt));
	log_dbg_print_free(ssl_x509_to_pem(cert->crt));
#endif /* DEBUG_CERTIFICATE */

	if (OPTS_DEBUG(opts)) {
		log_dbg_printf("Targets for '%s':", filename);
	}
	names = ssl_x509_names(cert->crt);
	for (char **p = names; *p; p++) {
		/* be deliberately vulnerable to NULL prefix attacks */
		char *sep;
		if ((sep = strchr(*p, '!'))) {
			*sep = '\0';
		}
		if (OPTS_DEBUG(opts)) {
			log_dbg_printf(" '%s'", *p);
		}
		cachemgr_tgcrt_set(*p, cert);
		free(*p);
	}
	if (OPTS_DEBUG(opts)) {
		log_dbg_printf("\n");
	}
	free(names);
	cert_free(cert);
}
示例#4
0
文件: log.c 项目: cgroschupp/sslsplit
static logbuf_t *
log_content_file_prepcb(void *fh, unsigned long prepflags, logbuf_t *lb)
{
	log_content_ctx_t *ctx = fh;
	int is_request = !!(prepflags & PREPFLAG_REQUEST);
	logbuf_t *head;
	time_t epoch;
	struct tm *utc;
	char *header;

	if (!(header = is_request ? ctx->u.file.header_req
	                          : ctx->u.file.header_resp))
		goto out;

	/* prepend size tag and newline */
	head = logbuf_new_printf(lb->fh, lb, " (%zu):\n", logbuf_size(lb));
	if (!head) {
		log_err_printf("Failed to allocate memory\n");
		logbuf_free(lb);
		return NULL;
	}
	lb = head;

	/* prepend header */
	head = logbuf_new_copy(header, strlen(header), lb->fh, lb);
	if (!head) {
		log_err_printf("Failed to allocate memory\n");
		logbuf_free(lb);
		return NULL;
	}
	lb = head;

	/* prepend timestamp */
	head = logbuf_new_alloc(32, lb->fh, lb);
	if (!head) {
		log_err_printf("Failed to allocate memory\n");
		logbuf_free(lb);
		return NULL;
	}
	lb = head;
	time(&epoch);
	utc = gmtime(&epoch);
	lb->sz = strftime((char*)lb->buf, lb->sz, "%Y-%m-%d %H:%M:%S UTC ",
	                  utc);

out:
	return lb;
}
示例#5
0
static void
pxy_log_connect_http(pxy_conn_ctx_t *ctx)
{
	char *msg;
	int rv;

#ifdef DEBUG_PROXY
	if (ctx->passthrough) {
		log_err_printf("Warning: pxy_log_connect_http called while in "
		               "passthrough mode\n");
		return;
	}
#endif

	if (!ctx->spec->ssl) {
		rv = asprintf(&msg, "http %s %s %s %s %s%s\n",
		              STRORDASH(ctx->src_str),
		              STRORDASH(ctx->dst_str),
		              STRORDASH(ctx->http_host),
		              STRORDASH(ctx->http_method),
		              STRORDASH(ctx->http_uri),
		              ctx->ocsp_denied ? " ocsp:denied" : "");
	} else {
		rv = asprintf(&msg, "https %s %s %s %s %s "
		              "sni:%s crt:%s origcrt:%s%s\n",
		              STRORDASH(ctx->src_str),
		              STRORDASH(ctx->dst_str),
		              STRORDASH(ctx->http_host),
		              STRORDASH(ctx->http_method),
		              STRORDASH(ctx->http_uri),
		              STRORDASH(ctx->sni),
		              STRORDASH(ctx->ssl_names),
		              STRORDASH(ctx->ssl_orignames),
		              ctx->ocsp_denied ? " ocsp:denied" : "");
	}
	if ((rv == -1) || !msg) {
		ctx->enomem = 1;
		return;
	}
	if (!ctx->opts->detach) {
		log_err_printf("%s", msg);
	}
	if (ctx->opts->connectlog) {
		log_connect_print_free(msg);
	} else {
		free(msg);
	}
}
示例#6
0
文件: log.c 项目: Azleal/sslsplit
void
log_content_open(log_content_ctx_t *ctx, char *srcaddr, char *dstaddr)
{
	if (ctx->open)
		return;

	if (content_fd != -1) {
		ctx->fd = content_fd;
		asprintf(&ctx->header_in, "%s -> %s", srcaddr, dstaddr);
		asprintf(&ctx->header_out, "%s -> %s", dstaddr, srcaddr);
	} else {
		char filename[1024];
		char timebuf[24];
		time_t epoch;
		struct tm *utc;

		time(&epoch);
		utc = gmtime(&epoch);
		strftime(timebuf, sizeof(timebuf), "%Y%m%dT%H%M%SZ", utc);
		snprintf(filename, sizeof(filename), "%s/%s-%s-%s.log",
		         content_basedir, timebuf, srcaddr, dstaddr);
		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));
		}
	}
	ctx->open = 1;
}
示例#7
0
/*
 * Dump information on a certificate to the debug log.
 */
static void
pxy_debug_crt(X509 *crt)
{
	char *sj = ssl_x509_subject(crt);
	if (sj) {
		log_dbg_printf("Subject DN: %s\n", sj);
		free(sj);
	}

	char *names = ssl_x509_names_to_str(crt);
	if (names) {
		log_dbg_printf("Common Names: %s\n", names);
		free(names);
	}

	unsigned char fpr[SSL_X509_FPRSZ];
	if (ssl_x509_fingerprint_sha1(crt, fpr) == -1) {
		log_err_printf("Warning: Error generating X509 fingerprint\n");
	} else {
		log_dbg_printf("Fingerprint: "     "%02x:%02x:%02x:%02x:"
		               "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:"
		               "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
		               fpr[0],  fpr[1],  fpr[2],  fpr[3],  fpr[4],
		               fpr[5],  fpr[6],  fpr[7],  fpr[8],  fpr[9],
		               fpr[10], fpr[11], fpr[12], fpr[13], fpr[14],
		               fpr[15], fpr[16], fpr[17], fpr[18], fpr[19]);
	}

#ifdef DEBUG_CERTIFICATE
	/* dump certificate */
	log_dbg_print_free(ssl_x509_to_str(crt));
	log_dbg_print_free(ssl_x509_to_pem(crt));
#endif /* DEBUG_CERTIFICATE */
}
示例#8
0
static void
pxy_log_connect_nonhttp(pxy_conn_ctx_t *ctx)
{
	char *msg;
	int rv;

	if (!ctx->spec->ssl || ctx->passthrough) {
		rv = asprintf(&msg, "%s %s %s\n",
		              ctx->passthrough ? "passthrough" : "tcp",
		              STRORDASH(ctx->src_str),
		              STRORDASH(ctx->dst_str));
	} else {
		rv = asprintf(&msg, "ssl %s %s "
		              "sni:%s crt:%s origcrt:%s\n",
		              STRORDASH(ctx->src_str),
		              STRORDASH(ctx->dst_str),
		              STRORDASH(ctx->sni),
		              STRORDASH(ctx->ssl_names),
		              STRORDASH(ctx->ssl_orignames));
	}
	if ((rv == -1) || !msg) {
		ctx->enomem = 1;
		return;
	}
	if (!ctx->opts->detach) {
		log_err_printf("%s", msg);
	}
	if (ctx->opts->connectlog) {
		log_connect_print_free(msg);
	} else {
		free(msg);
	}
}
示例#9
0
文件: sys.c 项目: 0x0mar/sslsplit
/*
 * Parse an ascii host/IP and port tuple into a sockaddr_storage.
 * On success, returns address family and fills in addr, addrlen.
 * Returns -1 on error.
 */
int
sys_sockaddr_parse(struct sockaddr_storage *addr, socklen_t *addrlen,
                   char *naddr, char *nport, int af, int flags)
{
	struct evutil_addrinfo hints;
	struct evutil_addrinfo *ai;
	int rv;

	memset(&hints, 0, sizeof(hints));
	hints.ai_family = af;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	hints.ai_flags = EVUTIL_AI_ADDRCONFIG | flags;
	rv = evutil_getaddrinfo(naddr, nport, &hints, &ai);
	if (rv != 0) {
		log_err_printf("Cannot resolve address '%s' port '%s': %s\n",
		               naddr, nport, gai_strerror(rv));
		return -1;
	}
	memcpy(addr, ai->ai_addr, ai->ai_addrlen);
	*addrlen = ai->ai_addrlen;
	af = ai->ai_family;
	freeaddrinfo(ai);
	return af;
}
示例#10
0
文件: log.c 项目: cgroschupp/sslsplit
static int
log_connect_preinit(const char *logfile)
{
	connect_fd = open(logfile, O_WRONLY|O_APPEND|O_CREAT, DFLT_FILEMODE);
	if (connect_fd == -1) {
		log_err_printf("Failed to open '%s' for writing: %s (%i)\n",
		               logfile, strerror(errno), errno);
		return -1;
	}
	if (!(connect_fn = realpath(logfile, NULL))) {
		log_err_printf("Failed to realpath '%s': %s (%i)\n",
		              logfile, strerror(errno), errno);
		close(connect_fd);
		connect_fd = -1;
		return -1;
	}
	return 0;
}
示例#11
0
文件: sys.c 项目: 0x0mar/sslsplit
/*
 * Open and lock process ID file fn.
 * Returns open file descriptor on success or -1 on errors.
 */
int
sys_pidf_open(const char *fn)
{
	int fd;

	if ((fd = open(fn, O_RDWR|O_CREAT, 0640)) == -1) {
		log_err_printf("Failed to open '%s': %s\n", fn,
		               strerror(errno));
		return -1;
	}
	if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
		log_err_printf("Failed to lock '%s': %s\n", fn,
		               strerror(errno));
		close(fd);
		return -1;
	}

	return fd;
}
示例#12
0
文件: sys.c 项目: amarnathpg/sslsplit
/*
 * Permanently drop from root privileges to an unprivileged user account.
 * Sets the real, effective and stored user and group ID and the list of
 * ancillary groups.  This is only safe if the effective user ID is 0.
 * If username is unset and the effective uid != uid, drop privs to uid.
 * This is to support setuid bit configurations.
 * If jaildir is set, also chroot to jaildir after reading system files
 * but before dropping privileges.
 * Returns 0 on success, -1 on failure.
 */
int
sys_privdrop(const char *username, const char *jaildir)
{
	struct passwd *pw = NULL;
	int ret = -1;

	if (username) {
		if (!(pw = getpwnam(username))) {
			log_err_printf("Failed to getpwnam user '%s': %s\n",
			               username, strerror(errno));
			goto error;
		}
		if (initgroups(username, pw->pw_gid) == -1) {
			log_err_printf("Failed to initgroups user '%s': %s\n",
			               username, strerror(errno));
			goto error;
		}
	}

	if (jaildir) {
		if (chroot(jaildir) == -1) {
			log_err_printf("Failed to chroot to '%s': %s\n",
			               jaildir, strerror(errno));
			goto error;
		}
		if (chdir("/") == -1) {
			log_err_printf("Failed to chdir to '/': %s\n",
			               strerror(errno));
			goto error;
		}
	}

	if (username) {
		if (setgid(pw->pw_gid) == -1) {
			log_err_printf("Failed to setgid to %i: %s\n",
			               pw->pw_gid, strerror(errno));
			goto error;
		}
		if (setuid(pw->pw_uid) == -1) {
			log_err_printf("Failed to setuid to %i: %s\n",
			               pw->pw_uid, strerror(errno));
			goto error;
		}
	} else if (getuid() != geteuid()) {
		if (setuid(getuid()) == -1) {
			log_err_printf("Failed to setuid(getuid()): %s\n",
			               strerror(errno));
			goto error;
		}
	}

	ret = 0;
error:
	if (pw) {
		endpwent();
	}
	return ret;
}
示例#13
0
文件: log.c 项目: Azleal/sslsplit
static int
log_content_open_singlefile(const char *logfile)
{
	content_fd = open(logfile, O_WRONLY|O_APPEND|O_CREAT, 0660);
	if (content_fd == -1) {
		log_err_printf("Failed to open '%s' for writing: %s\n",
		               logfile, strerror(errno));
		return -1;
	}
	return 0;
}
示例#14
0
文件: log.c 项目: cgroschupp/sslsplit
static ssize_t
log_content_file_writecb(void *fh, const void *buf, size_t sz)
{
	UNUSED log_content_ctx_t *ctx = fh;

	if (write(content_file_fd, buf, sz) == -1) {
		log_err_printf("Warning: Failed to write to content log: %s\n",
		               strerror(errno));
		return -1;
	}
	return sz;
}
示例#15
0
文件: log.c 项目: Azleal/sslsplit
/*
 * Do the actual write to the open connection log file descriptor.
 * We prepend a timestamp here, which means that timestamps are slightly
 * delayed from the time of actual logging.  Since we only have second
 * resolution that should not make any difference.
 */
static ssize_t
log_connect_writecb(UNUSED int fd, const void *buf, size_t sz)
{
	char timebuf[32];
	time_t epoch;
	struct tm *utc;
	size_t n;

	time(&epoch);
	utc = gmtime(&epoch);
	n = strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S UTC ", utc);
	if (n == 0) {
		log_err_printf("Error from strftime(): buffer too small\n");
		return -1;
	}
	if ((write(connect_fd, timebuf, n) == -1) ||
	    (write(connect_fd, buf, sz) == -1)) {
		log_err_printf("Warning: Failed to write to connect log: %s\n",
		               strerror(errno));
	}
	return 0;
}
示例#16
0
文件: log.c 项目: cgroschupp/sslsplit
static int
log_content_file_reopencb(void)
{
	close(content_file_fd);
	content_file_fd = open(content_file_fn,
	                       O_WRONLY|O_APPEND|O_CREAT, DFLT_FILEMODE);
	if (content_file_fd == -1) {
		log_err_printf("Failed to open '%s' for writing: %s (%i)\n",
		               content_file_fn, strerror(errno), errno);
		return -1;
	}
	return 0;
}
示例#17
0
文件: ssl.c 项目: caidongyun/backup
/*
 * OpenSSL temporary DH callback which loads DH parameters from static memory.
 */
DH *
ssl_tmp_dh_callback(UNUSED SSL *s, int is_export, int keylength)
{
	DH *dh;

	if (!(dh = DH_new())) {
		log_err_printf("DH_new() failed\n");
		return NULL;
	}
	switch (keylength) {
		case 512:
			dh->p = BN_bin2bn(dh512_p, sizeof(dh512_p), NULL);
			break;
		case 1024:
			dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL);
			break;
		case 2048:
			dh->p = BN_bin2bn(dh2048_p, sizeof(dh2048_p), NULL);
			break;
		case 4096:
			dh->p = BN_bin2bn(dh4096_p, sizeof(dh4096_p), NULL);
			break;
		default:
			log_err_printf("Unhandled DH keylength %i%s\n",
			               keylength,
			               (is_export ? " (export)" : ""));
			DH_free(dh);
			return NULL;
	}
	dh->g = BN_bin2bn(dh_g, sizeof(dh_g), NULL);
	if (!dh->p || !dh->g) {
		log_err_printf("Failed to load DH p and g from memory\n");
		DH_free(dh);
		return NULL;
	}

	return(dh);
}
示例#18
0
文件: log.c 项目: cgroschupp/sslsplit
static int
log_content_spec_opencb(void *fh)
{
	log_content_ctx_t *ctx = fh;

	if ((ctx->u.spec.fd = privsep_client_openfile(content_clisock,
	                                              ctx->u.spec.filename,
	                                              1)) == -1) {
		log_err_printf("Opening logspec file '%s' failed: %s (%i)\n",
		               ctx->u.spec.filename, strerror(errno), errno);
		return -1;
	}
	return 0;
}
示例#19
0
文件: log.c 项目: cgroschupp/sslsplit
static ssize_t
log_cert_writecb(void *fh, const void *buf, size_t sz)
{
	char *fn = fh;
	int fd;

	if ((fd = privsep_client_certfile(cert_clisock, fn)) == -1) {
		if (errno != EEXIST) {
			log_err_printf("Failed to open '%s': %s (%i)\n",
			               fn, strerror(errno), errno);
			return -1;
		}
		return sz;
	}
	if (write(fd, buf, sz) == -1) {
		log_err_printf("Warning: Failed to write to '%s': %s (%i)\n",
		               fn, strerror(errno), errno);
		close(fd);
		return -1;
	}
	close(fd);
	return sz;
}
示例#20
0
文件: log.c 项目: cgroschupp/sslsplit
static int
log_connect_reopencb(void)
{
	close(connect_fd);
	connect_fd = open(connect_fn, O_WRONLY|O_APPEND|O_CREAT, DFLT_FILEMODE);
	if (connect_fd == -1) {
		log_err_printf("Failed to open '%s' for writing: %s\n",
		               connect_fn, strerror(errno));
		free(connect_fn);
		connect_fn = NULL;
		return -1;
	}
	return 0;
}
示例#21
0
文件: log.c 项目: cgroschupp/sslsplit
int
log_content_submit(log_content_ctx_t *ctx, logbuf_t *lb, int is_request)
{
	unsigned long prepflags = 0;

	if (!ctx->open) {
		log_err_printf("log_content_submit called on closed ctx\n");
		return -1;
	}

	if (is_request)
		prepflags |= PREPFLAG_REQUEST;
	return logger_submit(content_log, ctx, prepflags, lb);
}
示例#22
0
文件: log.c 项目: Azleal/sslsplit
/*
 * Do the actual write to the open connection log file descriptor.
 * We prepend a timestamp here, which means that timestamps are slightly
 * delayed from the time of actual logging.  Since we only have second
 * resolution that should not make any difference.
 */
static ssize_t
log_content_writecb(int fd, const void *buf, size_t sz)
{
	if (!buf) {
		close(fd);
		return 0;
	}

	if (write(fd, buf, sz) == -1) {
		log_err_printf("Warning: Failed to write to content log: %s\n",
		               strerror(errno));
		return -1;
	}
	return 0;
}
示例#23
0
文件: sys.c 项目: 0x0mar/sslsplit
/*
 * Iterate over all files in a directory hierarchy, calling the callback
 * cb for each file, passing the filename and arg as arguments.  Files and
 * directories beginning with a dot are skipped, symlinks are followed.
 */
int
sys_dir_eachfile(const char *dirname, sys_dir_eachfile_cb_t cb, void *arg)
{
	FTS *tree;
	FTSENT *node;
	char * paths[2];

	paths[1] = NULL;
	paths[0] = strdup(dirname);
	if (!paths[0])
		return -1;

	tree = fts_open(paths, FTS_NOCHDIR | FTS_LOGICAL, NULL);
	if (!tree) {
		log_err_printf("Cannot open directory '%s': %s\n",
		               dirname, strerror(errno));
		return -1;
	}

	while ((node = fts_read(tree))) {
		if (node->fts_level > 0 && node->fts_name[0] == '.')
			fts_set(tree, node, FTS_SKIP);
		else if (node->fts_info & FTS_F) {
			cb(node->fts_path, arg);
		}
	}
	if (errno) {
		log_err_printf("Error reading directory entry: %s\n",
		               strerror(errno));
		return -1;
	}
	fts_close(tree);

	free(paths[0]);
	return 0;
}
示例#24
0
文件: sys.c 项目: 0x0mar/sslsplit
/*
 * Converts a local gid into a printable string representation.
 * Returns an allocated buffer which must be freed by caller, or NULL on error.
 */
char *
sys_group_str(gid_t gid)
{
	static int bufsize = 0;

	if (!bufsize) {
		/* on some platforms this compiles, but does not succeed */
		if ((bufsize = sysconf(_SC_GETGR_R_SIZE_MAX)) == -1) {
			bufsize = 64;
		}
	}

	char *buf, *newbuf;
	struct group grp, *result = NULL;
	int rv;
	char *name;

	if (!(buf = malloc(bufsize)))
		return NULL;

	do {
		rv = getgrgid_r(gid, &grp, buf, bufsize, &result);
		if (rv == 0) {
			if (result) {
				name = strdup(grp.gr_name);
				free(buf);
				return name;
			}
			free(buf);

			/* no entry found; return the integer representation */
			if (asprintf(&name, "%llu", (long long) gid) < 0) {
				return NULL;
			}
			return name;
		}
		bufsize *= 2;
		if (!(newbuf = realloc(buf, bufsize))) {
			free(buf);
			return NULL;
		}
		buf = newbuf;
	} while (rv == ERANGE);

	free(buf);
	log_err_printf("Failed to lookup gid: %s (%i)\n", strerror(rv), rv);
	return NULL;
}
示例#25
0
/*
 * Called by OpenSSL when a new src SSL session is created.
 * Return 0 means remove session from internal session cache.
 */
#ifdef DISABLE_SSLV2_SESSION_CACHE
#define MAYBE_UNUSED 
#else /* !DISABLE_SSLV2_SESSION_CACHE */
#define MAYBE_UNUSED UNUSED
#endif /* !DISABLE_SSLV2_SESSION_CACHE */
static int
pxy_ossl_sessnew_cb(MAYBE_UNUSED SSL *ssl, SSL_SESSION *sess)
#undef MAYBE_UNUSED
{
#ifdef DEBUG_SESSION_CACHE
	log_dbg_printf("===> OpenSSL new session callback:\n");
	if (sess) {
		log_dbg_print_free(ssl_session_to_str(sess));
	}
#endif /* DEBUG_SESSION_CACHE */
#ifdef DISABLE_SSLV2_SESSION_CACHE
	/* Session resumption seems to fail for SSLv2 with protocol
	 * parsing errors, so we disable caching for SSLv2. */
	if (SSL_version(ssl) == SSL2_VERSION) {
		log_err_printf("Warning: Session resumption denied to SSLv2"
		               "client.\n");
		return 0;
	}
#endif /* DISABLE_SSLV2_SESSION_CACHE */
	cachemgr_ssess_set(sess);
	return 0;
}
示例#26
0
/*
 * Garbage collect all the cache contents; free's up resources occupied by
 * certificates and sessions which are no longer valid.
 * This function returns after the cleanup completed and all threads are
 * joined.
 */
void
cachemgr_gc(void)
{
	pthread_t fkcrt_thr, dsess_thr, ssess_thr;
	int rv;

	/* the tgcrt cache does not need cleanup */

	rv = pthread_create(&fkcrt_thr, NULL, cachemgr_gc_thread,
	                    cachemgr_fkcrt);
	if (rv) {
		log_err_printf("cachemgr_gc: pthread_create failed: %s\n",
		               strerror(rv));
	}
	rv = pthread_create(&ssess_thr, NULL, cachemgr_gc_thread,
	                    cachemgr_ssess);
	if (rv) {
		log_err_printf("cachemgr_gc: pthread_create failed: %s\n",
		               strerror(rv));
	}
	rv = pthread_create(&dsess_thr, NULL, cachemgr_gc_thread,
	                    cachemgr_dsess);
	if (rv) {
		log_err_printf("cachemgr_gc: pthread_create failed: %s\n",
		               strerror(rv));
	}

	rv = pthread_join(fkcrt_thr, NULL);
	if (rv) {
		log_err_printf("cachemgr_gc: pthread_join failed: %s\n",
		               strerror(rv));
	}
	rv = pthread_join(ssess_thr, NULL);
	if (rv) {
		log_err_printf("cachemgr_gc: pthread_join failed: %s\n",
		               strerror(rv));
	}
	rv = pthread_join(dsess_thr, NULL);
	if (rv) {
		log_err_printf("cachemgr_gc: pthread_join failed: %s\n",
		               strerror(rv));
	}
}
示例#27
0
文件: main.c 项目: jjolla/sslsplit
/*
 * Main entry point.
 */
int
main(int argc, char *argv[])
{
	const char *argv0;
	int ch;
	opts_t *opts;
	char *natengine;
	int pidfd = -1;
	int rv = EXIT_FAILURE;

	argv0 = argv[0];
	opts = opts_new();
	natengine = strdup(nat_getdefaultname());

	while ((ch = getopt(argc, argv, OPT_g OPT_G OPT_Z
	                    "k:c:C:K:t:OPs:e:Eu:j:p:l:L:S:dDVh")) != -1) {
		switch (ch) {
			case 'c':
				if (opts->cacrt)
					X509_free(opts->cacrt);
				opts->cacrt = ssl_x509_load(optarg);
				if (!opts->cacrt) {
					fprintf(stderr, "%s: error loading CA "
					                "cert from '%s':\n",
					                argv0, optarg);
					if (errno) {
						fprintf(stderr, "%s\n",
						        strerror(errno));
					} else {
						ERR_print_errors_fp(stderr);
					}
					exit(EXIT_FAILURE);
				}
				ssl_x509_refcount_inc(opts->cacrt);
				sk_X509_insert(opts->chain, opts->cacrt, 0);
				if (!opts->cakey) {
					opts->cakey = ssl_key_load(optarg);
				}
#ifndef OPENSSL_NO_DH
				if (!opts->dh) {
					opts->dh = ssl_dh_load(optarg);
				}
#endif /* !OPENSSL_NO_DH */
				break;
			case 'k':
				if (opts->cakey)
					EVP_PKEY_free(opts->cakey);
				opts->cakey = ssl_key_load(optarg);
				if (!opts->cakey) {
					fprintf(stderr, "%s: error loading CA "
					                "key from '%s':\n",
					                argv0, optarg);
					if (errno) {
						fprintf(stderr, "%s\n",
						        strerror(errno));
					} else {
						ERR_print_errors_fp(stderr);
					}
					exit(EXIT_FAILURE);
				}
				if (!opts->cacrt) {
					opts->cacrt = ssl_x509_load(optarg);
					if (opts->cacrt) {
						ssl_x509_refcount_inc(
						               opts->cacrt);
						sk_X509_insert(opts->chain,
						               opts->cacrt, 0);
					}
				}
#ifndef OPENSSL_NO_DH
				if (!opts->dh) {
					opts->dh = ssl_dh_load(optarg);
				}
#endif /* !OPENSSL_NO_DH */
				break;
			case 'C':
				if (ssl_x509chain_load(NULL, &opts->chain,
				                       optarg) == -1) {
					fprintf(stderr, "%s: error loading "
					                "chain from '%s':\n",
					                argv0, optarg);
					if (errno) {
						fprintf(stderr, "%s\n",
						        strerror(errno));
					} else {
						ERR_print_errors_fp(stderr);
					}
					exit(EXIT_FAILURE);
				}
				break;
			case 'K':
				if (opts->key)
					EVP_PKEY_free(opts->key);
				opts->key = ssl_key_load(optarg);
				if (!opts->key) {
					fprintf(stderr, "%s: error loading lea"
					                "f key from '%s':\n",
					                argv0, optarg);
					if (errno) {
						fprintf(stderr, "%s\n",
						        strerror(errno));
					} else {
						ERR_print_errors_fp(stderr);
					}
					exit(EXIT_FAILURE);
				}
#ifndef OPENSSL_NO_DH
				if (!opts->dh) {
					opts->dh = ssl_dh_load(optarg);
				}
#endif /* !OPENSSL_NO_DH */
				break;
			case 't':
				if (!sys_isdir(optarg)) {
					fprintf(stderr, "%s: '%s' is not a "
					                "directory\n",
					                argv0, optarg);
					exit(EXIT_FAILURE);
				}
				if (opts->tgcrtdir)
					free(opts->tgcrtdir);
				opts->tgcrtdir = strdup(optarg);
				break;
			case 'O':
				opts->deny_ocsp = 1;
				break;
			case 'P':
				opts->passthrough = 1;
				break;
#ifndef OPENSSL_NO_DH
			case 'g':
				if (opts->dh)
					DH_free(opts->dh);
				opts->dh = ssl_dh_load(optarg);
				if (!opts->dh) {
					fprintf(stderr, "%s: error loading DH "
					                "params from '%s':\n",
					                argv0, optarg);
					if (errno) {
						fprintf(stderr, "%s\n",
						        strerror(errno));
					} else {
						ERR_print_errors_fp(stderr);
					}
					exit(EXIT_FAILURE);
				}
				break;
#endif /* !OPENSSL_NO_DH */
#ifndef OPENSSL_NO_ECDH
			case 'G':
			{
				EC_KEY *ec;
				if (opts->ecdhcurve)
					free(opts->ecdhcurve);
				if (!(ec = ssl_ec_by_name(optarg))) {
					fprintf(stderr, "%s: unknown curve "
					                "'%s'\n",
					                argv0, optarg);
					exit(EXIT_FAILURE);
				}
				EC_KEY_free(ec);
				opts->ecdhcurve = strdup(optarg);
				break;
			}
#endif /* !OPENSSL_NO_ECDH */
#ifdef SSL_OP_NO_COMPRESSION
			case 'Z':
				opts->sslcomp = 0;
				break;
#endif /* SSL_OP_NO_COMPRESSION */
			case 's':
				if (opts->ciphers)
					free(opts->ciphers);
				opts->ciphers = strdup(optarg);
				break;
			case 'e':
				free(natengine);
				natengine = strdup(optarg);
				break;
			case 'E':
				nat_list_engines();
				exit(EXIT_SUCCESS);
				break;
			case 'u':
				if (opts->dropuser)
					free(opts->dropuser);
				opts->dropuser = strdup(optarg);
				break;
			case 'p':
				if (opts->pidfile)
					free(opts->pidfile);
				opts->pidfile = strdup(optarg);
				break;
			case 'j':
				if (opts->jaildir)
					free(opts->jaildir);
				opts->jaildir = strdup(optarg);
				break;
			case 'l':
				if (opts->connectlog)
					free(opts->connectlog);
				opts->connectlog = strdup(optarg);
				break;
			case 'L':
				if (opts->contentlog)
					free(opts->contentlog);
				opts->contentlog = strdup(optarg);
				opts->contentlogdir = 0;
				break;
			case 'S':
				if (opts->contentlog)
					free(opts->contentlog);
				opts->contentlog = strdup(optarg);
				opts->contentlogdir = 1;
				break;
			case 'd':
				opts->detach = 1;
				break;
			case 'D':
				log_dbg_mode(LOG_DBG_MODE_ERRLOG);
				opts->debug = 1;
				break;
			case 'V':
				main_version();
				exit(EXIT_SUCCESS);
			case 'h':
				main_usage();
				exit(EXIT_SUCCESS);
			case '?':
				exit(EXIT_FAILURE);
			default:
				main_usage();
				exit(EXIT_FAILURE);
		}
	}
	argc -= optind;
	argv += optind;
	opts->spec = proxyspec_parse(&argc, &argv, natengine);

	/* usage checks */
	if (opts->detach && OPTS_DEBUG(opts)) {
		fprintf(stderr, "%s: -d and -D are mutually exclusive.\n",
		                argv0);
		exit(EXIT_FAILURE);
	}
	if (!opts->spec) {
		fprintf(stderr, "%s: no proxyspec specified.\n", argv0);
		exit(EXIT_FAILURE);
	}
	for (proxyspec_t *spec = opts->spec; spec; spec = spec->next) {
		if (spec->connect_addrlen || spec->sni_port)
			continue;
		if (!spec->natengine) {
			fprintf(stderr, "%s: no supported NAT engines "
			                "on this platform.\n"
			                "Only static addr and SNI proxyspecs "
			                "supported.\n", argv0);
			exit(EXIT_FAILURE);
		}
		if (spec->listen_addr.ss_family == AF_INET6 &&
		    !nat_ipv6ready(spec->natengine)) {
			fprintf(stderr, "%s: IPv6 not supported by '%s'\n",
			                argv0, spec->natengine);
			exit(EXIT_FAILURE);
		}
		spec->natlookup = nat_getlookupcb(spec->natengine);
		spec->natsocket = nat_getsocketcb(spec->natengine);
	}
	if (opts_has_ssl_spec(opts)) {
		if ((opts->cacrt || !opts->tgcrtdir) && !opts->cakey) {
			fprintf(stderr, "%s: no CA key specified (-k).\n",
			                argv0);
			exit(EXIT_FAILURE);
		}
		if (opts->cakey && !opts->cacrt) {
			fprintf(stderr, "%s: no CA cert specified (-c).\n",
			                argv0);
			exit(EXIT_FAILURE);
		}
		if (opts->cakey && opts->cacrt &&
		    (X509_check_private_key(opts->cacrt, opts->cakey) != 1)) {
			fprintf(stderr, "%s: CA cert does not match key.\n",
			                argv0);
			ERR_print_errors_fp(stderr);
			exit(EXIT_FAILURE);
		}
	}

	/* prevent multiple instances running */
	if (opts->pidfile) {
		pidfd = sys_pidf_open(opts->pidfile);
		if (pidfd == -1) {
			fprintf(stderr, "%s: cannot open PID file '%s' "
			                "- process already running?\n",
			                argv0, opts->pidfile);
			exit(EXIT_FAILURE);
		}
	}

	/* dynamic defaults */
	if (!opts->ciphers) {
		opts->ciphers = strdup("ALL:-aNULL");
		if (!opts->ciphers) {
			fprintf(stderr, "%s: out of memory.\n", argv0);
			exit(EXIT_FAILURE);
		}
	}
	if (!opts->jaildir && (geteuid() == 0) && !opts->contentlogdir) {
		opts->jaildir = strdup("/var/empty");
	}
	if (!opts->dropuser && !geteuid() && !getuid() &&
	    !opts->contentlogdir) {
		opts->dropuser = strdup("nobody");
	}
	if (opts_has_ssl_spec(opts) && !opts->key) {
		opts->key = ssl_key_genrsa(1024);
		if (!opts->key) {
			fprintf(stderr, "%s: error generating RSA key:\n",
			                argv0);
			ERR_print_errors_fp(stderr);
			exit(EXIT_FAILURE);
		}
		if (OPTS_DEBUG(opts)) {
			log_dbg_printf("Generated RSA key for leaf certs.\n");
		}
	}

	/* debugging */
	if (OPTS_DEBUG(opts)) {
		main_version();
		log_dbg_printf("proxyspecs:\n");
		for (proxyspec_t *spec = opts->spec; spec; spec = spec->next) {
			char *lbuf, *cbuf = NULL;
			lbuf = sys_sockaddr_str((struct sockaddr *)
			                        &spec->listen_addr,
			                        spec->listen_addrlen);
			if (spec->connect_addrlen) {
				cbuf = sys_sockaddr_str((struct sockaddr *)
				                        &spec->connect_addr,
				                        spec->connect_addrlen);
			}
			if (spec->sni_port) {
				asprintf(&cbuf, "sni %i", spec->sni_port);
			}
			log_dbg_printf("- %s %s %s %s\n", lbuf,
			               (spec->ssl ? "ssl" : "tcp"),
			               (spec->http ? "http" : "plain"),
			               (spec->natengine ? spec->natengine
			                                : cbuf));
			if (lbuf)
				free(lbuf);
			if (cbuf)
				free(cbuf);
		}
		if (opts->cacrt) {
			char *subj = ssl_x509_subject(opts->cacrt);
			log_dbg_printf("Loaded CA: '%s'\n", subj);
			free(subj);
#ifdef DEBUG_CERTIFICATE
			log_dbg_print_free(ssl_x509_to_str(opts->cacrt));
			log_dbg_print_free(ssl_x509_to_pem(opts->cacrt));
#endif /* DEBUG_CERTIFICATE */
		} else {
			log_dbg_printf("No CA loaded.\n");
		}
	}

	/*
	 * Initialize as much as possible before daemon() in order to be
	 * able to provide direct feedback to the user when failing.
	 */
	if (cachemgr_preinit() == -1) {
		fprintf(stderr, "%s: failed to preinit cachemgr.\n", argv0);
		exit(EXIT_FAILURE);
	}
	if (log_preinit(opts) == -1) {
		fprintf(stderr, "%s: failed to preinit logging.\n", argv0);
		exit(EXIT_FAILURE);
	}
	if (nat_preinit() == -1) {
		fprintf(stderr, "%s: failed to preinit NAT lookup.\n", argv0);
		exit(EXIT_FAILURE);
	}

	/* Bind listeners before dropping privileges */
	proxy_ctx_t *proxy = proxy_new(opts);
	if (!proxy) {
		fprintf(stderr, "%s: failed to initialize proxy.\n", argv0);
		exit(EXIT_FAILURE);
	}

	/* Drop privs, chroot, detach from TTY */
	if (sys_privdrop(opts->dropuser, opts->jaildir) == -1) {
		fprintf(stderr, "%s: failed to drop privileges: %s\n",
		                argv0, strerror(errno));
		exit(EXIT_FAILURE);
	}
	if (opts->detach) {
		if (OPTS_DEBUG(opts)) {
			log_dbg_printf("Detaching from TTY, see syslog for "
			               "errors after this point\n");
		}
		if (daemon(1, 0) == -1) {
			fprintf(stderr, "%s: failed to detach from TTY: %s\n",
			                argv0, strerror(errno));
			exit(EXIT_FAILURE);
		}
		log_err_mode(LOG_ERR_MODE_SYSLOG);
		ssl_reinit();
	}

	/* Post-privdrop/chroot/detach initialization, thread spawning */
	if (log_init(opts) == -1) {
		fprintf(stderr, "%s: failed to init log facility.\n", argv0);
		goto out_log_failed;
	}
	if (opts->pidfile && (sys_pidf_write(pidfd) == -1)) {
		log_err_printf("Failed to write PID to PID file '%s': %s\n",
		               opts->pidfile, strerror(errno));
		goto out_pidwrite_failed;
	}
	if (cachemgr_init() == -1) {
		log_err_printf("Failed to init cache manager.\n");
		goto out_cachemgr_failed;
	}
	if (nat_init() == -1) {
		log_err_printf("Failed to init NAT state table lookup.\n");
		goto out_nat_failed;
	}

	if (opts->tgcrtdir) {
		sys_dir_eachfile(opts->tgcrtdir, main_loadtgcrt, opts);
	}

	rv = EXIT_SUCCESS;

	proxy_run(proxy);
	proxy_free(proxy);
	nat_fini();
out_nat_failed:
	cachemgr_fini();
out_cachemgr_failed:
	if (opts->pidfile) {
		sys_pidf_close(pidfd, opts->pidfile);
	}
out_pidwrite_failed:
	log_fini();
out_log_failed:
	opts_free(opts);
	ssl_fini();
	return rv;
}
示例#28
0
文件: ssl.c 项目: caidongyun/backup
/*
 * Initialize OpenSSL and verify the random number generator works.
 * Returns -1 on failure, 0 on success.
 */
int
ssl_init(void)
{
#ifndef PURIFY
	int fd;
#endif /* !PURIFY */
	char buf[256];

	if (ssl_initialized)
		return 0;

	/* general initialization */
	SSL_library_init();
#ifdef PURIFY
	CRYPTO_umalloc_init();
	CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
#endif /* PURIFY */
	SSL_load_error_strings();
	OpenSSL_add_all_algorithms();

	/* thread-safety */
#ifdef OPENSSL_THREADS
	ssl_mutex_num = CRYPTO_num_locks();
	ssl_mutex = umalloc(ssl_mutex_num * sizeof(*ssl_mutex));
	int i;
	for (i = 0; i < ssl_mutex_num; i++) {
		pthread_mutex_init(&ssl_mutex[i], NULL);
	}
	CRYPTO_set_locking_callback(ssl_thr_locking_cb);
	CRYPTO_set_dynlock_create_callback(ssl_thr_dyn_create_cb);
	CRYPTO_set_dynlock_lock_callback(ssl_thr_dyn_lock_cb);
	CRYPTO_set_dynlock_destroy_callback(ssl_thr_dyn_destroy_cb);
#ifdef OPENSSL_NO_THREADID
	CRYPTO_set_id_callback(ssl_thr_id_cb);
#else /* !OPENSSL_NO_THREADID */
	CRYPTO_THREADID_set_callback(ssl_thr_id_cb);
#endif /* !OPENSSL_NO_THREADID */
#endif /* OPENSSL_THREADS */

	/* randomness */
#ifndef PURIFY
	if ((fd = open("/dev/urandom", O_RDONLY)) == -1) {
		log_err_printf("Error opening /dev/urandom for reading: %s\n",
		               strerror(errno));
		return -1;
	}
	while (!RAND_status()) {
		if (read(fd, buf, sizeof(buf)) == -1) {
			log_err_printf("Error reading from /dev/urandom: %s\n",
			               strerror(errno));
			close(fd);
			return -1;
		}
		RAND_seed(buf, sizeof(buf));
	}
	close(fd);
	if (!RAND_poll()) {
		log_err_printf("RAND_poll() failed.\n");
		return -1;
	}
#else /* PURIFY */
	log_err_printf("Warning: not seeding OpenSSL RAND due to PURITY!\n");
	memset(buf, 0, sizeof(buf));
	while (!RAND_status()) {
		RAND_seed(buf, sizeof(buf));
	}
#endif /* PURIFY */

#ifdef USE_FOOTPRINT_HACKS
	/* HACK: disable compression by zeroing the global comp algo stack.
	 * This lowers the per-connection memory footprint by ~500k. */
	STACK_OF(SSL_COMP)* comp_methods = SSL_COMP_get_compression_methods();
	sk_SSL_COMP_zero(comp_methods);
#endif /* USE_FOOTPRINT_HACKS */

	ssl_initialized = 1;
	return 0;
}
示例#29
0
文件: log.c 项目: cgroschupp/sslsplit
log_content_format_pathspec(const char *logspec,
                            char *srchost, char *srcport,
                            char *dsthost, char *dstport,
                            char *exec_path, char *user, char *group)
{
	/* set up buffer to hold our generated file path */
	size_t path_buflen = PATH_BUF_INC;
	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 */
		char addrbuf[INET6_ADDRSTRLEN + 8]; /* [host]:port */

		/* 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':
				if (snprintf(addrbuf, sizeof(addrbuf),
				             "%s,%s", dsthost, dstport) < 0) {
					addrbuf[0] = '?';
					addrbuf[1] = '\0';
				}
				elem = addrbuf;
				elem_len = strlen(addrbuf);
				break;
			case 'D':
				elem = dsthost;
				elem_len = strlen(dsthost);
				break;
			case 'p':
				elem = dstport;
				elem_len = strlen(dstport);
				break;
			case 's':
				if (snprintf(addrbuf, sizeof(addrbuf),
				             "%s,%s", srchost, srcport) < 0) {
					addrbuf[0] = '?';
					addrbuf[1] = '\0';
				}
				elem = addrbuf;
				elem_len = strlen(addrbuf);
				break;
			case 'S':
				elem = srchost;
				elem_len = strlen(srchost);
				break;
			case 'q':
				elem = srcport;
				elem_len = strlen(srcport);
				break;
			case 'x':
				if (exec_path) {
					char *match = exec_path;
					while ((match = strchr(match, '/')) != NULL) {
						match++;
						elem = match;
					}
					elem_len = elem ? strlen(elem) : 0;
				} 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;
		}

		if (elem_len > 0) {
			/* growing the buffer to fit elem_len + terminating \0 */
			if (path_buflen - path_len < elem_len + 1) {
				/* Grow in PATH_BUF_INC chunks.
				 * Note that the use of `PATH_BUF_INC' provides
				 * our guaranteed space for a trailing '\0' */
				path_buflen += elem_len + PATH_BUF_INC;
				char *newbuf = realloc(path_buf, path_buflen);
				if (newbuf == NULL) {
					log_err_printf("failed to reallocate"
					               " path buffer\n");
					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;
}
示例#30
0
文件: log.c 项目: cgroschupp/sslsplit
int
log_content_open(log_content_ctx_t **pctx, opts_t *opts,
                 char *srchost, char *srcport,
                 char *dsthost, char *dstport,
                 char *exec_path, char *user, char *group)
{
	log_content_ctx_t *ctx;

	if (*pctx)
		return 0;
	*pctx = malloc(sizeof(log_content_ctx_t));
	if (!*pctx)
		return -1;
	ctx = *pctx;

	if (opts->contentlog_isdir) {
		/* per-connection-file content log (-S) */
		char timebuf[24];
		time_t epoch;
		struct tm *utc;
		char *dsthost_clean, *srchost_clean;

		if (time(&epoch) == -1) {
			log_err_printf("Failed to get time\n");
			goto errout;
		}
		if ((utc = gmtime(&epoch)) == NULL) {
			log_err_printf("Failed to convert time: %s (%i)\n",
			               strerror(errno), errno);
			goto errout;
		}
		if (!strftime(timebuf, sizeof(timebuf),
		              "%Y%m%dT%H%M%SZ", utc)) {
			log_err_printf("Failed to format time: %s (%i)\n",
			               strerror(errno), errno);
			goto errout;
		}
		srchost_clean = sys_ip46str_sanitize(srchost);
		if (!srchost_clean) {
			log_err_printf("Failed to sanitize srchost\n");
			goto errout;
		}
		dsthost_clean = sys_ip46str_sanitize(dsthost);
		if (!dsthost_clean) {
			log_err_printf("Failed to sanitize dsthost\n");
			free(srchost_clean);
			goto errout;
		}
		if (asprintf(&ctx->u.dir.filename, "%s/%s-%s,%s-%s,%s.log",
		             opts->contentlog, timebuf,
		             srchost_clean, srcport,
		             dsthost_clean, dstport) < 0) {
			log_err_printf("Failed to format filename: %s (%i)\n",
			               strerror(errno), errno);
			free(srchost_clean);
			free(dsthost_clean);
			goto errout;
		}
		free(srchost_clean);
		free(dsthost_clean);
	} else if (opts->contentlog_isspec) {
		/* per-connection-file content log with logspec (-F) */
		char *dsthost_clean, *srchost_clean;
		srchost_clean = sys_ip46str_sanitize(srchost);
		if (!srchost_clean) {
			log_err_printf("Failed to sanitize srchost\n");
			goto errout;
		}
		dsthost_clean = sys_ip46str_sanitize(dsthost);
		if (!dsthost_clean) {
			log_err_printf("Failed to sanitize dsthost\n");
			free(srchost_clean);
			goto errout;
		}
		ctx->u.spec.filename = log_content_format_pathspec(
		                                       opts->contentlog,
		                                       srchost_clean, srcport,
		                                       dsthost_clean, dstport,
		                                       exec_path, user, group);
		free(srchost_clean);
		free(dsthost_clean);
		if (!ctx->u.spec.filename) {
			goto errout;
		}
	} else {
		/* single-file content log (-L) */
		if (asprintf(&ctx->u.file.header_req, "[%s]:%s -> [%s]:%s",
		             srchost, srcport, dsthost, dstport) < 0) {
			goto errout;
		}
		if (asprintf(&ctx->u.file.header_resp, "[%s]:%s -> [%s]:%s",
		             dsthost, dstport, srchost, srcport) < 0) {
			free(ctx->u.file.header_req);
			goto errout;
		}
	}

	/* submit an open event */
	if (logger_open(content_log, ctx) == -1)
		goto errout;
	ctx->open = 1;
	return 0;
errout:
	free(ctx);
	*pctx = NULL;
	return -1;
}