From 494738dd55cba683c14ed85ca2b9f8848298f4d0 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Thu, 12 Feb 2015 14:14:13 +0100 Subject: [PATCH] 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. --- doc/sample.config | 5 ++ src/Makefile.am | 2 +- src/config.c | 76 ++++++++++++++++++++++++ src/vpn.h | 12 ++++ src/worker-http.c | 8 ++- src/worker-urlfw.c | 140 +++++++++++++++++++++++++++++++++++++++++++++ src/worker-vpn.c | 2 +- src/worker.h | 3 +- 8 files changed, 244 insertions(+), 4 deletions(-) create mode 100644 src/worker-urlfw.c diff --git a/doc/sample.config b/doc/sample.config index 7f326924..df9fdf1a 100644 --- a/doc/sample.config +++ b/doc/sample.config @@ -401,6 +401,11 @@ no-route = 192.168.5.0/255.255.255.0 #proxy-url = http://example.com/ #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 # compatibility. diff --git a/src/Makefile.am b/src/Makefile.am index cf55afdd..6c868734 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ 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) \ - 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 \ sup-config/file.c sup-config/file.h \ sup-config/radius.c sup-config/radius.h \ diff --git a/src/config.c b/src/config.c index 723da54c..9581e016 100644 --- a/src/config.c +++ b/src/config.c @@ -37,6 +37,10 @@ #include #include +#include +#include +#include + #include #include #include @@ -89,6 +93,7 @@ static struct cfg_options available_options[] = { { .name = "connect-script", .type = OPTION_STRING, .mandatory = 0 }, { .name = "disconnect-script", .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 = "listen-clear-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); } +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;iurlfw[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) { tOptionValue const * pov; @@ -529,6 +593,8 @@ unsigned prefix = 0, auto_select_group = 0; unsigned prefix4 = 0; char *tmp; unsigned force_cert_auth; +char **urlfw = NULL; +unsigned urlfw_size = 0; pov = configFileLoad(file); 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_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("udp-port", config->udp_port); READ_NUMERIC("keepalive", config->keepalive); @@ -984,6 +1056,10 @@ unsigned i; DEL(config->disconnect_script); DEL(config->proxy_url); + for (i=0;iurlfw_size;i++) + DEL(config->urlfw[i].url); + DEL(config->urlfw); + DEL(config->network.ipv4); DEL(config->network.ipv4_netmask); DEL(config->network.ipv6); diff --git a/src/vpn.h b/src/vpn.h index a6a8c349..7a85f9cc 100644 --- a/src/vpn.h +++ b/src/vpn.h @@ -217,6 +217,15 @@ typedef struct auth_struct_st { bool enabled; } 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 { char *name; /* server name */ unsigned int port; @@ -226,6 +235,9 @@ struct cfg_st { unsigned int sup_config_type; /* one of SUP_CONFIG_ */ unsigned int stats_report_time; + urlfw_st *urlfw; + unsigned int urlfw_size; + char *pin_file; char *srk_pin_file; char **cert; diff --git a/src/worker-http.c b/src/worker-http.c index bf501475..4cf4fb4c 100644 --- a/src/worker-http.c +++ b/src/worker-http.c @@ -408,9 +408,10 @@ url_handler_fn http_get_url_handler(const char *url) 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; + unsigned i; p = known_urls; do { @@ -419,6 +420,11 @@ url_handler_fn http_post_url_handler(const char *url) p++; } while (p->url != NULL); + for (i=0;iconfig->urlfw_size;i++) { + if (ws->config->urlfw[i].url && strcmp(ws->config->urlfw[i].url, url) == 0) + return post_urlfw_handler; + } + return NULL; } diff --git a/src/worker-urlfw.c b/src/worker-urlfw.c new file mode 100644 index 00000000..ccfadbd0 --- /dev/null +++ b/src/worker-urlfw.c @@ -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 . + */ + +#include + +#include +#include +#include +#include + +#include +#include + +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;iconfig->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; + +} diff --git a/src/worker-vpn.c b/src/worker-vpn.c index d487a8b1..b4ac16ee 100644 --- a/src/worker-vpn.c +++ b/src/worker-vpn.c @@ -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) { oclog(ws, LOG_HTTP_DEBUG, "unexpected POST URL %s", ws->req.url); diff --git a/src/worker.h b/src/worker.h index df8a657c..41190d8c 100644 --- a/src/worker.h +++ b/src/worker.h @@ -269,6 +269,7 @@ int auth_user_deinit(worker_st *ws); int get_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_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); 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, struct vpn_st* vinfo);