From: Nenad Merdanovic <[email protected]>

Signed-off-by: Nenad Merdanovic <[email protected]>
---
 doc/configuration.txt    |  10 ++++
 include/types/listener.h |   1 +
 include/types/server.h   |   2 +
 include/types/ssl_sock.h |   5 ++
 src/ssl_sock.c           | 133 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 151 insertions(+)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index 43a6c3e1..08b646f5 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -10492,6 +10492,10 @@ process [ all | odd | even | <number 1-64>[-<number 
1-64>] ]
   and allow a smoother inter-process load balancing. Currently Linux 3.9 and
   above is known for supporting this. See also "bind-process" and "nbproc".
 
+psk-file <file>
+  Enables use of PSK cipher suites with PSKs stored in the specified file.
+  The entries should be in form "identity:key", one per line.
+
 ssl
   This setting is only available when support for OpenSSL was built in. It
   enables SSL deciphering on connections instantiated from this listener. A
@@ -11110,6 +11114,12 @@ port <port>
 
   Supported in default-server: Yes
 
+psk <identity:key>
+  Sets the PSK to use when connecting to the backend server over SSL, if the 
PSK
+  cipher suite is negotiated. The entry must be in form "identity:key".
+
+  Supported in default-server: No
+
 redir <prefix>
   The "redir" parameter enables the redirection mode for all GET and HEAD
   requests addressing this server. This means that instead of having HAProxy
diff --git a/include/types/listener.h b/include/types/listener.h
index d813deb3..98e34a38 100644
--- a/include/types/listener.h
+++ b/include/types/listener.h
@@ -147,6 +147,7 @@ struct bind_conf {
        int strict_sni;            /* refuse negotiation if sni doesn't match a 
certificate */
        struct eb_root sni_ctx;    /* sni_ctx tree of all known certs 
full-names sorted by name */
        struct eb_root sni_w_ctx;  /* sni_ctx tree of all known certs wildcards 
sorted by name */
+       struct eb_root psk;        /* PSK tree, keyed by identity */
        struct tls_keys_ref *keys_ref; /* TLS ticket keys reference */
 
        char *ca_sign_file;        /* CAFile used to generate and sign server 
certificates */
diff --git a/include/types/server.h b/include/types/server.h
index 51b7e535..25aa4bbd 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -262,6 +262,8 @@ struct server {
                char *ca_file;                  /* CAfile to use on verify */
                char *crl_file;                 /* CRLfile to use on verify */
                char *client_crt;               /* client certificate to send */
+               char *psk_identity;             /* PSK identity */
+               char *psk_key;                  /* PSK key */
                struct sample_expr *sni;        /* sample expression for SNI */
        } ssl_ctx;
 #endif
diff --git a/include/types/ssl_sock.h b/include/types/ssl_sock.h
index a384f055..b51ddf49 100644
--- a/include/types/ssl_sock.h
+++ b/include/types/ssl_sock.h
@@ -26,6 +26,11 @@
 #include <openssl/ssl.h>
 #include <ebmbtree.h>
 
+struct psk_pair {
+       char *key;
+       struct ebmb_node node;
+};
+
 struct sni_ctx {
        SSL_CTX *ctx;             /* context associated to the certificate */
        int order;                /* load order for the certificate */
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 232a4970..664581c2 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -533,6 +533,31 @@ end:
 }
 #endif
 
+static int ssl_srv_psk_cb(SSL *ssl, char *identity, unsigned char *psk, 
unsigned int max_psk_len)
+{
+       struct connection *conn;
+       struct ebmb_node *node;
+       struct psk_pair *pp;
+       int keylen;
+
+       conn = SSL_get_app_data(ssl);
+
+       node = ebst_lookup(&objt_listener(conn->target)->bind_conf->psk, 
identity);
+
+       if (!node)
+               return 0;
+
+       pp = ebmb_entry(node, struct psk_pair, node);
+
+       keylen = strlen(pp->key);
+       if(keylen > max_psk_len)
+               return 0;
+
+       memcpy(psk, pp->key, keylen);
+
+       return keylen;
+}
+
 #if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
 static int ssl_tlsext_ticket_key_cb(SSL *s, unsigned char key_name[16], 
unsigned char *iv, EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx, int enc)
 {
@@ -2955,6 +2980,9 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, 
struct ssl_bind_conf *ssl_
        }
 #endif
 
+       if(ebmb_first(&bind_conf->psk))
+               SSL_CTX_set_psk_server_callback(ctx, ssl_srv_psk_cb);
+
        if (global_ssl.life_time)
                SSL_CTX_set_timeout(ctx, global_ssl.life_time);
 
@@ -3140,6 +3168,32 @@ static int ssl_sock_srv_hostcheck(const char *pattern, 
const char *hostname)
        return 1;
 }
 
+static int ssl_sock_client_psk_cb(SSL *ssl, const char *hint, char *identity, 
unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len)
+{
+       struct connection *conn;
+       struct server *srv;
+       int keylen;
+       int ret;
+
+       (void) hint;
+
+       conn = SSL_get_app_data(ssl);
+       srv = objt_server(conn->target);
+
+       if(strlen(srv->ssl_ctx.psk_identity) + 1 > max_identity_len)
+               return 0;
+
+       strcpy(identity, srv->ssl_ctx.psk_identity);
+
+       keylen = strlen(srv->ssl_ctx.psk_key);
+       if(keylen > max_psk_len)
+               return 0;
+
+       memcpy(psk, srv->ssl_ctx.psk_key, keylen);
+
+       return keylen;
+}
+
 static int ssl_sock_srv_verifycbk(int ok, X509_STORE_CTX *ctx)
 {
        SSL *ssl;
@@ -3367,6 +3421,9 @@ int ssl_sock_prepare_srv_ctx(struct server *srv)
                cfgerr++;
        }
 
+       if (srv->ssl_ctx.psk_identity && srv->ssl_ctx.psk_key)
+               SSL_CTX_set_psk_client_callback(srv->ssl_ctx.ctx, 
ssl_sock_client_psk_cb);
+
        return cfgerr;
 }
 
@@ -5849,6 +5906,56 @@ static int bind_parse_alpn(char **args, int cur_arg, 
struct proxy *px, struct bi
        return ssl_bind_parse_alpn(args, cur_arg, px, &conf->ssl_conf, err);
 }
 
+/* parse the "psk-file" bind keyword */
+static int bind_parse_psk_file(char **args, int cur_arg, struct proxy *px, 
struct bind_conf *conf, char **err)
+{
+       FILE *f;
+       int i = 0;
+       char thisline[LINESIZE];
+       struct psk_pair *pp;
+       char *key;
+
+       if (!*args[cur_arg + 1]) {
+               if (err)
+                       memprintf(err, "'%s' : missing PSK file path", 
args[cur_arg]);
+               return ERR_ALERT | ERR_FATAL;
+       }
+
+       if ((f = fopen(args[cur_arg + 1], "r")) == NULL) {
+               if (err)
+                       memprintf(err, "'%s' : unable to load ssl PSK file", 
args[cur_arg+1]);
+               return ERR_ALERT | ERR_FATAL;
+       }
+
+       while (fgets(thisline, sizeof(thisline), f) != NULL) {
+               int len = strlen(thisline);
+               i++;
+               /* Strip newline characters from the end */
+               if(thisline[len - 1] == '\n')
+                       thisline[--len] = 0;
+
+               if(thisline[len - 1] == '\r')
+                       thisline[--len] = 0;
+
+               key = strchr(thisline, ':');
+               if (!key || key == thisline || key == thisline + len - 1) {
+                       if (err)
+                               memprintf(err, "'%s' : unable to load ssl PSK 
file, syntax error at line %d", args[cur_arg+1], i);
+                       return ERR_ALERT | ERR_FATAL;
+               }
+
+               *key++ = '\0';
+
+               pp = calloc(1, sizeof(*pp) + strlen(thisline) + 1);
+               memcpy(pp->node.key, thisline, strlen(thisline) + 1);
+               pp->key = strdup(key);
+
+               ebst_insert(&conf->psk, &pp->node);
+       }
+
+       return 0;
+}
+
 /* parse the "ssl" bind keyword */
 static int bind_parse_ssl(char **args, int cur_arg, struct proxy *px, struct 
bind_conf *conf, char **err)
 {
@@ -6148,6 +6255,30 @@ 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;
 }
+
+static int srv_parse_psk(char **args, int *cur_arg, struct proxy *px, struct 
server *newsrv, char **err)
+{
+       char *key;
+
+       if (!*args[*cur_arg + 1]) {
+               memprintf(err, "'%s' : missing PSK pair in format 
<identity>:<key>", args[*cur_arg]);
+               return ERR_ALERT | ERR_FATAL;
+       }
+
+       key = strchr(args[*cur_arg + 1], ':');
+       if (!key || key == args[*cur_arg + 1] || key == args[*cur_arg + 1] + 
strlen(args[*cur_arg + 1]) - 1) {
+               if (err)
+                       memprintf(err, "'%s' : unable to load ssl PSK file in 
format <identity>:<key>", args[*cur_arg+1]);
+               return ERR_ALERT | ERR_FATAL;
+       }
+
+       *key++ = '\0';
+       newsrv->ssl_ctx.psk_identity = strdup(args[*cur_arg+1]);
+       newsrv->ssl_ctx.psk_key = strdup(key);
+
+       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)
 {
@@ -6899,6 +7030,7 @@ static struct bind_kw_list bind_kws = { "SSL", { }, {
        { "no-tlsv11",             bind_parse_no_tlsv11,       0 }, /* disable 
TLSv11 */
        { "no-tlsv12",             bind_parse_no_tlsv12,       0 }, /* disable 
TLSv12 */
        { "no-tls-tickets",        bind_parse_no_tls_tickets,  0 }, /* disable 
session resumption tickets */
+       { "psk-file",              bind_parse_psk_file,        1 }, /* load the 
file containing PSKs */
        { "ssl",                   bind_parse_ssl,             0 }, /* enable 
SSL processing */
        { "strict-sni",            bind_parse_strict_sni,      0 }, /* refuse 
negotiation if sni doesn't match a certificate */
        { "tls-ticket-keys",       bind_parse_tls_ticket_keys, 1 }, /* set file 
to load TLS ticket keys from */
@@ -6930,6 +7062,7 @@ 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 */
+       { "psk",                   srv_parse_psk,            1, 0 }, /* PSK */
        { "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 */
        { "sni",                   srv_parse_sni,            1, 0 }, /* send 
SNI extension */
-- 
2.11.0


Reply via email to