Redirect user to login page and redirect again to orig target after successful authentication, currently supported only on OpenBSD

Get ethernet address and compare with the one in userdb, on each conn setup
Create user_auth options
Rename and clean-up
pull/13/head
Soner Tari 5 years ago
parent fb25c45c66
commit cde3fbca3f

@ -82,7 +82,7 @@ DEBUG_CFLAGS?= -g
#FEATURES+= -DPURIFY
# Define to add proxy state machine debugging; dump state in debug mode.
FEATURES+= -DDEBUG_PROXY
#FEATURES+= -DDEBUG_PROXY
# Define to add certificate debugging; dump all certificates in debug mode.
#FEATURES+= -DDEBUG_CERTIFICATE
@ -97,7 +97,7 @@ FEATURES+= -DDEBUG_PROXY
#FEATURES+= -DDEBUG_THREAD
# Define to add privilege separation server event loop debugging.
FEATURES+= -DDEBUG_PRIVSEP_SERVER
#FEATURES+= -DDEBUG_PRIVSEP_SERVER
# Define to add diagnostic output for debugging option parsing.
#FEATURES+= -DDEBUG_OPTS

@ -618,6 +618,24 @@ main(int argc, char *argv[])
}
}
if (opts->user_auth) {
// @todo Check if we can really pass the db var into the child process for privsep
// https://www.sqlite.org/faq.html:
// "Under Unix, you should not carry an open SQLite database across a fork() system call into the child process."
if (sqlite3_open("/var/db/duaf.db", &opts->userdb)) {
fprintf(stderr, "Error opening user db file: %s\n", sqlite3_errmsg(opts->userdb));
sqlite3_close(opts->userdb);
exit(EXIT_FAILURE);
}
// @todo Change mac column to ether
int rc = sqlite3_prepare_v2(opts->userdb, "UPDATE ip2user SET atime = ?1 WHERE ip = ?2 AND user = ?3 AND mac = ?4", 200, &opts->update_user_atime, NULL);
if (rc) {
log_err_level_printf(LOG_CRIT, "Error preparing update_user_atime sql stmt: %s\n", sqlite3_errmsg(opts->userdb));
sqlite3_close(opts->userdb);
exit(EXIT_FAILURE);
}
}
/* dynamic defaults */
if (!opts->ciphers) {
opts->ciphers = strdup(DFLT_CIPHERS);

@ -70,22 +70,6 @@ opts_new(void)
opts->remove_http_accept_encoding = 1;
opts->remove_http_referer = 1;
opts->verify_peer = 1;
// @todo Check if we can pass the db var into the child process for privsep
// https://www.sqlite.org/faq.html:
// "Under Unix, you should not carry an open SQLite database across a fork() system call into the child process."
if (sqlite3_open("/var/db/duaf.db", &opts->userdb)) {
fprintf(stderr, "Error opening user db file: %s\n", sqlite3_errmsg(opts->userdb));
sqlite3_close(opts->userdb);
exit(EXIT_FAILURE);
}
int rc = sqlite3_prepare_v2(opts->userdb, "UPDATE ip2user SET atime = ?1 WHERE ip = ?2 AND user = ?3", 100, &opts->update_user_atime_sql_stmt, NULL);
if (rc) {
log_err_level_printf(LOG_CRIT, "Error preparing update_user_atime_sql_stmt: %s\n", sqlite3_errmsg(opts->userdb));
sqlite3_close(opts->userdb);
exit(EXIT_FAILURE);
}
return opts;
}
@ -176,8 +160,13 @@ opts_free(opts_t *opts)
free(opts->mirrortarget);
}
#endif /* !WITHOUT_MIRROR */
sqlite3_finalize(opts->update_user_atime_sql_stmt);
sqlite3_close(opts->userdb);
if (opts->user_auth_url) {
free(opts->user_auth_url);
}
if (opts->user_auth) {
sqlite3_finalize(opts->update_user_atime);
sqlite3_close(opts->userdb);
}
memset(opts, 0, sizeof(opts_t));
free(opts);
}
@ -1461,6 +1450,32 @@ opts_unset_allow_wrong_host(opts_t *opts)
opts->allow_wrong_host = 0;
}
static void
opts_set_user_auth(UNUSED opts_t *opts)
{
#ifdef __OpenBSD__
// Enable user auth only on OpenBSD
opts->user_auth = 1;
#endif /* __OpenBSD__ */
}
static void
opts_unset_user_auth(opts_t *opts)
{
opts->user_auth = 0;
}
static void
opts_set_user_auth_url(opts_t *opts, const char *optarg)
{
if (opts->user_auth_url)
free(opts->user_auth_url);
opts->user_auth_url = strdup(optarg);
#ifdef DEBUG_OPTS
log_dbg_printf("UserAuthURL: %s\n", opts->user_auth_url);
#endif /* DEBUG_OPTS */
}
static int
check_value_yesno(const char *value, const char *name, int line_num)
{
@ -1621,6 +1636,17 @@ set_option(opts_t *opts, const char *argv0,
#endif /* DEBUG_OPTS */
} else if (!strncmp(name, "DebugLevel", 11)) {
opts_set_debug_level(value);
} else if (!strncmp(name, "UserAuth", 9)) {
yes = check_value_yesno(value, "UserAuth", line_num);
if (yes == -1) {
goto leave;
}
yes ? opts_set_user_auth(opts) : opts_unset_user_auth(opts);
#ifdef DEBUG_OPTS
log_dbg_printf("UserAuth: %u\n", opts->user_auth);
#endif /* DEBUG_OPTS */
} else if (!strncmp(name, "UserAuthURL", 12)) {
opts_set_user_auth_url(opts, value);
} else if (!strncmp(name, "ProxySpec", 10)) {
/* Use MAX_TOKEN instead of computing the actual number of tokens in value */
char **argv = malloc(sizeof(char *) * MAX_TOKEN);

@ -143,14 +143,17 @@ typedef struct opts {
unsigned int remove_http_referer: 1;
unsigned int verify_peer: 1;
unsigned int allow_wrong_host: 1;
unsigned int user_auth: 1;
char *user_auth_url;
sqlite3 *userdb;
struct sqlite3_stmt *update_user_atime_sql_stmt;
struct sqlite3_stmt *update_user_atime;
} opts_t;
typedef struct dbkeys {
typedef struct userdbkeys {
char ip[46];
char user[32];
} dbkeys_t;
char ether[18];
} userdbkeys_t;
void NORET oom_die(const char *) NONNULL(1);

@ -378,30 +378,26 @@ privsep_server_certfile(const char *fn)
}
static int WUNRES
privsep_server_update_atime(opts_t *opts, const dbkeys_t *keys)
privsep_server_update_atime(opts_t *opts, const userdbkeys_t *keys)
{
log_dbg_printf("privsep_server_update_atime: ENTER\n");
time_t atime = time(NULL);
sqlite3_reset(opts->update_user_atime_sql_stmt);
sqlite3_bind_int(opts->update_user_atime_sql_stmt, 1, atime);
sqlite3_bind_text(opts->update_user_atime_sql_stmt, 2, keys->ip, -1, NULL);
sqlite3_bind_text(opts->update_user_atime_sql_stmt, 3, keys->user, -1, NULL);
sqlite3_reset(opts->update_user_atime);
sqlite3_bind_int(opts->update_user_atime, 1, atime);
sqlite3_bind_text(opts->update_user_atime, 2, keys->ip, -1, NULL);
sqlite3_bind_text(opts->update_user_atime, 3, keys->user, -1, NULL);
sqlite3_bind_text(opts->update_user_atime, 4, keys->ether, -1, NULL);
int count = 0;
int rc;
do {
rc = sqlite3_step(opts->update_user_atime_sql_stmt);
usleep(100);
// Retry in case we cannot acquire db file or database: SQLITE_BUSY or SQLITE_LOCKED respectively
} while ((rc == SQLITE_BUSY || rc == SQLITE_LOCKED) && count++ < 5);
int rc = sqlite3_step(opts->update_user_atime);
// @todo Should we retry in case we cannot acquire db file or database: SQLITE_BUSY or SQLITE_LOCKED respectively?
if (rc == SQLITE_DONE) {
log_err_level_printf(LOG_CRIT, "privsep_server_update_atime: Updated atime of user %s=%lld\n", "soner", (long long)atime);
log_dbg_printf("privsep_server_update_atime: Updated atime of user %s=%lld\n", keys->user, (long long)atime);
} else {
log_err_level_printf(LOG_CRIT, "privsep_server_update_atime: Error updating user atime: %s\n", sqlite3_errmsg(opts->userdb));
log_err_level_printf(LOG_ERR, "privsep_server_update_atime: Error updating user atime: %s\n", sqlite3_errmsg(opts->userdb));
}
sqlite3_reset(opts->update_user_atime_sql_stmt);
sqlite3_reset(opts->update_user_atime);
return 0;
}
@ -588,10 +584,9 @@ privsep_server_handle_req(opts_t *opts, int srvsock)
break;
}
case PRIVSEP_REQ_UPDATE_ATIME: {
dbkeys_t *arg;
userdbkeys_t *arg;
log_dbg_printf("ENTER PRIVSEP_REQ_UPDATE_ATIME\n");
if (n != sizeof(char) + sizeof(dbkeys_t)) {
if (n != sizeof(char) + sizeof(userdbkeys_t)) {
ans[0] = PRIVSEP_ANS_INVALID;
if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) {
log_err_level_printf(LOG_CRIT, "Sending message failed: %s (%i"
@ -600,12 +595,9 @@ privsep_server_handle_req(opts_t *opts, int srvsock)
")\n", strerror(errno), errno);
return -1;
}
log_dbg_printf(">>>>>>>>> Returning: n=%zd, arg+1=%lu\n", n, sizeof(char) + sizeof(dbkeys_t));
return 0;
}
log_dbg_printf(">>>>>>>>> Continue n=%zd, arg+1=%lu\n", n, sizeof(char) + sizeof(dbkeys_t));
if (!(arg = malloc(n))) {
ans[0] = PRIVSEP_ANS_SYS_ERR;
*((int*)&ans[1]) = errno;
@ -1087,15 +1079,15 @@ privsep_client_close(int clisock)
}
int
privsep_client_update_atime(int clisock, const dbkeys_t *dbkeys)
privsep_client_update_atime(int clisock, const userdbkeys_t *keys)
{
char ans[PRIVSEP_MAX_ANS_SIZE];
char req[1 + sizeof(dbkeys_t)];
char req[1 + sizeof(userdbkeys_t)];
int rv = -1;
ssize_t n;
req[0] = PRIVSEP_REQ_UPDATE_ATIME;
memcpy(req + 1, dbkeys, sizeof(req) - 1);
memcpy(req + 1, keys, sizeof(req) - 1);
if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) {
return -1;

@ -39,7 +39,7 @@ int privsep_client_opensock(int, const proxyspec_t *spec);
int privsep_client_opensock_child(int, const proxyspec_t *spec);
int privsep_client_certfile(int, const char *);
int privsep_client_close(int);
int privsep_client_update_atime(int, const dbkeys_t *);
int privsep_client_update_atime(int, const userdbkeys_t *);
#endif /* !PRIVSEP_H */
/* vim: set noet ft=c: */

@ -374,7 +374,7 @@ protohttp_filter_request_header_line(const char *line, pxy_conn_ctx_t *ctx, prot
return (char*)line;
}
static void NONNULL(1,2,3,4)
static void NONNULL(1,2,3,4)
protohttp_filter_request_header(struct evbuffer *inbuf, struct evbuffer *outbuf, pxy_conn_ctx_t *ctx, protohttp_ctx_t *http_ctx)
{
char *line;
@ -434,9 +434,66 @@ protohttp_filter_request_header(struct evbuffer *inbuf, struct evbuffer *outbuf,
}
}
static char * NONNULL(1,2)
protohttp_get_url(struct evbuffer *inbuf, pxy_conn_ctx_t *ctx)
{
char *line;
char *path = NULL;
char *host = NULL;
char *url = NULL;
while ((!host || !path) && (line = evbuffer_readln(inbuf, NULL, EVBUFFER_EOL_CRLF))) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protohttp_get_url: %s, fd=%d\n", line, ctx->fd);
#endif /* DEBUG_PROXY */
//GET / HTTP/1.1
if (!path && !strncasecmp(line, "GET ", 4)) {
path = strdup(util_skipws(line + 4));
path = strsep(&path, " \t");
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protohttp_get_url: path=%s, fd=%d\n", path, ctx->fd);
#endif /* DEBUG_PROXY */
//Host: example.com
} else if (!host && !strncasecmp(line, "Host:", 5)) {
host = strdup(util_skipws(line + 5));
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protohttp_get_url: host=%s, fd=%d\n", host, ctx->fd);
#endif /* DEBUG_PROXY */
}
free(line);
}
if (host && path) {
// Assume that path will always have a leading /, so do not insert an extra / in between host and path
size_t url_size = 4 + 1 + 3 + strlen(host) + strlen(path) + 1;
url = malloc(url_size);
snprintf(url, url_size, "http%s://%s%s", ctx->spec->ssl ? "s": "", host, path);
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protohttp_get_url: url=%s, fd=%d\n", url, ctx->fd);
#endif /* DEBUG_PROXY */
}
if (host)
free(host);
if (path)
free(path);
return url;
}
static void NONNULL(1)
protohttp_bev_readcb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
{
const char redirect[] =
"HTTP/1.1 302 Found\r\n"
"Location: %s\r\n"
"\r\n";
const char redirect_url[] =
"HTTP/1.1 302 Found\r\n"
"Location: %s?SSLproxy=%s\r\n"
"\r\n";
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protohttp_bev_readcb_src: ENTER, size=%zu, fd=%d\n",
evbuffer_get_length(bufferevent_get_input(bev)), ctx->fd);
@ -451,6 +508,23 @@ protohttp_bev_readcb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
struct evbuffer *inbuf = bufferevent_get_input(bev);
struct evbuffer *outbuf = bufferevent_get_output(ctx->dst.bev);
if (ctx->opts->user_auth && !ctx->user) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "protohttp_bev_readcb_src: Redirecting conn, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
char *url = protohttp_get_url(inbuf, ctx);
pxy_discard_inbuf(bev);
if (url) {
evbuffer_add_printf(bufferevent_get_output(bev), redirect_url, ctx->opts->user_auth_url, url);
free(url);
} else {
evbuffer_add_printf(bufferevent_get_output(bev), redirect, ctx->opts->user_auth_url);
}
ctx->redirected = 1;
return;
}
// We insert our special header line to the first packet we get, e.g. right after the first \r\n in the case of http
// @todo Should we look for GET/POST or Host header lines to detect the first packet?
// But there is no guarantee that they will exist, due to fragmentation.

@ -385,6 +385,14 @@ prototcp_bev_writecb_src(struct bufferevent *bev, pxy_conn_ctx_t *ctx)
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_writecb_src: ENTER, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
if (ctx->opts->user_auth && !ctx->user && ctx->redirected) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "prototcp_bev_writecb_src: Closing redirected conn, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
pxy_conn_term(ctx, 1);
return;
}
if (ctx->dst.closed) {
if (pxy_try_close_conn_end(&ctx->src, ctx) == 1) {
#ifdef DEBUG_PROXY

@ -48,9 +48,15 @@
#include <event2/listener.h>
#ifdef HAVE_NETFILTER
#include <glob.h>
#endif /* HAVE_NETFILTER */
#include <net/if_arp.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/route.h>
#include <netinet/if_ether.h>
#ifdef __OpenBSD__
#include <net/if_dl.h>
#endif /* __OpenBSD__ */
/*
* Maximum size of data to buffer per connection direction before
@ -362,23 +368,27 @@ pxy_conn_ctx_free(pxy_conn_ctx_t *ctx, int by_requestor)
}
}
struct dbkeys *ipuser = malloc(sizeof(struct dbkeys));
if (ipuser) {
memset(ipuser, 0, sizeof(dbkeys_t));
memcpy(ipuser->ip, ctx->srchost_str, strlen(ctx->srchost_str));
memcpy(ipuser->user, ctx->user, strlen(ctx->user));
if (ctx->opts->user_auth && ctx->srchost_str && ctx->user && ctx->ether) {
struct userdbkeys *keys = malloc(sizeof(userdbkeys_t));
if (keys) {
memset(keys, 0, sizeof(userdbkeys_t));
// @todo Should limit copy with max dest size?
memcpy(keys->ip, ctx->srchost_str, strlen(ctx->srchost_str));
memcpy(keys->user, ctx->user, strlen(ctx->user));
memcpy(keys->ether, ctx->ether, strlen(ctx->ether));
if (privsep_client_update_atime(ctx->clisock, ipuser) == -1) {
if (privsep_client_update_atime(ctx->clisock, keys) == -1) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_conn_ctx_free: Error updating user atime: %s, ctx->fd=%d\n", sqlite3_errmsg(ctx->opts->userdb), ctx->fd);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_conn_ctx_free: Error updating user atime: %s, ctx->fd=%d\n", sqlite3_errmsg(ctx->opts->userdb), ctx->fd);
#endif /* DEBUG_PROXY */
} else {
} else {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_conn_ctx_free: Successfully updated user atime, ctx->fd=%d\n", ctx->fd);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_conn_ctx_free: Successfully updated user atime, ctx->fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
}
free(keys);
}
free(ipuser);
}
}
pxy_thrmgr_detach(ctx);
if (ctx->srchost_str) {
@ -415,6 +425,13 @@ pxy_conn_ctx_free(pxy_conn_ctx_t *ctx, int by_requestor)
ctx->protoctx->proto_free(ctx);
}
free(ctx->protoctx);
if (ctx->user) {
free(ctx->user);
}
if (ctx->ether) {
free(ctx->ether);
}
free(ctx);
}
@ -1472,50 +1489,102 @@ pxy_fd_readcb(evutil_socket_t fd, UNUSED short what, void *arg)
ctx->protoctx->fd_readcb(fd, what, arg);
}
#ifdef __OpenBSD__
static void
pxy_conn_identify_user(UNUSED evutil_socket_t fd, UNUSED short what, void *arg)
identify_user(UNUSED evutil_socket_t fd, UNUSED short what, void *arg)
{
pxy_conn_ctx_t *ctx = arg;
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_conn_identify_user: ENTER, ctx->fd=%d\n", ctx->fd);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "identify_user: ENTER, ctx->fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
if (ctx->identify_user_count++ < 50) {
int rc;
sqlite3_reset(ctx->thr->get_user_sql_stmt);
sqlite3_bind_text(ctx->thr->get_user_sql_stmt, 1, ctx->srchost_str, -1, NULL);
rc = sqlite3_step(ctx->thr->get_user_sql_stmt);
if (rc == SQLITE_ROW) {
ctx->user = strdup((char *)sqlite3_column_text(ctx->thr->get_user_sql_stmt, 0));
sqlite3_reset(ctx->thr->get_user_sql_stmt);
if (ctx->ev) {
event_free(ctx->ev);
ctx->ev = NULL;
}
if (ctx->identify_user_count++ >= 50) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_conn_identify_user: Conn user=%s, ctx->fd=%d\n", ctx->user, ctx->fd);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "identify_user: Cannot get conn user, ctx->fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
} else if (rc == SQLITE_DONE) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_conn_identify_user: Conn has no user, ctx->fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
}
goto redirect;
} else {
int rc;
sqlite3_reset(ctx->thr->get_user);
sqlite3_bind_text(ctx->thr->get_user, 1, ctx->srchost_str, -1, NULL);
rc = sqlite3_step(ctx->thr->get_user);
event_free(ctx->ev);
// Retry in case we cannot acquire db file or database: SQLITE_BUSY or SQLITE_LOCKED respectively
if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED) {
ctx->ev = event_new(ctx->evbase, -1, 0, pxy_conn_identify_user, ctx);
ctx->ev = event_new(ctx->evbase, -1, 0, identify_user, ctx);
if (!ctx->ev)
goto memout;
struct timeval retry_delay = {0, 100};
event_add(ctx->ev, &retry_delay);
return;
} else if (rc == SQLITE_DONE) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "identify_user: Conn has no user, ctx->fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
goto redirect;
} else if (rc == SQLITE_ROW) {
char *ether = (char *)sqlite3_column_text(ctx->thr->get_user, 1);
if (strncmp(ether, ctx->ether, 17)) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "identify_user: Ethernet addresses do not match, db=%s, arp cache=%s, ctx->fd=%d\n", ether, ctx->ether, ctx->fd);
#endif /* DEBUG_PROXY */
goto redirect;
}
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "identify_user: Passed ethernet address test, %s, ctx->fd=%d\n", ether, ctx->fd);
#endif /* DEBUG_PROXY */
int atime = sqlite3_column_int(ctx->thr->get_user, 2);
time_t now = time(NULL);
if (now - atime > 300) {
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "identify_user: User entry timed out, now=%lld, atime=%u, ctx->fd=%d\n", (long long)now, atime, ctx->fd);
#endif /* DEBUG_PROXY */
goto redirect;
}
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "identify_user: Passed atime test, %u, ctx->fd=%d\n", atime, ctx->fd);
#endif /* DEBUG_PROXY */
ctx->user = strdup((char *)sqlite3_column_text(ctx->thr->get_user, 0));
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "identify_user: Conn user=%s, ctx->fd=%d\n", ctx->user, ctx->fd);
#endif /* DEBUG_PROXY */
}
}
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_conn_identify_user: Passed user identification, ctx->fd=%d\n", ctx->fd);
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "identify_user: Passed user identification, ctx->fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
redirect:
sqlite3_reset(ctx->thr->get_user);
// @todo Make this a callback function for different protos?
// Redirect http only
if (!ctx->spec->http) {
goto memout;
}
if (ctx->ev) {
event_free(ctx->ev);
ctx->ev = NULL;
}
/* for SSL, defer dst connection setup to initial_readcb */
if (ctx->spec->ssl) {
// @todo Move this code to fd_readcb of ssl proto
@ -1534,6 +1603,120 @@ memout:
pxy_conn_ctx_free(ctx, 1);
}
/*
* This is a modified version of the same function from OpenBSD sources,
* which has a 3-clause BSD license.
*/
static char *
ether_str(struct sockaddr_dl *sdl)
{
char hbuf[NI_MAXHOST];
u_char *cp;
if (sdl->sdl_alen) {
cp = (u_char *)LLADDR(sdl);
snprintf(hbuf, sizeof(hbuf), "%02x:%02x:%02x:%02x:%02x:%02x",
cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
return strdup(hbuf);
} else {
return NULL;
}
}
/*
* This is a modified version of a similar function from OpenBSD sources,
* which has a 3-clause BSD license.
*/
static int
get_client_ether(in_addr_t addr, pxy_conn_ctx_t *ctx)
{
int mib[7];
size_t needed;
char *lim, *buf = NULL, *next;
struct rt_msghdr *rtm;
struct sockaddr_inarp *sin;
struct sockaddr_dl *sdl;
int found_entry = 0;
int rdomain = getrtable();
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = AF_INET;
mib[4] = NET_RT_FLAGS;
mib[5] = RTF_LLINFO;
mib[6] = rdomain;
while (1) {
if (sysctl(mib, 7, NULL, &needed, NULL, 0) == -1) {
log_err_level_printf(LOG_WARNING, "route-sysctl-estimate\n");
}
if (needed == 0) {
return found_entry;
}
if ((buf = realloc(buf, needed)) == NULL) {
return -1;
}
if (sysctl(mib, 7, buf, &needed, NULL, 0) == -1) {
if (errno == ENOMEM)
continue;
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "actual retrieval of routing table\n");
#endif /* DEBUG_PROXY */
}
lim = buf + needed;
break;
}
int expired = 0;
int incomplete = 0;
for (next = buf; next < lim; next += rtm->rtm_msglen) {
rtm = (struct rt_msghdr *)next;
if (rtm->rtm_version != RTM_VERSION)
continue;
sin = (struct sockaddr_inarp *)(next + rtm->rtm_hdrlen);
sdl = (struct sockaddr_dl *)(sin + 1);
if (addr) {
if (addr != sin->sin_addr.s_addr)
continue;
found_entry++;
}
char *expire = NULL;
if (rtm->rtm_flags & (RTF_PERMANENT_ARP | RTF_LOCAL)) {
expire = "permanent";
} else if (rtm->rtm_rmx.rmx_expire == 0) {
expire = "static";
} else if (rtm->rtm_rmx.rmx_expire > time(NULL)) {
expire = "active";
} else {
expire = "expired";
expired++;
}
char *ether = ether_str(sdl);
if (ether) {
// Record the first unexpired complete entry
if (!ctx->ether && (found_entry - expired) == 1) {
// Dup before assignment because we free local var ether below
ctx->ether = strdup(ether);
}
} else {
incomplete++;
}
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "Arp entry %u for %s: %s (%s)\n", found_entry, inet_ntoa(sin->sin_addr), ether ? ether : "incomplete", expire);
#endif /* DEBUG_PROXY */
if (ether) {
free(ether);
}
}
free(buf);
return found_entry - expired - incomplete;
}
#endif /* __OpenBSD__ */
/*
* Callback for accept events on the socket listener bufferevent.
* Called when a new incoming connection has been accepted.
@ -1616,14 +1799,43 @@ pxy_conn_setup(evutil_socket_t fd,
memcpy(&ctx->srcaddr, peeraddr, ctx->srcaddrlen);
}
ctx->ev = event_new(ctx->evbase, -1, 0, pxy_conn_identify_user, ctx);
if (!ctx->ev)
goto memout;
event_active(ctx->ev, 0, 0);
return;
if (ctx->opts->user_auth) {
#ifdef __OpenBSD__
int ec = get_client_ether(((struct sockaddr_in *)peeraddr)->sin_addr.s_addr, ctx);
if (ec == 1) {
ctx->ev = event_new(ctx->evbase, -1, 0, identify_user, ctx);
if (!ctx->ev)
goto memout;
event_active(ctx->ev, 0, 0);
return;
} else if (ec == 0) {
log_err_level_printf(LOG_CRIT, "Cannot find ethernet address of client IP address\n");
} else if (ec > 1) {
log_err_level_printf(LOG_CRIT, "Multiple ethernet addresses for the same client IP address\n");
} else {
// ec == -1
goto memout;
}
#endif /* __OpenBSD__ */
log_err_level_printf(LOG_CRIT, "Aborting connection setup (user auth)!\n");
goto out;
} else {
/* for SSL, defer dst connection setup to initial_readcb */
if (ctx->spec->ssl) {
// @todo Move this code to fd_readcb of ssl proto
ctx->ev = event_new(ctx->evbase, ctx->fd, EV_READ, ctx->protoctx->fd_readcb, ctx);
if (!ctx->ev)
goto memout;
event_add(ctx->ev, NULL);
} else {
ctx->protoctx->fd_readcb(ctx->fd, 0, ctx);
}
return;
}
memout:
log_err_level_printf(LOG_CRIT, "Aborting connection setup (out of memory)!\n");
out:
evutil_closesocket(fd);
pxy_conn_ctx_free(ctx, 1);
}

@ -278,6 +278,9 @@ struct pxy_conn_ctx {
unsigned int identify_user_count;
char *user;
char *ether;
unsigned int ether_count;
unsigned int redirected : 1;
#ifdef HAVE_LOCAL_PROCINFO
/* local process information */

@ -422,9 +422,7 @@ pxy_thrmgr_run(pxy_thrmgr_ctx_t *ctx)
ctx->thr[idx]->timeout_count = 0;
ctx->thr[idx]->thrmgr = ctx;
int rc;
rc = sqlite3_prepare_v2(ctx->opts->userdb, "SELECT user FROM ip2user WHERE ip = ?1", 100, &ctx->thr[idx]->get_user_sql_stmt, NULL);
if (rc) {
if (ctx->opts->user_auth && sqlite3_prepare_v2(ctx->opts->userdb, "SELECT user,mac,atime FROM ip2user WHERE ip = ?1", 100, &ctx->thr[idx]->get_user, NULL)) {
log_err_level_printf(LOG_CRIT, "Error preparing get_user_sql_stmt: %s\n", sqlite3_errmsg(ctx->opts->userdb));
goto leave;
}
@ -499,7 +497,9 @@ pxy_thrmgr_free(pxy_thrmgr_ctx_t *ctx)
if (ctx->thr[idx]->evbase) {
event_base_free(ctx->thr[idx]->evbase);
}
sqlite3_finalize(ctx->thr[idx]->get_user_sql_stmt);
if (ctx->opts->user_auth) {
sqlite3_finalize(ctx->thr[idx]->get_user);
}
free(ctx->thr[idx]);
}
free(ctx->thr);

@ -63,7 +63,7 @@ typedef struct pxy_thr_ctx {
long long unsigned int extif_out_bytes;
unsigned short stats_idx;
pxy_conn_ctx_t *conns;
struct sqlite3_stmt *get_user_sql_stmt;
struct sqlite3_stmt *get_user;
} pxy_thr_ctx_t;
struct pxy_thrmgr_ctx {

@ -202,6 +202,12 @@ VerifyPeer yes
# Helps pass the wrong.host test at https://badssl.com.
AllowWrongHost no
# Require authentication for users to use SSLproxy
#UserAuth no
# Redirect URL for users to log in to the system
#UserAuthURL https://192.168.0.1/userdblogin.php
# Proxy specifications
# type listenaddr+port up:utmport ua:utmaddr ra:returnaddr
#ProxySpec https 127.0.0.1 8443 up:8080 ua:127.0.0.1 ra:127.0.0.1

Loading…
Cancel
Save