Hello,

On 1/5/2017 4:47 PM, Emeric Brun wrote:
> On 01/05/2017 04:22 AM, Nenad Merdanovic wrote:
>> I have a working patch for this, but it's very ugly currently (minimal
>> error checking, no warnings/messages, no docs, very basic tests done
>> only, etc.)
>>
>> I expect to have a version for review by EOW (depending on the workload,
>> maybe a bit sooner).
>>
>> Regards,
>> Nenad
> 
> Great news Nenad!

I haven't really had as much time as I wanted for this, but I am
attaching a patch that I think is good enough for review as I don't
expect design decisions to change.

There are some minor things I want to improve (rename things like
'psk_key'), add some ifdefs for OPENSSL_NO_PSK and write the
documentation of course. Depending on the client/server side:
- On the bind line, there is a psk-file keyword that loads a series of
PSKs and any can be used
- On the server line, there is a psk keyword, that takes the same format
as the file (<identity>:<key>) and is used for the backend connection.

I'll send a full Git patch if this looks OK within the next few days.

Regards,
Nenad
diff --git a/include/types/listener.h b/include/types/listener.h
index 03f4a72b..4dc5a05b 100644
--- a/include/types/listener.h
+++ b/include/types/listener.h
@@ -134,6 +134,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 5092eb7d..f0cf8bd4 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -261,6 +261,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 e71ba793..6a24a2ec 100644
--- a/include/types/ssl_sock.h
+++ b/include/types/ssl_sock.h
@@ -25,6 +25,12 @@
 #include <openssl/ssl.h>
 #include <ebmbtree.h>
 
+struct psk_pair {
+       char *key;
+       char *identity;
+       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 acf1c39c..717ad323 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -531,6 +531,29 @@ end:
        return ret;
 }
 
+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;
+
+       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);
+
+       if(strlen(pp->key) > max_psk_len)
+               return 0;
+
+       memcpy(psk, pp->key, strlen(pp->key));
+
+       return(strlen(pp->key));
+}
+
 #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)
 {
@@ -2827,6 +2850,9 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, 
SSL_CTX *ctx)
        }
 #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);
 
@@ -2988,6 +3014,30 @@ 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 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;
+
+       strncpy(identity, srv->ssl_ctx.psk_identity, max_identity_len);
+
+       if(strlen(srv->ssl_ctx.psk_key) > max_psk_len)
+               return 0;
+
+       memcpy(psk, srv->ssl_ctx.psk_key, strlen(srv->ssl_ctx.psk_key));
+
+       return(strlen(srv->ssl_ctx.psk_key));
+}
+
 static int ssl_sock_srv_verifycbk(int ok, X509_STORE_CTX *ctx)
 {
        SSL *ssl;
@@ -3214,6 +3264,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;
 }
 
@@ -5608,6 +5661,57 @@ static int bind_parse_alpn(char **args, int cur_arg, 
struct proxy *px, struct bi
 #endif
 }
 
+
+/* 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);
+               pp->identity = strdup(thisline);
+
+               ebst_insert(&conf->psk, &pp->node);
+       }
+}
+
+
 /* parse the "ssl" bind keyword */
 static int bind_parse_ssl(char **args, int cur_arg, struct proxy *px, struct 
bind_conf *conf, char **err)
 {
@@ -5903,6 +6007,28 @@ 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);
+}
+
 /* 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)
 {
@@ -6635,6 +6761,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 */
@@ -6666,6 +6793,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 */

Reply via email to