summaryrefslogtreecommitdiff
path: root/accel-pppd
diff options
context:
space:
mode:
authorVladislav Grishenko <themiron@mail.ru>2018-02-27 16:48:59 +0500
committerVladislav Grishenko <themiron@mail.ru>2018-02-27 18:14:57 +0500
commitc2fe175d1b98ae3cc728d9254568201d45e2aeca (patch)
tree98611bb607d2b58add79cfe2c19d0a3ba1214cd3 /accel-pppd
parent8b24e82a67005ade50b92dfefa6413874a758132 (diff)
downloadaccel-ppp-c2fe175d1b98ae3cc728d9254568201d45e2aeca.tar.gz
accel-ppp-c2fe175d1b98ae3cc728d9254568201d45e2aeca.zip
sstp: implement proxy-protocol 1 & 2 support
Diffstat (limited to 'accel-pppd')
-rw-r--r--accel-pppd/accel-ppp.conf2
-rw-r--r--accel-pppd/accel-ppp.conf.55
-rw-r--r--accel-pppd/ctrl/sstp/proxy_prot.h74
-rw-r--r--accel-pppd/ctrl/sstp/sstp.c247
4 files changed, 313 insertions, 15 deletions
diff --git a/accel-pppd/accel-ppp.conf b/accel-pppd/accel-ppp.conf
index d2e7a26d..607609ac 100644
--- a/accel-pppd/accel-ppp.conf
+++ b/accel-pppd/accel-ppp.conf
@@ -113,7 +113,7 @@ verbose=1
#cert-hash-proto=sha1,sha256
#cert-hash-sha1=
#cert-hash-sha256=
-#accept=ssl
+#accept=ssl,proxy
#ssl-ciphers=DEFAULT
#ssl-prefer-server-ciphers=0
#ssl-ca-file=/etc/ssl/sstp-ca.crt
diff --git a/accel-pppd/accel-ppp.conf.5 b/accel-pppd/accel-ppp.conf.5
index fb38fea1..97e7ed5b 100644
--- a/accel-pppd/accel-ppp.conf.5
+++ b/accel-pppd/accel-ppp.conf.5
@@ -662,11 +662,14 @@ If this option is given and greater then zero then sstp will send echo-request e
seconds and drop connection without a reply.
Default is 60.
.TP
-.BI "accept=" ssl
+.BI "accept=" ssl,proxy
Specifies incoming connection acceptance mode.
.br
.B ssl
- enable SSL/TLS support.
+.br
+.B proxy
+- enable PROXY protocol 1 & 2 support.
.TP
.BI "ssl-ciphers=" string
Specifies the enabled ciphers. The ciphers are specified in the format understood by the OpenSSL library.
diff --git a/accel-pppd/ctrl/sstp/proxy_prot.h b/accel-pppd/ctrl/sstp/proxy_prot.h
new file mode 100644
index 00000000..bada8aa3
--- /dev/null
+++ b/accel-pppd/ctrl/sstp/proxy_prot.h
@@ -0,0 +1,74 @@
+#ifndef PROXY_PROT_H
+#define PROXY_PROT_H
+
+#include <stdint.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#define PROXY_SIG { 'P', 'R', 'O', 'X', 'Y' }
+#define PROXY_MINLEN 8
+#define PROXY_TCP4 "TCP4"
+#define PROXY_TCP6 "TCP6"
+#define PROXY_UNKNOWN "UNKNOWN"
+
+#define PROXY2_SIG { 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a }
+#define PROXY2_MINLEN 16
+#define PROXY2_LOCAL 0
+#define PROXY2_PROXY 1
+#define PROXY2_AF_UNSPEC 0
+#define PROXY2_AF_INET 1
+#define PROXY2_AF_INET6 2
+#define PROXY2_AF_UNIX 3
+#define PROXY2_UNSPEC 0
+#define PROXY2_STREAM 1
+#define PROXY2_DGRAM 2
+
+struct proxy_hdr {
+ char line[108];
+} __attribute__((packed));
+
+struct proxy2_ipv4 {
+ struct in_addr src_addr;
+ struct in_addr dst_addr;
+ uint16_t src_port;
+ uint16_t dst_port;
+} __attribute__((packed));
+
+struct proxy2_ipv6 {
+ struct in6_addr src_addr;
+ struct in6_addr dst_addr;
+ uint16_t src_port;
+ uint16_t dst_port;
+} __attribute__((packed));
+
+struct proxy2_unix {
+ char src_addr[108];
+ char dst_addr[108];
+} __attribute__((packed));
+
+union proxy2_addr {
+ struct proxy2_ipv4 ipv4_addr;
+ struct proxy2_ipv6 ipv6_addr;
+ struct proxy2_unix unix_addr;
+};
+
+struct proxy2_hdr {
+ uint8_t sig[12]; /* hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */
+ uint8_t ver_cmd; /* protocol version and command */
+ uint8_t fam; /* protocol family and address */
+ uint16_t len; /* number of following bytes part of the header */
+ union proxy2_addr __addr[0];
+#define ipv4_addr __addr[0].ipv4_addr
+#define ipv6_addr __addr[0].ipv6_addr
+#define unix_addr __addr[0].unix_addr
+} __attribute__((packed));
+
+struct BUG_bad_sizeof_proxy_hdr {
+ uint8_t proxy_hdr[sizeof(struct proxy_hdr) < PROXY_MINLEN ? -1 : 0 ];
+};
+
+struct BUG_bad_sizeof_proxy2_hdr {
+ uint8_t proxy2_hdr[sizeof(struct proxy2_hdr) != PROXY2_MINLEN ? -1 : 0 ];
+};
+
+#endif
diff --git a/accel-pppd/ctrl/sstp/sstp.c b/accel-pppd/ctrl/sstp/sstp.c
index 11d57bae..5eabbfc1 100644
--- a/accel-pppd/ctrl/sstp/sstp.c
+++ b/accel-pppd/ctrl/sstp/sstp.c
@@ -36,11 +36,15 @@
#include "memdebug.h"
+#include "proxy_prot.h"
#include "sstp_prot.h"
#ifndef min
#define min(x,y) ((x) < (y) ? (x) : (y))
#endif
+#ifndef max
+#define max(x,y) ((x) > (y) ? (x) : (y))
+#endif
#define PPP_SYNC 0 /* buggy yet */
#define PPP_BUF_SIZE 8192
@@ -132,6 +136,7 @@ struct sstp_conn_t {
struct buffer_t *ppp_in;
struct list_head ppp_queue;
+ struct sockaddr_t addr;
struct ppp_t ppp;
struct ap_ctrl ctrl;
};
@@ -153,6 +158,7 @@ static int conf_verbose = 0;
static int conf_ppp_max_mtu = 1452;
static const char *conf_ip_pool;
static const char *conf_ifname;
+static int conf_proxyproto = 0;
static int conf_hash_protocol = CERT_HASH_PROTOCOL_SHA1 | CERT_HASH_PROTOCOL_SHA256;
static struct hash_t conf_hash_sha1 = { .len = 0 };
@@ -168,6 +174,7 @@ static int sstp_send(struct sstp_conn_t *conn, struct buffer_t *buf);
static int sstp_abort(struct sstp_conn_t *conn, int disconnect);
static void sstp_disconnect(struct sstp_conn_t *conn);
static int sstp_handler(struct sstp_conn_t *conn, struct buffer_t *buf);
+static int http_handler(struct sstp_conn_t *conn, struct buffer_t *buf);
/*
* FCS lookup table as calculated by genfcstab.
@@ -240,6 +247,27 @@ static int hex2bin(const char *src, uint8_t *dst, size_t size)
return n;
}
+#define vstrsep(buf, sep, args...) _vstrsep(buf, sep, args, (void*)-1)
+static int _vstrsep(char *buf, const char *sep, ...)
+{
+ va_list ap;
+ char **arg, *val, *ptr;
+ int n = 0;
+
+ va_start(ap, sep);
+ while ((arg = va_arg(ap, char **)) != (void *)-1) {
+ val = strtok_r(buf, sep, &ptr);
+ buf = NULL;
+ if (!val)
+ break;
+ if (arg)
+ *arg = val;
+ n++;
+ }
+ va_end(ap);
+ return n;
+}
+
static in_addr_t sockaddr_ipv4(struct sockaddr_t *addr)
{
switch (addr->u.sa.sa_family) {
@@ -526,6 +554,194 @@ error:
}
#endif
+/* proxy */
+
+static int proxy_parse(struct buffer_t *buf, struct sockaddr_t *peer, struct sockaddr_t *addr)
+{
+ static const uint8_t proxy_sig[] = PROXY_SIG;
+ struct proxy_hdr *hdr;
+ char *ptr, *src_addr, *dst_addr, *src_port, *dst_port;
+ int n, count;
+
+ if (buf->len < PROXY_MINLEN || memcmp(buf->head, proxy_sig, sizeof(proxy_sig)) != 0)
+ return 0;
+
+ ptr = memmem(buf->head, buf->len, "\r\n", 2);
+ if (!ptr) {
+ if (buf_tailroom(buf) > 0)
+ return 0;
+ log_error("sstp: proxy: %s\n", "too long header");
+ return -1;
+ } else
+ *ptr = '\0';
+
+ hdr = (void *)buf->head;
+ n = ptr + 2 - hdr->line;
+
+ if (conf_verbose)
+ log_ppp_info2("recv [PROXY <%s>]\n", hdr->line);
+
+ count = vstrsep(hdr->line, " ", NULL, &ptr, &src_addr, &dst_addr, &src_port, &dst_port);
+ if (count < 2)
+ goto error;
+
+ if (strcasecmp(ptr, PROXY_TCP4) == 0) {
+ if (count < 6 ||
+ inet_pton(AF_INET, src_addr, &peer->u.sin.sin_addr) <= 0 ||
+ inet_pton(AF_INET, dst_addr, &addr->u.sin.sin_addr) <= 0) {
+ goto error;
+ }
+ peer->len = addr->len = sizeof(addr->u.sin);
+ peer->u.sin.sin_family = addr->u.sin.sin_family = AF_INET;
+ peer->u.sin.sin_port = htons(atoi(src_port));
+ addr->u.sin.sin_port = htons(atoi(dst_port));
+ } else if (strcasecmp(ptr, PROXY_TCP6) == 0) {
+ if (count < 6 ||
+ inet_pton(AF_INET6, src_addr, &peer->u.sin6.sin6_addr) <= 0 ||
+ inet_pton(AF_INET6, dst_addr, &addr->u.sin6.sin6_addr) <= 0) {
+ goto error;
+ }
+ peer->len = addr->len = sizeof(addr->u.sin6);
+ peer->u.sin6.sin6_family = addr->u.sin6.sin6_family = AF_INET;
+ peer->u.sin6.sin6_port = htons(atoi(src_port));
+ addr->u.sin6.sin6_port = htons(atoi(dst_port));
+ } else if (strcasecmp(ptr, PROXY_UNKNOWN) != 0)
+ goto error;
+
+ return n;
+
+error:
+ log_error("sstp: proxy: %s\n", "invalid header");
+ return -1;
+}
+
+static int proxy_parse_v2(struct buffer_t *buf, struct sockaddr_t *peer, struct sockaddr_t *addr)
+{
+ static const uint8_t proxy2_sig[] = PROXY2_SIG;
+ struct proxy2_hdr *hdr;
+ int n;
+
+ if (buf->len < PROXY2_MINLEN || memcmp(buf->head, proxy2_sig, sizeof(proxy2_sig)) != 0)
+ return 0;
+
+ hdr = (void *)buf->head;
+
+ if (conf_verbose) {
+ log_ppp_info2("recv [PROXY ver/cmd=0x%02x fam/addr=0x%02x len=%d]\n",
+ hdr->ver_cmd, hdr->fam, ntohs(hdr->len));
+ }
+
+ if ((hdr->ver_cmd & 0xf0) != 0x20)
+ goto error;
+
+ n = sizeof(*hdr) + ntohs(hdr->len);
+ if (n > buf->len) {
+ if (buf_tailroom(buf) > 0)
+ return 0;
+ log_error("sstp: proxy2: %s\n", "too long header");
+ return -1;
+ }
+
+ switch (hdr->ver_cmd & 0x0f) {
+ case PROXY2_PROXY:
+ switch (hdr->fam >> 4) {
+ case PROXY2_AF_INET:
+ if (n < sizeof(hdr) + sizeof(hdr->ipv4_addr))
+ goto error;
+ peer->len = addr->len = sizeof(addr->u.sin);
+ peer->u.sin.sin_family = addr->u.sin.sin_family = AF_INET;
+ peer->u.sin.sin_addr.s_addr = hdr->ipv4_addr.src_addr.s_addr;
+ peer->u.sin.sin_port = hdr->ipv4_addr.src_port;
+ addr->u.sin.sin_addr.s_addr = hdr->ipv4_addr.dst_addr.s_addr;
+ addr->u.sin.sin_port = hdr->ipv4_addr.dst_port;
+ break;
+ case PROXY2_AF_INET6:
+ if (n < sizeof(hdr) + sizeof(hdr->ipv6_addr))
+ goto error;
+ peer->len = addr->len = sizeof(addr->u.sin6);
+ peer->u.sin6.sin6_family = addr->u.sin6.sin6_family = AF_INET6;
+ memcpy(&peer->u.sin6.sin6_addr, &hdr->ipv6_addr.src_addr, sizeof(peer->u.sin6.sin6_addr));
+ peer->u.sin6.sin6_port = hdr->ipv6_addr.src_port;
+ memcpy(&addr->u.sin6.sin6_addr, &hdr->ipv6_addr.dst_addr, sizeof(addr->u.sin6.sin6_addr));
+ addr->u.sin6.sin6_port = hdr->ipv6_addr.dst_port;
+ break;
+ case PROXY2_AF_UNIX:
+ if (n < sizeof(hdr) + sizeof(hdr->unix_addr))
+ goto error;
+ peer->len = addr->len = sizeof(addr->u.sun);
+ peer->u.sun.sun_family = addr->u.sun.sun_family = AF_UNIX;
+ memcpy(peer->u.sun.sun_path, hdr->unix_addr.src_addr, sizeof(peer->u.sun.sun_path));
+ memcpy(addr->u.sun.sun_path, hdr->unix_addr.dst_addr, sizeof(addr->u.sun.sun_path));
+ break;
+ case PROXY2_AF_UNSPEC:
+ break;
+ default:
+ goto error;
+ }
+ /* fall through */
+ case PROXY2_LOCAL:
+ break;
+ default:
+ goto error;
+ }
+
+ return n;
+
+error:
+ log_error("sstp: proxy2: %s\n", "invalid header");
+ return -1;
+}
+
+static int proxy_handler(struct sstp_conn_t *conn, struct buffer_t *buf)
+{
+ struct sockaddr_t addr;
+ char addr_buf[ADDRSTR_MAXLEN];
+ in_addr_t ip;
+ int n;
+
+ if (conn->sstp_state != STATE_SERVER_CALL_DISCONNECTED)
+ return -1;
+
+ memset(&addr, 0, sizeof(addr));
+
+ n = proxy_parse_v2(buf, &conn->addr, &addr);
+ if (n == 0)
+ n = proxy_parse(buf, &conn->addr, &addr);
+
+ if (n == 0 && buf->len >= max(PROXY2_MINLEN, PROXY_MINLEN)) {
+ log_error("sstp: proxy: %s\n", "no header found");
+ return -1;
+ } else if (n <= 0)
+ return n;
+
+ ip = sockaddr_ipv4(&conn->addr);
+ if (ip && triton_module_loaded("connlimit") && connlimit_check(cl_key_from_ipv4(ip)))
+ return -1;
+
+ sockaddr_ntop(&conn->addr, addr_buf, sizeof(addr_buf));
+ log_info2("sstp: proxy: connection from %s\n", addr_buf);
+
+ if (ip && iprange_client_check(ip)) {
+ log_warn("sstp: proxy: IP is out of client-ip-range, droping connection...\n");
+ return -1;
+ }
+
+ if (addr.u.sa.sa_family != AF_UNSPEC) {
+ _free(conn->ctrl.calling_station_id);
+ conn->ctrl.calling_station_id = _strdup(addr_buf);
+ conn->ppp.ses.chan_name = conn->ctrl.calling_station_id;
+
+ sockaddr_ntop(&addr, addr_buf, sizeof(addr_buf));
+ _free(conn->ctrl.called_station_id);
+ conn->ctrl.called_station_id = _strdup(addr_buf);
+ }
+
+ buf_pull(buf, n);
+
+ conn->handler = http_handler;
+ return buf->len;
+}
+
/* http */
static char *http_getline(struct buffer_t *buf, char *line, size_t size)
@@ -619,12 +835,9 @@ static int http_recv_request(struct sstp_conn_t *conn, uint8_t *data, int len)
if (conf_verbose)
log_ppp_info2("recv [HTTP <%s>]\n", line);
- method = strsep(&line, " ");
- request = strsep(&line, " ");
- proto = strsep(&line, " ");
host = NULL;
- if (!method || !request || !proto) {
+ if (vstrsep(line, " ", &method, &request, &proto) < 3) {
http_send_response(conn, "HTTP/1.1", "400 Bad Request", NULL);
goto error;
}
@@ -703,7 +916,7 @@ static int http_handler(struct sstp_conn_t *conn, struct buffer_t *buf)
conn->sstp_state = STATE_SERVER_CONNECT_REQUEST_PENDING;
conn->handler = sstp_handler;
- return 0;
+ return buf->len;
}
/* ppp */
@@ -1574,9 +1787,11 @@ static int sstp_read(struct triton_md_handler_t *h)
}
buf_put(buf, n);
- n = conn->handler(conn, buf);
- if (n < 0)
- goto drop;
+ do {
+ n = conn->handler(conn, buf);
+ if (n < 0)
+ goto drop;
+ } while (n > 0);
buf_expand_tail(buf, SSTP_MAX_PACKET_SIZE);
}
@@ -1823,7 +2038,7 @@ static int sstp_connect(struct triton_md_handler_t *h)
continue;
}
- ip = sockaddr_ipv4(&addr);
+ ip = conf_proxyproto ? INADDR_ANY : sockaddr_ipv4(&addr);
if (ip && triton_module_loaded("connlimit") && connlimit_check(cl_key_from_ipv4(ip))) {
close(sock);
return 0;
@@ -1877,7 +2092,7 @@ static int sstp_connect(struct triton_md_handler_t *h)
conn->sstp_state = STATE_SERVER_CALL_DISCONNECTED;
conn->ppp_state = STATE_INIT;
- conn->handler = http_handler;
+ conn->handler = conf_proxyproto ? proxy_handler : http_handler;
//conn->bypass_auth = conf_bypass_auth;
//conn->http_cookie = NULL:
@@ -1887,6 +2102,7 @@ static int sstp_connect(struct triton_md_handler_t *h)
conn->in = alloc_buf(SSTP_MAX_PACKET_SIZE*2);
INIT_LIST_HEAD(&conn->out_queue);
INIT_LIST_HEAD(&conn->ppp_queue);
+ memcpy(&conn->addr, &addr, sizeof(conn->addr));
conn->ctrl.ctx = &conn->ctx;
conn->ctrl.started = ppp_started;
@@ -2090,14 +2306,19 @@ static void load_config(void)
conf_hash_protocol |= CERT_HASH_PROTOCOL_SHA256;
}
+ opt = conf_get_opt("sstp", "accept");
+ conf_proxyproto = strhas(opt, "proxy", ',');
+
#ifdef CRYPTO_OPENSSL
ssl_load_config(&serv, conf_hostname);
opt = serv.ssl_ctx ? "enabled" : "disabled";
#else
opt = "not available";
#endif
- if (conf_verbose)
- log_info2("sstp: SSL support %s\n", opt);
+ if (conf_verbose) {
+ log_info2("sstp: SSL/TLS support %s, PROXY support %s\n",
+ opt, conf_proxyproto ? "enabled" : "disabled");
+ }
opt = conf_get_opt("sstp", "cert-hash-sha1");
if (opt) {
@@ -2126,7 +2347,7 @@ static void load_config(void)
conf_ip_pool = conf_get_opt("sstp", "ip-pool");
conf_ifname = conf_get_opt("sstp", "ifname");
- ipmode = (serv.addr.u.sa.sa_family == AF_INET) ?
+ ipmode = (serv.addr.u.sa.sa_family == AF_INET && !conf_proxyproto) ?
iprange_check_activation() : -1;
switch (ipmode) {
case IPRANGE_DISABLED: