Skip to content

Commit 1be71bd

Browse files
authored
Merge pull request #14 from Castaglia/haproxy-v2-tlvs-issue12
Issue #12: Implement support for PROXY protocol v2 TLVs.
2 parents e526eb5 + 7e6eba0 commit 1be71bd

File tree

1 file changed

+239
-21
lines changed

1 file changed

+239
-21
lines changed

mod_proxy_protocol.c

Lines changed: 239 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ static int read_sock(int sockfd, void *buf, size_t reqlen) {
181181

182182
session.total_raw_in += reqlen;
183183

184-
if (res == remainlen) {
184+
if ((size_t) res == remainlen) {
185185
break;
186186
}
187187

@@ -616,6 +616,220 @@ static int read_haproxy_v1(pool *p, conn_t *conn,
616616

617617
static const char haproxy_v2_sig[12] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
618618

619+
/* The TLS TLV is convoluted enough to warrant its own special function. */
620+
static int read_haproxy_v2_tls_tlv(pool *p, void *tlv_val, size_t tlv_valsz) {
621+
uint8_t client;
622+
uint32_t verify;
623+
unsigned char *ptr;
624+
size_t len;
625+
626+
ptr = tlv_val;
627+
len = tlv_valsz;
628+
629+
memcpy(&client, ptr, sizeof(client));
630+
ptr += sizeof(client);
631+
len -= sizeof(client);
632+
633+
memcpy(&verify, ptr, sizeof(verify));
634+
ptr += sizeof(verify);
635+
len -= sizeof(verify);
636+
637+
if (client > 0) {
638+
/* CLIENT_CERT_CONN */
639+
if (client & 0x02) {
640+
pr_trace_msg(trace_channel, 19,
641+
"TLS TLV: client provided certificate over current connection");
642+
643+
/* CLIENT_CERT_SESS */
644+
} else if (client & 0x04) {
645+
pr_trace_msg(trace_channel, 19,
646+
"TLS TLV: client provided certificate over current TLS session");
647+
648+
} else {
649+
pr_trace_msg(trace_channel, 19, "TLS TLV: client connected using TLS");
650+
}
651+
652+
} else {
653+
pr_trace_msg(trace_channel, 19, "TLS TLV: client did connect using TLS");
654+
}
655+
656+
if (verify > 0) {
657+
pr_trace_msg(trace_channel, 19,
658+
"TLS TLV: client provided verified certificate");
659+
}
660+
661+
while (len > 0) {
662+
uint8_t tls_type;
663+
uint16_t tls_len;
664+
void *tls_val;
665+
size_t tls_valsz;
666+
667+
pr_signals_handle();
668+
669+
memcpy(&tls_type, ptr, sizeof(tls_type));
670+
ptr += sizeof(tls_type);
671+
len -= sizeof(tls_type);
672+
673+
memcpy(&tls_len, ptr, sizeof(tls_len));
674+
ptr += sizeof(tls_len);
675+
len -= sizeof(tls_len);
676+
677+
tls_valsz = ntohs(tls_len);
678+
tls_val = ptr;
679+
len -= tls_valsz;
680+
681+
switch (tls_type) {
682+
/* TLS version */
683+
case 0x21:
684+
pr_trace_msg(trace_channel, 19,
685+
"TLS TLV: TLS version: %.*s", (int) tls_valsz, (char *) tls_val);
686+
break;
687+
688+
/* TLS CN */
689+
case 0x22:
690+
pr_trace_msg(trace_channel, 19,
691+
"TLS TLV: TLS CN: %*.s", (int) tls_valsz, (char *) tls_val);
692+
break;
693+
694+
/* TLS cipher */
695+
case 0x23:
696+
pr_trace_msg(trace_channel, 19,
697+
"TLS TLV: TLS cipher: %.*s", (int) tls_valsz, (char *) tls_val);
698+
break;
699+
700+
/* TLS signature algorithm */
701+
case 0x24:
702+
pr_trace_msg(trace_channel, 19,
703+
"TLS TLV: TLS signature algorithm: %.*s", (int) tls_valsz,
704+
(char *) tls_val);
705+
break;
706+
707+
/* TLS key algorithm */
708+
case 0x25:
709+
pr_trace_msg(trace_channel, 19,
710+
"TLS TLV: TLS key algorithm: %.*s", (int) tls_valsz,
711+
(char *) tls_val);
712+
break;
713+
714+
default:
715+
pr_trace_msg(trace_channel, 3,
716+
"unsupported TLS TLV: %0x", tls_type);
717+
}
718+
}
719+
720+
return 0;
721+
}
722+
723+
static int read_haproxy_v2_tlvs(pool *p, conn_t *conn, size_t len) {
724+
while (len > 0) {
725+
int res;
726+
uint8_t tlv_type;
727+
uint16_t tlv_len;
728+
size_t tlv_valsz;
729+
void *tlv_val;
730+
struct iovec tlv_hdr[2];
731+
732+
pr_signals_handle();
733+
734+
tlv_hdr[0].iov_base = (void *) &tlv_type;
735+
tlv_hdr[0].iov_len = sizeof(tlv_type);
736+
tlv_hdr[1].iov_base = (void *) &tlv_len;
737+
tlv_hdr[1].iov_len = sizeof(tlv_len);
738+
739+
res = readv_sock(conn->rfd, tlv_hdr, 2);
740+
if (res < 0) {
741+
return -1;
742+
}
743+
744+
if (res != 3) {
745+
pr_trace_msg(trace_channel, 20, "read %lu TLV bytes, expected %lu bytes",
746+
(unsigned long) res, (unsigned long) 3);
747+
errno = EPERM;
748+
return -1;
749+
}
750+
751+
len -= res;
752+
753+
tlv_valsz = ntohs(tlv_len);
754+
tlv_val = palloc(p, tlv_valsz);
755+
756+
/* TODO: Handle short reads, interrupted reads better? */
757+
res = read(conn->rfd, tlv_val, tlv_valsz);
758+
if (res < 0) {
759+
return -1;
760+
}
761+
762+
if ((size_t) res != tlv_valsz) {
763+
pr_trace_msg(trace_channel, 20, "read %lu TLV bytes, expected %lu bytes",
764+
(unsigned long) res, (unsigned long) tlv_valsz);
765+
errno = EPERM;
766+
return -1;
767+
}
768+
769+
len -= res;
770+
771+
switch (tlv_type) {
772+
/* ALPN */
773+
case 0x01:
774+
pr_trace_msg(trace_channel, 19,
775+
"received proxy protocol V2 ALPN: %.*s", (int) tlv_valsz,
776+
(char *) tlv_val);
777+
break;
778+
779+
/* "Authority" (server name, ala SNI) */
780+
case 0x02:
781+
pr_trace_msg(trace_channel, 19,
782+
"received proxy protocol V2 SNI: %.*s", (int) tlv_valsz,
783+
(char *) tlv_val);
784+
break;
785+
786+
/* CRC32 */
787+
case 0x03:
788+
pr_trace_msg(trace_channel, 19,
789+
"received proxy protocol V2 CRC32 TLV (%lu bytes)",
790+
(unsigned long) tlv_valsz);
791+
break;
792+
793+
/* NOOP */
794+
case 0x04:
795+
pr_trace_msg(trace_channel, 19,
796+
"received proxy protocol V2 NOOP TLV (%lu bytes)",
797+
(unsigned long) tlv_valsz);
798+
break;
799+
800+
/* Unique ID */
801+
case 0x05:
802+
pr_trace_msg(trace_channel, 19,
803+
"received proxy protocol V2 Unique ID TLV (%lu bytes)",
804+
(unsigned long) tlv_valsz);
805+
break;
806+
807+
/* TLS */
808+
case 0x20:
809+
pr_trace_msg(trace_channel, 19,
810+
"received proxy protocol V2 TLS TLV (%lu bytes)",
811+
(unsigned long) tlv_valsz);
812+
if (read_haproxy_v2_tls_tlv(p, tlv_val, tlv_valsz) < 0) {
813+
return -1;
814+
}
815+
break;
816+
817+
/* Network namespace */
818+
case 0x30:
819+
pr_trace_msg(trace_channel, 19,
820+
"received proxy protocol V2 Network Namespace: %.*s",
821+
(int) tlv_valsz, (char *) tlv_val);
822+
break;
823+
824+
default:
825+
pr_trace_msg(trace_channel, 3,
826+
"unsupported proxy protocol TLV: %0x", tlv_type);
827+
}
828+
}
829+
830+
return 0;
831+
}
832+
619833
static int read_haproxy_v2(pool *p, conn_t *conn,
620834
const pr_netaddr_t **proxied_addr, unsigned int *proxied_port) {
621835
int res = 0;
@@ -668,18 +882,18 @@ static int read_haproxy_v2(pool *p, conn_t *conn,
668882
uint16_t src_port, dst_port;
669883
struct iovec ipv4[4];
670884
struct sockaddr_in *saddr;
885+
size_t tlv_len = 0;
671886

672887
pr_trace_msg(trace_channel, 17,
673888
"received proxy protocol V2 TCP/IPv4 transport family (%lu bytes)",
674889
(unsigned long) ntohs(v2_len));
675890

676-
if (ntohs(v2_len) != 12) {
891+
if (ntohs(v2_len) > 12) {
892+
/* The addresses are followed by TLVs. */
893+
tlv_len = ntohs(v2_len) - 12;
677894
pr_trace_msg(trace_channel, 3,
678-
"proxy protocol V2 TCP/IPv4 transport family sent %lu bytes, "
679-
"expected %lu bytes", (unsigned long) ntohs(v2_len),
680-
(unsigned long) 12);
681-
errno = EINVAL;
682-
return -1;
895+
"proxy protocol V2 TCP/IPv4 transport family received %lu bytes "
896+
"of TLV data", (unsigned long) tlv_len);
683897
}
684898

685899
ipv4[0].iov_base = (void *) &src_ipv4;
@@ -696,6 +910,12 @@ static int read_haproxy_v2(pool *p, conn_t *conn,
696910
return -1;
697911
}
698912

913+
if (tlv_len > 0) {
914+
if (read_haproxy_v2_tlvs(p, conn, tlv_len) < 0) {
915+
return -1;
916+
}
917+
}
918+
699919
src_addr = pr_netaddr_alloc(p);
700920
pr_netaddr_set_family((pr_netaddr_t *) src_addr, AF_INET);
701921
saddr = (struct sockaddr_in *) pr_netaddr_get_sockaddr(src_addr);
@@ -729,18 +949,18 @@ static int read_haproxy_v2(pool *p, conn_t *conn,
729949
uint16_t src_port, dst_port;
730950
struct iovec ipv6[4];
731951
struct sockaddr_in6 *saddr;
952+
size_t tlv_len = 0;
732953

733954
pr_trace_msg(trace_channel, 17,
734955
"received proxy protocol V2 TCP/IPv6 transport family (%lu bytes)",
735956
(unsigned long) ntohs(v2_len));
736957

737-
if (ntohs(v2_len) != 36) {
958+
if (ntohs(v2_len) > 36) {
959+
/* The addresses are followed by TLVs. */
960+
tlv_len = ntohs(v2_len) - 36;
738961
pr_trace_msg(trace_channel, 3,
739-
"proxy protocol V2 TCP/IPv6 transport family sent %lu bytes, "
740-
"expected %lu bytes", (unsigned long) ntohs(v2_len),
741-
(unsigned long) 36);
742-
errno = EINVAL;
743-
return -1;
962+
"proxy protocol V2 TCP/IPv4 transport family received %lu bytes "
963+
"of TLV data", (unsigned long) tlv_len);
744964
}
745965

746966
#ifdef PR_USE_IPV6
@@ -758,6 +978,12 @@ static int read_haproxy_v2(pool *p, conn_t *conn,
758978
return -1;
759979
}
760980

981+
if (tlv_len > 0) {
982+
if (read_haproxy_v2_tlvs(p, conn, tlv_len) < 0) {
983+
return -1;
984+
}
985+
}
986+
761987
src_addr = pr_netaddr_alloc(p);
762988
pr_netaddr_set_family((pr_netaddr_t *) src_addr, AF_INET6);
763989
saddr = (struct sockaddr_in6 *) pr_netaddr_get_sockaddr(src_addr);
@@ -885,14 +1111,6 @@ static int read_haproxy_v2(pool *p, conn_t *conn,
8851111
return 0;
8861112
}
8871113

888-
if (ntohs(pr_netaddr_get_port(dst_addr)) > 65535) {
889-
pr_log_debug(DEBUG0, MOD_PROXY_PROTOCOL_VERSION
890-
": out-of-range destination port provided: %u",
891-
ntohs(pr_netaddr_get_port(dst_addr)));
892-
errno = EINVAL;
893-
return -1;
894-
}
895-
8961114
/* Paranoidly check the given source address/port against the
8971115
* destination address/port. If the two tuples match, then the remote
8981116
* client is lying to us (it's not possible to have a TCP connection

0 commit comments

Comments
 (0)