]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
start of proxy_protocol support
authorAlan T. DeKok <aland@freeradius.org>
Tue, 27 Jul 2021 12:00:49 +0000 (08:00 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Tue, 27 Jul 2021 12:37:16 +0000 (08:37 -0400)
it doesn't yet parse the full "PROXY ..." string, but the basics
are there

src/include/listen.h
src/main/listen.c
src/main/tls_listen.c

index 47e6caae7dda68f7dcf2d6767ef5fe0e572cf1df..cc52d2c04f7e320ef302897b56be36368954b241 100644 (file)
@@ -70,9 +70,11 @@ struct rad_listen {
        int             status;
 #ifdef WITH_TCP
        int             count;
-       bool            dual;
        rbtree_t        *children;
        rad_listen_t    *parent;
+
+       bool            dual;
+       bool            proxy_protocol;         //!< haproxy protocol
 #endif
        bool            nodup;
        bool            synchronous;
@@ -165,6 +167,9 @@ typedef struct listen_socket_t {
        RADCLIENT       *client;
 
        RADIUS_PACKET   *packet; /* for reading partial packets */
+
+       fr_ipaddr_t     haproxy_src_ipaddr;     //!< for proxy_protocol
+       fr_ipaddr_t     haproxy_dst_ipaddr;
 #endif
 
 #ifdef WITH_TLS
index 49ab5aaf2dada975504013ae94e74f0aef1e5954..314e11677ce72d77cdeb0b8d081298a670325c0d 100644 (file)
@@ -1188,6 +1188,12 @@ int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
 
                } else if (strcmp(proto, "tcp") == 0) {
                        sock->proto = IPPROTO_TCP;
+
+                       /*
+                        *      Add support for http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
+                        */
+                       rcode = cf_item_parse(cs, "proxy_protocol", FR_ITEM_POINTER(PW_TYPE_BOOLEAN, &this->proxy_protocol), NULL);
+                       if (rcode < 0) return -1;
                } else {
                        cf_log_err_cs(cs,
                                   "Unknown proto name \"%s\"", proto);
@@ -1241,7 +1247,6 @@ int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
 
                        rcode = cf_item_parse(cs, "check_client_connections", FR_ITEM_POINTER(PW_TYPE_BOOLEAN, &this->check_client_connections), "no");
                        if (rcode < 0) return -1;
-
                }
 #else  /* WITH_TLS */
                /*
index dcea519bf30a529c2c78ec87870a2eae6a50a59b..d864ff2c7318fab2832fda3bbf2d85c1c5c18274 100644 (file)
@@ -119,6 +119,83 @@ static int CC_HINT(nonnull) tls_socket_write(rad_listen_t *listener, REQUEST *re
        return 1;
 }
 
+/*
+ *     Check for PROXY protocol.  Once that's done, clear
+ *     listener->proxy_protocol.
+ */
+static int proxy_protocol_check(rad_listen_t *listener, REQUEST *request)
+{
+       listen_socket_t *sock = listener->data;
+       uint8_t const *p, *end, *eol;
+
+       p = sock->ssn->dirty_in.data;
+
+       /*
+        *      CRLF MUST be within the first 107 bytes.
+        */
+       if (sock->ssn->dirty_in.used < 107) {
+               end = p + sock->ssn->dirty_in.used;
+       } else {
+               end = p + 107;
+       }
+       eol = NULL;
+
+       /*
+        *      Scan for CRLF.  Keep reading until it's found.
+        */
+       while ((p + 1) < end) {
+               if ((p[0] == 0x0d) && (p[1] == 0x0a)) {
+                       eol = p;
+                       break;
+               }
+
+               /*
+                *      Other control characters, or non-ASCII data.
+                *      That's a problem.
+                */
+               if ((*p <= ' ') || (*p >= 0x80)) {
+               invalid_data:
+                       DEBUG("Closing TLS PROXY socket from client port %u - received invalid data", sock->other_port);
+                       return -1;
+               }
+
+               p++;
+       }
+
+       /*
+        *      No CRLF, keep reading until we have it.
+        */
+       if (!eol) {
+               ssize_t rcode;
+
+               rcode = read(request->packet->sockfd,
+                            sock->ssn->dirty_in.data + sock->ssn->dirty_in.used,
+                            sizeof(sock->ssn->dirty_in.data) - sock->ssn->dirty_in.used);
+               if (rcode < 0) {
+                       if (errno == EINTR) return 0;
+                       DEBUG("Failed reading from TLS PROXY socket from client port %u - %s", sock->other_port, fr_syserror(errno));
+                       return -1;
+               }
+       }
+
+       p = sock->ssn->dirty_in.data;
+
+       /*
+        *      We MUST have an EOL now.  Let's see if it's in any way well formed.
+        */
+       if ((eol - p) < 14) goto invalid_data;
+       
+       /*
+        *      We only support TCP4 and TCP6.
+        */
+       if (memcmp(p, "PROXY TCP", 9) != 0) goto invalid_data;
+
+       /*
+        *      @todo - parse the rest of the string
+        */
+
+       return -1;              /* not done yet! */
+}
 
 static int tls_socket_recv(rad_listen_t *listener)
 {
@@ -187,6 +264,19 @@ static int tls_socket_recv(rad_listen_t *listener)
 
        request = sock->request;
 
+       /*
+        *      Bypass ALL of the TLS stuff until we've read the PROXY
+        *      header.
+        *
+        *      If the PROXY header checks pass, then the flag is
+        *      cleared, as we don't need it any more.
+        */
+       if (listener->proxy_protocol) {
+               rcode = proxy_protocol_check(listener, request);
+               if (rcode < 0) goto do_close;
+               if (rcode == 0) return 1;
+       }
+
        if (sock->state == LISTEN_TLS_SETUP) {
                RDEBUG3("Setting connection state to RUNNING");
                sock->state = LISTEN_TLS_RUNNING;