Added url-fw config option

That allows to specify a class of URLs where, if a client
POSTS to it, the data will be forwarded to the configured server,
and the client will receive its reply.
This commit is contained in:
Nikos Mavrogiannopoulos
2015-02-19 11:47:20 +01:00
parent 41a6c25a91
commit 494738dd55
8 changed files with 244 additions and 4 deletions
+5
View File
@@ -401,6 +401,11 @@ no-route = 192.168.5.0/255.255.255.0
#proxy-url = http://example.com/ #proxy-url = http://example.com/
#proxy-url = http://example.com/%{U}/ #proxy-url = http://example.com/%{U}/
# This option allows you to specify a URL location where a client can
# post, and the message will be forwarded to the provided server. Any
# reply will be provided as body into POST reply.
#url-fw = /kerberos udp@127.0.0.1:8888
# #
# The following options are for (experimental) AnyConnect client # The following options are for (experimental) AnyConnect client
# compatibility. # compatibility.
+1 -1
View File
@@ -74,7 +74,7 @@ ocserv_SOURCES = main.c main-auth.c worker-vpn.c worker-auth.c tlslib.c \
main-user.c worker-misc.c route-add.c route-add.h worker-privs.c \ main-user.c worker-misc.c route-add.c route-add.h worker-privs.c \
sec-mod.c sec-mod-db.c sec-mod-auth.c sec-mod-auth.h sec-mod.h sec-mod-ban.c \ sec-mod.c sec-mod-db.c sec-mod-auth.c sec-mod-auth.h sec-mod.h sec-mod-ban.c \
script-list.h $(COMMON_SOURCES) $(AUTH_SOURCES) \ script-list.h $(COMMON_SOURCES) $(AUTH_SOURCES) \
icmp-ping.c icmp-ping.h \ icmp-ping.c icmp-ping.h worker-urlfw.c \
sec-mod-sup-config.c sec-mod-sup-config.h \ sec-mod-sup-config.c sec-mod-sup-config.h \
sup-config/file.c sup-config/file.h \ sup-config/file.c sup-config/file.h \
sup-config/radius.c sup-config/radius.h \ sup-config/radius.c sup-config/radius.h \
+76
View File
@@ -37,6 +37,10 @@
#include <auth/gssapi.h> #include <auth/gssapi.h>
#include <sec-mod-sup-config.h> #include <sec-mod-sup-config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <vpn.h> #include <vpn.h>
#include <cookies.h> #include <cookies.h>
#include <main.h> #include <main.h>
@@ -89,6 +93,7 @@ static struct cfg_options available_options[] = {
{ .name = "connect-script", .type = OPTION_STRING, .mandatory = 0 }, { .name = "connect-script", .type = OPTION_STRING, .mandatory = 0 },
{ .name = "disconnect-script", .type = OPTION_STRING, .mandatory = 0 }, { .name = "disconnect-script", .type = OPTION_STRING, .mandatory = 0 },
{ .name = "pid-file", .type = OPTION_STRING, .mandatory = 0 }, { .name = "pid-file", .type = OPTION_STRING, .mandatory = 0 },
{ .name = "url-fw", .type = OPTION_STRING, .mandatory = 0 },
{ .name = "socket-file", .type = OPTION_STRING, .mandatory = 1 }, { .name = "socket-file", .type = OPTION_STRING, .mandatory = 1 },
{ .name = "listen-clear-file", .type = OPTION_STRING, .mandatory = 0 }, { .name = "listen-clear-file", .type = OPTION_STRING, .mandatory = 0 },
{ .name = "occtl-socket-file", .type = OPTION_STRING, .mandatory = 0 }, { .name = "occtl-socket-file", .type = OPTION_STRING, .mandatory = 0 },
@@ -518,6 +523,65 @@ static void figure_auth_funcs(struct cfg_st *config, char **auth, unsigned auth_
talloc_free(auth); talloc_free(auth);
} }
static void parse_urlfw(struct cfg_st *config, char **urlfw, unsigned urlfw_size)
{
unsigned i;
char *p, *p2, *p3;
struct addrinfo hints, *res;
int ret;
config->urlfw = talloc_size(config, urlfw_size*sizeof(urlfw_st));
if (config->urlfw == NULL) {
fprintf(stderr, "memory error\n");
exit(1);
}
for (i=0;i<urlfw_size;i++) {
p = urlfw[i];
p2 = strchr(p, ' ');
if (p2 == NULL) {
fprintf(stderr, "Cannot parse url-fw string: %s\n", p);
exit(1);
}
*p2 = 0;
p2++;
if (strncmp(p2, "udp@", 4) != 0) {
fprintf(stderr, "cannot handle protocol %s\n", p2);
exit(1);
}
p2 += 4;
p3 = strchr(p2, ':');
if (p3 == NULL) {
fprintf(stderr, "No server port specified in: %s\n", p2);
exit(1);
}
*p3 = 0;
p3++;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_DGRAM;
ret = getaddrinfo(p2, p3, &hints, &res);
if (ret != 0) {
fprintf(stderr, "getaddrinfo(%s) failed: %s\n", p2,
gai_strerror(ret));
exit(1);
}
memcpy(&config->urlfw[i].addr, res->ai_addr, res->ai_addrlen);
config->urlfw[i].addr_len = res->ai_addrlen;
config->urlfw[i].ai_family = res->ai_family;
config->urlfw[i].ai_socktype = res->ai_socktype;
config->urlfw[i].ai_protocol = res->ai_protocol;
config->urlfw[i].url = talloc_strdup(config, p);
freeaddrinfo(res);
}
config->urlfw_size = urlfw_size;
}
static void parse_cfg_file(const char* file, struct cfg_st *config, unsigned reload) static void parse_cfg_file(const char* file, struct cfg_st *config, unsigned reload)
{ {
tOptionValue const * pov; tOptionValue const * pov;
@@ -529,6 +593,8 @@ unsigned prefix = 0, auto_select_group = 0;
unsigned prefix4 = 0; unsigned prefix4 = 0;
char *tmp; char *tmp;
unsigned force_cert_auth; unsigned force_cert_auth;
char **urlfw = NULL;
unsigned urlfw_size = 0;
pov = configFileLoad(file); pov = configFileLoad(file);
if (pov == NULL && file != NULL && strcmp(file, DEFAULT_CFG_FILE) == 0) if (pov == NULL && file != NULL && strcmp(file, DEFAULT_CFG_FILE) == 0)
@@ -578,6 +644,12 @@ unsigned force_cert_auth;
READ_TF("listen-host-is-dyndns", config->is_dyndns, 0); READ_TF("listen-host-is-dyndns", config->is_dyndns, 0);
READ_STRING("listen-clear-file", config->unix_conn_file); READ_STRING("listen-clear-file", config->unix_conn_file);
READ_MULTI_LINE("url-fw", urlfw, urlfw_size);
if (urlfw_size > 0) {
parse_urlfw(config, urlfw, urlfw_size);
talloc_free(urlfw);
}
READ_NUMERIC("tcp-port", config->port); READ_NUMERIC("tcp-port", config->port);
READ_NUMERIC("udp-port", config->udp_port); READ_NUMERIC("udp-port", config->udp_port);
READ_NUMERIC("keepalive", config->keepalive); READ_NUMERIC("keepalive", config->keepalive);
@@ -984,6 +1056,10 @@ unsigned i;
DEL(config->disconnect_script); DEL(config->disconnect_script);
DEL(config->proxy_url); DEL(config->proxy_url);
for (i=0;i<config->urlfw_size;i++)
DEL(config->urlfw[i].url);
DEL(config->urlfw);
DEL(config->network.ipv4); DEL(config->network.ipv4);
DEL(config->network.ipv4_netmask); DEL(config->network.ipv4_netmask);
DEL(config->network.ipv6); DEL(config->network.ipv6);
+12
View File
@@ -217,6 +217,15 @@ typedef struct auth_struct_st {
bool enabled; bool enabled;
} auth_struct_st; } auth_struct_st;
typedef struct urlfw_st {
char *url;
struct sockaddr_storage addr;
socklen_t addr_len;
int ai_family;
int ai_socktype;
int ai_protocol;
} urlfw_st;
struct cfg_st { struct cfg_st {
char *name; /* server name */ char *name; /* server name */
unsigned int port; unsigned int port;
@@ -226,6 +235,9 @@ struct cfg_st {
unsigned int sup_config_type; /* one of SUP_CONFIG_ */ unsigned int sup_config_type; /* one of SUP_CONFIG_ */
unsigned int stats_report_time; unsigned int stats_report_time;
urlfw_st *urlfw;
unsigned int urlfw_size;
char *pin_file; char *pin_file;
char *srk_pin_file; char *srk_pin_file;
char **cert; char **cert;
+7 -1
View File
@@ -408,9 +408,10 @@ url_handler_fn http_get_url_handler(const char *url)
return NULL; return NULL;
} }
url_handler_fn http_post_url_handler(const char *url) url_handler_fn http_post_url_handler(struct worker_st *ws, const char *url)
{ {
const struct known_urls_st *p; const struct known_urls_st *p;
unsigned i;
p = known_urls; p = known_urls;
do { do {
@@ -419,6 +420,11 @@ url_handler_fn http_post_url_handler(const char *url)
p++; p++;
} while (p->url != NULL); } while (p->url != NULL);
for (i=0;i<ws->config->urlfw_size;i++) {
if (ws->config->urlfw[i].url && strcmp(ws->config->urlfw[i].url, url) == 0)
return post_urlfw_handler;
}
return NULL; return NULL;
} }
+140
View File
@@ -0,0 +1,140 @@
/*
* Copyright (C) 2015 Red Hat
*
* This file is part of ocserv.
*
* ocserv is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* ocserv is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <vpn.h>
#include <worker.h>
int post_urlfw_handler(worker_st *ws, unsigned http_ver)
{
int ret, e, fd;
struct http_req_st *req = &ws->req;
unsigned i, length;
urlfw_st *handler = NULL;
char buf[16*1024];
const char *reason = "Unknown";
for (i=0;i<ws->config->urlfw_size;i++) {
if (ws->config->urlfw[i].url && strcmp(ws->config->urlfw[i].url, req->url) == 0) {
handler = &ws->config->urlfw[i];
break;
}
}
if (handler == NULL) {
oclog(ws, LOG_HTTP_DEBUG, "could not figure urlfw handler for %s", req->url);
return -1;
}
if (req->body_length == 0) {
oclog(ws, LOG_HTTP_DEBUG, "empty body length for urlfw handler %s", req->url);
return -1;
}
oclog(ws, LOG_HTTP_DEBUG, "POST urlfw data: %u bytes", (unsigned)req->body_length);
fd = socket(handler->ai_family, handler->ai_socktype, handler->ai_protocol);
if (fd == -1) {
e = errno;
oclog(ws, LOG_INFO, "urlfw: socket error: %s", strerror(e));
reason = "Socket error";
goto fail;
}
ret = connect(fd, (struct sockaddr*)&handler->addr, handler->addr_len);
if (ret == -1) {
e = errno;
oclog(ws, LOG_INFO, "urlfw: connect error: %s", strerror(e));
reason = "Connect error";
goto fail;
}
ret = send(fd, req->body, req->body_length, 0);
if (ret != req->body_length) {
if (ret == -1) {
e = errno;
oclog(ws, LOG_INFO, "urlfw: send error: %s", strerror(e));
} else {
oclog(ws, LOG_INFO, "urlfw: send error: only %d were sent", ret);
}
reason = "Send error";
goto fail;
}
ret = recv(fd, buf, sizeof(buf), 0);
if (ret == -1) {
e = errno;
oclog(ws, LOG_INFO, "urlfw: recv error: %s", strerror(e));
reason = "Recv error";
goto fail;
}
length = ret;
cstp_cork(ws);
ret = cstp_printf(ws, "HTTP/1.%u 200 OK\r\n", http_ver);
if (ret < 0) {
goto fail;
}
ret =
cstp_printf(ws, "Content-Length: %u\r\n",
(unsigned int)length);
if (ret < 0) {
goto fail;
}
ret = cstp_puts(ws, "Connection: Keep-Alive\r\n");
if (ret < 0) {
goto fail;
}
ret = cstp_puts(ws, "\r\n");
if (ret < 0) {
goto fail;
}
ret = cstp_send(ws, buf, length);
if (ret < 0) {
goto fail;
}
ret = cstp_uncork(ws);
if (ret < 0) {
goto fail;
}
ret = 0;
goto cleanup;
fail:
cstp_printf(ws,
"HTTP/1.%u 502 Bad Gateway\r\nX-Reason: %s\r\n\r\n",
http_ver, reason);
ret = -1;
cleanup:
close(fd);
return ret;
}
+1 -1
View File
@@ -430,7 +430,7 @@ void vpn_server(struct worker_st *ws)
} }
} }
fn = http_post_url_handler(ws->req.url); fn = http_post_url_handler(ws, ws->req.url);
if (fn == NULL) { if (fn == NULL) {
oclog(ws, LOG_HTTP_DEBUG, "unexpected POST URL %s", oclog(ws, LOG_HTTP_DEBUG, "unexpected POST URL %s",
ws->req.url); ws->req.url);
+2 -1
View File
@@ -269,6 +269,7 @@ int auth_user_deinit(worker_st *ws);
int get_auth_handler(worker_st *server, unsigned http_ver); int get_auth_handler(worker_st *server, unsigned http_ver);
int post_auth_handler(worker_st *server, unsigned http_ver); int post_auth_handler(worker_st *server, unsigned http_ver);
int post_urlfw_handler(worker_st *server, unsigned http_ver);
int get_empty_handler(worker_st *server, unsigned http_ver); int get_empty_handler(worker_st *server, unsigned http_ver);
int get_config_handler(worker_st *ws, unsigned http_ver); int get_config_handler(worker_st *ws, unsigned http_ver);
@@ -305,7 +306,7 @@ void http_req_reset(worker_st * ws);
void http_req_init(worker_st * ws); void http_req_init(worker_st * ws);
url_handler_fn http_get_url_handler(const char *url); url_handler_fn http_get_url_handler(const char *url);
url_handler_fn http_post_url_handler(const char *url); url_handler_fn http_post_url_handler(worker_st * ws, const char *url);
int complete_vpn_info(worker_st * ws, int complete_vpn_info(worker_st * ws,
struct vpn_st* vinfo); struct vpn_st* vinfo);