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 */