Hi Willy--
   Here's my latest on extending Proxy Protocol V2.
   I'm still testing this, but I would like to solicit any feedback that
you may have.
   I believe I have incorporated all of your comments to date.
   So far, I have implemented CN as a first sub-vector.  I'm willing to
write a couple others, if you would like to suggest any.
   Thanks,
--Dave
diff --git a/include/proto/connection.h b/include/proto/connection.h
index 8609f17..0db677e 100644
--- a/include/proto/connection.h
+++ b/include/proto/connection.h
@@ -41,7 +41,9 @@ int conn_fd_handler(int fd);
 
 /* receive a PROXY protocol header over a connection */
 int conn_recv_proxy(struct connection *conn, int flag);
-int make_proxy_line(char *buf, int buf_len, struct sockaddr_storage *src, 
struct sockaddr_storage *dst);
+int make_proxy_line(char *buf, int buf_len, struct server *srv, struct 
connection *remote);
+int make_proxy_line_v1(char *buf, int buf_len, struct sockaddr_storage *src, 
struct sockaddr_storage *dst);
+int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct 
connection *remote);
 
 /* returns true is the transport layer is ready */
 static inline int conn_xprt_ready(const struct connection *conn)
diff --git a/include/proto/ssl_sock.h b/include/proto/ssl_sock.h
index 9d891d9..454edd5 100644
--- a/include/proto/ssl_sock.h
+++ b/include/proto/ssl_sock.h
@@ -40,6 +40,10 @@ int ssl_sock_prepare_srv_ctx(struct server *srv, struct 
proxy *px);
 void ssl_sock_free_all_ctx(struct bind_conf *bind_conf);
 const char *ssl_sock_get_cipher_name(struct connection *conn);
 const char *ssl_sock_get_proto_version(struct connection *conn);
+int ssl_sock_is_ssl(struct connection *conn);
+int ssl_sock_get_cert_used(struct connection *conn);
+char *ssl_sock_get_common_name(struct connection *conn);
+unsigned int ssl_sock_get_verify_result(struct connection *conn);
 
 #endif /* _PROTO_SSL_SOCK_H */
 
diff --git a/include/types/connection.h b/include/types/connection.h
index 5341a86..953cb16 100644
--- a/include/types/connection.h
+++ b/include/types/connection.h
@@ -265,6 +265,87 @@ struct connection {
        } addr; /* addresses of the remote side, client for producer and server 
for consumer */
 };
 
+/* proxy protocol v2 definitions */
+#define PP2_SIGNATURE_LEN        12
+#define PP2_HEADER_LEN           16
+#define PP2_VERSION            0x20
+#define PP2_CMD_LOCAL          0x00
+#define PP2_CMD_PROXY          0x01
+#define PP2_FAM_UNSPEC         0x00
+#define PP2_FAM_INET           0x10
+#define PP2_FAM_INET6          0x20
+#define PP2_FAM_UNIX           0x30
+#define PP2_TRANS_UNSPEC       0x00
+#define PP2_TRANS_STREAM       0x01
+#define PP2_TRANS_DGRAM        0x02
+
+#define PP2_ADDR_LEN_UNSPEC       0
+#define PP2_ADDR_LEN_INET        12
+#define PP2_ADDR_LEN_INET6       36
+#define PP2_ADDR_LEN_UNIX       216
+
+#define PP2_HDR_LEN_UNSPEC  (PP2_HEADER_LEN + PP2_ADDR_LEN_UNSPEC)
+#define PP2_HDR_LEN_INET    (PP2_HEADER_LEN + PP2_ADDR_LEN_INET)
+#define PP2_HDR_LEN_INET6   (PP2_HEADER_LEN + PP2_ADDR_LEN_INET6)
+#define PP2_HDR_LEN_UNIX    (PP2_HEADER_LEN + PP2_ADDR_LEN_UNIX)
+
+struct proxy_hdr_v2 {
+       uint8_t sig[12];   /* hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */
+       uint8_t cmd;       /* protocol version and command */
+       uint8_t fam;       /* protocol family and transport */
+       uint16_t len;      /* number of following bytes part of the header */
+};
+
+union proxy_addr {
+       struct {        /* for TCP/UDP over IPv4, len = 12 */
+               uint32_t src_addr;
+               uint32_t dst_addr;
+               uint16_t src_port;
+               uint16_t dst_port;
+       } ipv4_addr;
+       struct {        /* for TCP/UDP over IPv6, len = 36 */
+               uint8_t  src_addr[16];
+               uint8_t  dst_addr[16];
+               uint16_t src_port;
+               uint16_t dst_port;
+       } ipv6_addr;
+       struct {        /* for AF_UNIX sockets, len = 216 */
+               uint8_t src_addr[108];
+               uint8_t dst_addr[108];
+       } unix_addr;
+};
+
+#define PP2_TYPE_SSL        0x20
+#define PP2_TYPE_SSL_CN     0x21
+#define PP2_TYPE_SSL_DN     0x22
+
+struct tlv {
+       uint16_t length;
+       uint8_t type;
+       uint8_t value[0];
+}__attribute__((packed));
+
+struct tlv_ssl {
+       struct tlv tlv;
+       uint32_t version;
+       uint32_t client;
+       uint32_t verify;
+       uint8_t sub_tlv[0];
+}__attribute__((packed));
+
+#define PP2_CLIENT_SSL      0x00000001
+#define PP2_CLIENT_CERT     0x00000002
+
+struct tlv_cn {
+       struct tlv tlv;
+       uint8_t cn[0];
+}__attribute__((packed));
+
+struct tlv_dn {
+       struct tlv tlv;
+       uint8_t dn[0];
+}__attribute__((packed));
+
 #endif /* _TYPES_CONNECTION_H */
 
 /*
diff --git a/include/types/server.h b/include/types/server.h
index 54ab813..8c4c784 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -57,6 +57,12 @@
 #define SRV_SEND_PROXY 0x0800  /* this server talks the PROXY protocol */
 #define SRV_NON_STICK  0x1000  /* never add connections allocated to this 
server to a stick table */
 
+/* configured server options for send-proxy */
+#define SRV_PP_V2          0x0001        /* proxy protocol version 2 */
+#define SRV_PP_V2_SSL      0x0002        /* proxy protocol version 2 with SSL*/
+#define SRV_PP_V2_SSL_CN   0x0004        /* proxy protocol version 2 with SSL 
and CN*/
+#define SRV_PP_V2_SSL_DN   0x0008        /* proxy protocol version 2 with SSL 
and DN*/
+
 /* function which act on servers need to return various errors */
 #define SRV_STATUS_OK       0   /* everything is OK. */
 #define SRV_STATUS_INTERNAL 1   /* other unrecoverable errors. */
@@ -106,6 +112,7 @@ struct server {
        int rdr_len;                            /* the length of the 
redirection prefix */
        char *cookie;                           /* the id set in the cookie */
        char *rdr_pfx;                          /* the redirection prefix */
+       int pp_opts;                            /* proxy protocol options */
 
        struct proxy *proxy;                    /* the proxy this server 
belongs to */
        int served;                             /* # of active sessions 
currently being served (ie not pending) */
diff --git a/src/connection.c b/src/connection.c
index 1483f18..13abc16 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -437,6 +437,23 @@ int conn_recv_proxy(struct connection *conn, int flag)
        return 0;
 }
 
+int make_proxy_line(char *buf, int buf_len, struct server *srv, struct 
connection *remote)
+{
+       int ret = 0;
+
+       if (srv && (srv->pp_opts & SRV_PP_V2)) {
+               ret = make_proxy_line_v2(buf, buf_len, srv, remote);
+       }
+       else {
+               if (remote)
+                       ret = make_proxy_line_v1(buf, buf_len, 
&remote->addr.from, &remote->addr.to);
+               else
+                       ret = make_proxy_line_v1(buf, buf_len, NULL, NULL);
+       }
+
+       return ret;
+}
+
 /* Makes a PROXY protocol line from the two addresses. The output is sent to
  * buffer <buf> for a maximum size of <buf_len> (including the trailing zero).
  * It returns the number of bytes composing this line (including the trailing
@@ -444,7 +461,7 @@ int conn_recv_proxy(struct connection *conn, int flag)
  * TCP6 and "UNKNOWN" formats. If any of <src> or <dst> is null, UNKNOWN is
  * emitted as well.
  */
-int make_proxy_line(char *buf, int buf_len, struct sockaddr_storage *src, 
struct sockaddr_storage *dst)
+int make_proxy_line_v1(char *buf, int buf_len, struct sockaddr_storage *src, 
struct sockaddr_storage *dst)
 {
        int ret = 0;
 
@@ -516,3 +533,96 @@ int make_proxy_line(char *buf, int buf_len, struct 
sockaddr_storage *src, struct
        }
        return ret;
 }
+
+int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct 
connection *remote)
+{
+       const char pp2_signature[12] = {0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 
0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A};
+       int ret = 0;
+       struct proxy_hdr_v2 *hdr_p = (struct proxy_hdr_v2 *)buf;
+       union proxy_addr *addr_p = (union proxy_addr *)(buf + PP2_HEADER_LEN);
+       struct sockaddr_storage null_addr = {0};
+       struct sockaddr_storage *src = &null_addr;
+       struct sockaddr_storage *dst = &null_addr;
+
+       if (buf_len < PP2_HEADER_LEN)
+               return 0;
+       memcpy(hdr_p->sig, pp2_signature, PP2_SIGNATURE_LEN);
+
+       if (remote) {
+               src = &remote->addr.from;
+               dst = &remote->addr.to;
+       }
+       if (src && dst && src->ss_family == dst->ss_family && src->ss_family == 
AF_INET) {
+               if (buf_len < PP2_HDR_LEN_INET)
+                       return 0;
+               hdr_p->cmd = PP2_VERSION | PP2_CMD_PROXY;
+               hdr_p->fam = PP2_FAM_INET | PP2_TRANS_STREAM;
+               addr_p->ipv4_addr.src_addr = ((struct sockaddr_in 
*)src)->sin_addr.s_addr;
+               addr_p->ipv4_addr.dst_addr = ((struct sockaddr_in 
*)dst)->sin_addr.s_addr;
+               addr_p->ipv4_addr.src_port = ((struct sockaddr_in 
*)src)->sin_port;
+               addr_p->ipv4_addr.dst_port = ((struct sockaddr_in 
*)dst)->sin_port;
+               ret = PP2_HDR_LEN_INET;
+       }
+       else if (src && dst && src->ss_family == dst->ss_family && 
src->ss_family == AF_INET6) {
+               if (buf_len < PP2_HDR_LEN_INET6)
+                       return 0;
+               hdr_p->cmd = PP2_VERSION | PP2_CMD_PROXY;
+               hdr_p->fam = PP2_FAM_INET6 | PP2_TRANS_STREAM;
+               memcpy(addr_p->ipv6_addr.src_addr, &((struct sockaddr_in6 
*)src)->sin6_addr, 16);
+               memcpy(addr_p->ipv6_addr.dst_addr, &((struct sockaddr_in6 
*)dst)->sin6_addr, 16);
+               addr_p->ipv6_addr.src_port = ((struct sockaddr_in6 
*)src)->sin6_port;
+               addr_p->ipv6_addr.dst_port = ((struct sockaddr_in6 
*)dst)->sin6_port;
+               ret = PP2_HDR_LEN_INET6;
+       }
+       else {
+               if (buf_len < PP2_HDR_LEN_UNSPEC)
+                       return 0;
+               hdr_p->cmd = PP2_VERSION | PP2_CMD_LOCAL;
+               hdr_p->fam = PP2_FAM_UNSPEC | PP2_TRANS_UNSPEC;
+               ret = PP2_HDR_LEN_UNSPEC;
+       }
+
+#ifdef USE_OPENSSL
+       if (srv->pp_opts & SRV_PP_V2_SSL) {
+               int ssl_tlv_len = 0;
+               if ((buf_len - ret) < sizeof(struct tlv_ssl))
+                       return 0;
+               struct tlv_ssl *tlv = (struct tlv_ssl *)&buf[ret];
+               memset(tlv, 0, sizeof(struct tlv_ssl));
+               ssl_tlv_len += sizeof(struct tlv_ssl);
+               tlv->tlv.type = PP2_TYPE_SSL;
+               if (ssl_sock_is_ssl(remote)) {
+                       tlv->client |= PP2_CLIENT_SSL;
+                       if (ssl_sock_get_cert_used(remote)) {
+                               tlv->client |= PP2_CLIENT_CERT;
+                               tlv->verify = 
htonl(ssl_sock_get_verify_result(remote));
+                       }
+                       if (srv->pp_opts & SRV_PP_V2_SSL_CN) {
+                               char *cn = ssl_sock_get_common_name(remote);
+                               if (cn) {
+                                       int cn_len = strlen(cn);
+                                       if ((buf_len - ret - ssl_tlv_len) < 
cn_len + sizeof(struct tlv_cn))
+                                               return 0;
+                                       struct tlv_cn *tlv_cn = (struct tlv_cn 
*)&buf[ret+ssl_tlv_len];
+                                       memset(tlv_cn, 0, sizeof(struct 
tlv_cn));
+                                       tlv_cn->tlv.type = PP2_TYPE_SSL_CN;
+                                       tlv_cn->tlv.length = htons(cn_len + 
sizeof(tlv_cn));
+                                       memcpy(tlv_cn->cn, cn, cn_len);
+                                       ssl_tlv_len += cn_len + sizeof(tlv_cn);
+                               }
+                       }
+
+                       if (srv->pp_opts & SRV_PP_V2_SSL_DN) {
+
+                       }
+
+               }
+               tlv->tlv.length = htons((uint16_t)ssl_tlv_len);
+               ret += ssl_tlv_len;
+       }
+#endif
+
+       hdr_p->len = htons((uint16_t)(ret - PP2_HEADER_LEN));
+
+       return ret;
+}
diff --git a/src/server.c b/src/server.c
index 5ac5e37..cd651a0 100644
--- a/src/server.c
+++ b/src/server.c
@@ -615,6 +615,11 @@ int parse_server(const char *file, int linenum, char 
**args, struct proxy *curpr
                                newsrv->state |= SRV_SEND_PROXY;
                                cur_arg ++;
                        }
+                       else if (!defsrv && !strcmp(args[cur_arg], 
"send-proxy-v2")) {
+                               newsrv->state |= SRV_SEND_PROXY;
+                               newsrv->pp_opts |= SRV_PP_V2;
+                               cur_arg ++;
+                       }
                        else if (!defsrv && !strcmp(args[cur_arg], 
"check-send-proxy")) {
                                newsrv->check.send_proxy = 1;
                                cur_arg ++;
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 525c7b5..49e3c06 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -1896,6 +1896,80 @@ ssl_sock_get_dn_oneline(X509_NAME *a, struct chunk *out)
 
        return 1;
 }
+/* boolean, returns true if connection is over SSL */
+int ssl_sock_is_ssl(struct connection *conn)
+{
+       if (!conn || conn->xprt != &ssl_sock || !conn->xprt_ctx)
+               return 0;
+       else
+               return 1;
+}
+
+/* returns common name, NULL terminated, from client certificate, or NULL if 
none */
+char *ssl_sock_get_common_name(struct connection *conn)
+{
+       X509 *crt = NULL;
+       X509_NAME *name;
+       struct chunk *trash;
+       const char find_cn[] = "CN";
+       const struct chunk find_cn_chunk = {
+               .str = (char *)&find_cn,
+               .len = sizeof(find_cn)-1
+       };
+       char *result = NULL;
+
+       if (!conn || conn->xprt != &ssl_sock || !conn->xprt_ctx)
+               return NULL;
+
+       /* SSL_get_peer_certificate, it increase X509 * ref count */
+       crt = SSL_get_peer_certificate(conn->xprt_ctx);
+       if (!crt)
+               goto out;
+
+       name = X509_get_subject_name(crt);
+       if (!name)
+               goto out;
+
+       trash = get_trash_chunk();
+       if (ssl_sock_get_dn_entry(name, &find_cn_chunk, 1, trash) <= 0)
+               goto out;
+       trash->str[trash->len] = '\0';
+       result = trash->str;
+
+       out:
+       if (crt)
+               X509_free(crt);
+
+       return result;
+}
+
+/* returns 1 if client passed a certificate, 0 if not */
+int ssl_sock_get_cert_used(struct connection *conn)
+{
+       X509 *crt = NULL;
+
+       if (!conn || conn->xprt != &ssl_sock || !conn->xprt_ctx)
+               return 0;
+
+       /* SSL_get_peer_certificate, it increase X509 * ref count */
+       crt = SSL_get_peer_certificate(conn->xprt_ctx);
+       if (crt) {
+               X509_free(crt);
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
+/* returns result from SSL verify */
+unsigned int ssl_sock_get_verify_result(struct connection *conn)
+{
+       if (!conn || conn->xprt != &ssl_sock || !conn->xprt_ctx)
+               return (unsigned int)X509_V_ERR_APPLICATION_VERIFICATION;
+
+       return (unsigned int)SSL_get_verify_result(conn->xprt_ctx);
+}
+
 
 /***** Below are some sample fetching functions for ACL/patterns *****/
 
@@ -3605,6 +3679,34 @@ static int srv_parse_no_tls_tickets(char **args, int 
*cur_arg, struct proxy *px,
        newsrv->ssl_ctx.options |= SRV_SSL_O_NO_TLS_TICKETS;
        return 0;
 }
+/* parse the "send-proxy-v2-ssl" server keyword */
+static int srv_parse_send_proxy_ssl(char **args, int *cur_arg, struct proxy 
*px, struct server *newsrv, char **err)
+{
+       newsrv->state |= SRV_SEND_PROXY;
+       newsrv->pp_opts |= SRV_PP_V2;
+       newsrv->pp_opts |= SRV_PP_V2_SSL;
+       return 0;
+}
+
+/* parse the "send-proxy-v2-ssl-cn" server keyword */
+static int srv_parse_send_proxy_cn(char **args, int *cur_arg, struct proxy 
*px, struct server *newsrv, char **err)
+{
+       newsrv->state |= SRV_SEND_PROXY;
+       newsrv->pp_opts |= SRV_PP_V2;
+       newsrv->pp_opts |= SRV_PP_V2_SSL;
+       newsrv->pp_opts |= SRV_PP_V2_SSL_CN;
+       return 0;
+}
+
+/* parse the "send-proxy-v2-ssl-dn" server keyword */
+static int srv_parse_send_proxy_dn(char **args, int *cur_arg, struct proxy 
*px, struct server *newsrv, char **err)
+{
+       newsrv->state |= SRV_SEND_PROXY;
+       newsrv->pp_opts |= SRV_PP_V2;
+       newsrv->pp_opts |= SRV_PP_V2_SSL;
+       newsrv->pp_opts |= SRV_PP_V2_SSL_DN;
+       return 0;
+}
 
 /* parse the "ssl" server keyword */
 static int srv_parse_ssl(char **args, int *cur_arg, struct proxy *px, struct 
server *newsrv, char **err)
@@ -3784,6 +3886,9 @@ static struct srv_kw_list srv_kws = { "SSL", { }, {
        { "no-tlsv11",             srv_parse_no_tlsv11,      0, 0 }, /* disable 
TLSv11 */
        { "no-tlsv12",             srv_parse_no_tlsv12,      0, 0 }, /* disable 
TLSv12 */
        { "no-tls-tickets",        srv_parse_no_tls_tickets, 0, 0 }, /* disable 
session resumption tickets */
+       { "send-proxy-v2-ssl",     srv_parse_send_proxy_ssl, 0, 0 }, /* send 
PROXY protocol header v2 with SSL info */
+       { "send-proxy-v2-ssl-cn",  srv_parse_send_proxy_cn,  0, 0 }, /* send 
PROXY protocol header v2 with CN */
+       { "send-proxy-v2-ssl-dn",  srv_parse_send_proxy_dn,  0, 0 }, /* send 
PROXY protocol header v2 with DN */
        { "ssl",                   srv_parse_ssl,            0, 0 }, /* enable 
SSL processing */
        { "verify",                srv_parse_verify,         1, 0 }, /* set SSL 
verify method */
        { "verifyhost",            srv_parse_verifyhost,     1, 0 }, /* require 
that SSL cert verifies for hostname */
diff --git a/src/stream_interface.c b/src/stream_interface.c
index f23a9b0..67a5234 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -422,11 +422,7 @@ int conn_si_send_proxy(struct connection *conn, unsigned 
int flag)
                if (conn->data == &si_conn_cb) {
                        struct stream_interface *si = conn->owner;
                        struct connection *remote = 
objt_conn(si->ob->prod->end);
-
-                       if (remote)
-                               ret = make_proxy_line(trash.str, trash.size, 
&remote->addr.from, &remote->addr.to);
-                       else
-                               ret = make_proxy_line(trash.str, trash.size, 
NULL, NULL);
+                       ret = make_proxy_line(trash.str, trash.size, 
objt_server(conn->target), remote);
                }
                else {
                        /* The target server expects a LOCAL line to be sent 
first. Retrieving
@@ -440,7 +436,7 @@ int conn_si_send_proxy(struct connection *conn, unsigned 
int flag)
                        if (!(conn->flags & CO_FL_ADDR_TO_SET))
                                goto out_wait;
 
-                       ret = make_proxy_line(trash.str, trash.size, 
&conn->addr.from, &conn->addr.to);
+                       ret = make_proxy_line(trash.str, trash.size, 
objt_server(conn->target), conn);
                }
 
                if (!ret)

Reply via email to