]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: ssl: add support of aes256 bits ticket keys on file and cli.
authorEmeric Brun <ebrun@haproxy.com>
Thu, 10 Jan 2019 16:51:55 +0000 (17:51 +0100)
committerWilly Tarreau <w@1wt.eu>
Mon, 14 Jan 2019 18:32:58 +0000 (19:32 +0100)
Openssl switched from aes128 to aes256 since may 2016  to compute
tls ticket secrets used by default. But Haproxy still handled only
128 bits keys for both tls key file and CLI.

This patch permit the user to set aes256 keys throught CLI or
the key file (80 bytes encoded in base64) in the same way that
aes128 keys were handled (48 bytes encoded in base64):
- first 16 bytes for the key name
- next 16/32 bytes for aes 128/256 key bits key
- last 16/32 bytes for hmac 128/256 bits

Both sizes are now supported (but keys from same file must be
of the same size and can but updated via CLI only using a key of
the same size).

Note: This feature need the fix "dec func ignores padding for output
size checking."

doc/configuration.txt
doc/management.txt
include/proto/ssl_sock.h
include/types/ssl_sock.h
src/ssl_sock.c

index 888515fb22a467c317d2db2d0541bafe2e554c56..d6842942de2fb859fa3f9fbb34c1991e63100413 100644 (file)
@@ -11337,13 +11337,16 @@ tfo
 
 tls-ticket-keys <keyfile>
   Sets the TLS ticket keys file to load the keys from. The keys need to be 48
-  bytes long, encoded with base64 (ex. openssl rand -base64 48). Number of keys
-  is specified by the TLS_TICKETS_NO build option (default 3) and at least as
-  many keys need to be present in the file. Last TLS_TICKETS_NO keys will be
-  used for decryption and the penultimate one for encryption. This enables easy
-  key rotation by just appending new key to the file and reloading the process.
-  Keys must be periodically rotated (ex. every 12h) or Perfect Forward Secrecy
-  is compromised. It is also a good idea to keep the keys off any permanent
+  or 80 bytes long, depending if aes128 or aes256 is used, encoded with base64
+  with one line per key (ex. openssl rand 80 | openssl base64 -A | xargs echo).
+  The first key determines the key length used for next keys: you can't mix
+  aes128 and aes256 keys. Number of keys is specified by the TLS_TICKETS_NO
+  build option (default 3) and at least as many keys need to be present in
+  the file. Last TLS_TICKETS_NO keys will be used for decryption and the
+  penultimate one for encryption. This enables easy key rotation by just
+  appending new key to the file and reloading the process.  Keys must be
+  periodically rotated (ex. every 12h) or Perfect Forward Secrecy is
+  compromised. It is also a good idea to keep the keys off any permanent
   storage such as hard drives (hint: use tmpfs and don't swap those files).
   Lifetime hint can be changed using tune.ssl.timeout.
 
index 3b557fed0cf7f485781f04449ded8879e072630e..a3f237122f0a0e20a4793937711a6fb278e691d2 100644 (file)
@@ -1761,7 +1761,7 @@ set ssl tls-key <id> <tlskey>
   ultimate key, while the penultimate one is used for encryption (others just
   decrypt). The oldest TLS key present is overwritten. <id> is either a numeric
   #<id> or <file> returned by "show tls-keys". <tlskey> is a base64 encoded 48
-  bit TLS ticket key (ex. openssl rand -base64 48).
+  or 80 bits TLS ticket key (ex. openssl rand 80 | openssl base64 -A).
 
 set table <table> key <key> [data.<data_type> <value>]*
   Create or update a stick-table entry in the table. If the key is not present,
index ce66cdfa1ce991bf62f049af6d10dfa1ab9b9f6b..62ebcb87e413eeb9a02913ca0d6dc8f130699152 100644 (file)
@@ -67,7 +67,7 @@ unsigned int ssl_sock_get_verify_result(struct connection *conn);
 int ssl_sock_update_ocsp_response(struct buffer *ocsp_response, char **err);
 #endif
 #if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
-void ssl_sock_update_tlskey_ref(struct tls_keys_ref *ref,
+int ssl_sock_update_tlskey_ref(struct tls_keys_ref *ref,
                                struct buffer *tlskey);
 int ssl_sock_update_tlskey(char *filename, struct buffer *tlskey, char **err);
 struct tls_keys_ref *tlskeys_ref_lookup(const char *filename);
index 2e02631c5de2690aed87131a00255c8728872a88..a2fff77f65f4db5406f36fba2d0fc48580854274 100644 (file)
@@ -49,19 +49,32 @@ struct tls_version_filter {
 
 extern struct list tlskeys_reference;
 
-struct tls_sess_key {
+struct tls_sess_key_128 {
        unsigned char name[16];
        unsigned char aes_key[16];
        unsigned char hmac_key[16];
 } __attribute__((packed));
 
+struct tls_sess_key_256 {
+       unsigned char name[16];
+       unsigned char aes_key[32];
+       unsigned char hmac_key[32];
+} __attribute__((packed));
+
+union tls_sess_key{
+       unsigned char name[16];
+       struct tls_sess_key_256 key_128;
+       struct tls_sess_key_256 key_256;
+} __attribute__((packed));
+
 struct tls_keys_ref {
        struct list list; /* Used to chain refs. */
        char *filename;
        int unique_id; /* Each pattern reference have unique id. */
        int refcount;  /* number of users of this tls_keys_ref. */
-       struct tls_sess_key *tlskeys;
+       union tls_sess_key *tlskeys;
        int tls_ticket_enc_index;
+       int key_size_bits;
        __decl_hathreads(HA_RWLOCK_T lock); /* lock used to protect the ref */
 };
 
index f1af558ed9889aac903a5228770d133e4f2de768..928de05f832c949d4a52d4ae09fe5ad235fb3f1b 100644 (file)
@@ -849,7 +849,7 @@ end:
 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)
 {
        struct tls_keys_ref *ref;
-       struct tls_sess_key *keys;
+       union tls_sess_key *keys;
        struct connection *conn;
        int head;
        int i;
@@ -868,11 +868,22 @@ static int ssl_tlsext_ticket_key_cb(SSL *s, unsigned char key_name[16], unsigned
                if(!RAND_pseudo_bytes(iv, EVP_MAX_IV_LENGTH))
                        goto end;
 
-               if(!EVP_EncryptInit_ex(ectx, EVP_aes_128_cbc(), NULL, keys[head].aes_key, iv))
-                       goto end;
+               if (ref->key_size_bits == 128) {
 
-               HMAC_Init_ex(hctx, keys[head].hmac_key, 16, HASH_FUNCT(), NULL);
-               ret = 1;
+                       if(!EVP_EncryptInit_ex(ectx, EVP_aes_128_cbc(), NULL, keys[head].key_128.aes_key, iv))
+                               goto end;
+
+                       HMAC_Init_ex(hctx, keys[head].key_128.hmac_key, 16, HASH_FUNCT(), NULL);
+                       ret = 1;
+               }
+               else if (ref->key_size_bits == 256 ) {
+
+                       if(!EVP_EncryptInit_ex(ectx, EVP_aes_256_cbc(), NULL, keys[head].key_256.aes_key, iv))
+                               goto end;
+
+                       HMAC_Init_ex(hctx, keys[head].key_256.hmac_key, 32, HASH_FUNCT(), NULL);
+                       ret = 1;
+               }
        } else {
                for (i = 0; i < TLS_TICKETS_NO; i++) {
                        if (!memcmp(key_name, keys[(head + i) % TLS_TICKETS_NO].name, 16))
@@ -882,13 +893,22 @@ static int ssl_tlsext_ticket_key_cb(SSL *s, unsigned char key_name[16], unsigned
                goto end;
 
          found:
-               HMAC_Init_ex(hctx, keys[(head + i) % TLS_TICKETS_NO].hmac_key, 16, HASH_FUNCT(), NULL);
-               if(!EVP_DecryptInit_ex(ectx, EVP_aes_128_cbc(), NULL, keys[(head + i) % TLS_TICKETS_NO].aes_key, iv))
-                       goto end;
-
-               /* 2 for key renewal, 1 if current key is still valid */
-               ret = i ? 2 : 1;
+               if (ref->key_size_bits == 128) {
+                       HMAC_Init_ex(hctx, keys[(head + i) % TLS_TICKETS_NO].key_128.hmac_key, 16, HASH_FUNCT(), NULL);
+                       if(!EVP_DecryptInit_ex(ectx, EVP_aes_128_cbc(), NULL, keys[(head + i) % TLS_TICKETS_NO].key_128.aes_key, iv))
+                               goto end;
+                       /* 2 for key renewal, 1 if current key is still valid */
+                       ret = i ? 2 : 1;
+               }
+               else if (ref->key_size_bits == 256) {
+                       HMAC_Init_ex(hctx, keys[(head + i) % TLS_TICKETS_NO].key_256.hmac_key, 32, HASH_FUNCT(), NULL);
+                       if(!EVP_DecryptInit_ex(ectx, EVP_aes_256_cbc(), NULL, keys[(head + i) % TLS_TICKETS_NO].key_256.aes_key, iv))
+                               goto end;
+                       /* 2 for key renewal, 1 if current key is still valid */
+                       ret = i ? 2 : 1;
+               }
        }
+
   end:
        HA_RWLOCK_RDUNLOCK(TLSKEYS_REF_LOCK, &ref->lock);
        return ret;
@@ -914,14 +934,31 @@ struct tls_keys_ref *tlskeys_ref_lookupid(int unique_id)
         return NULL;
 }
 
-void ssl_sock_update_tlskey_ref(struct tls_keys_ref *ref,
+/* Update the key into ref: if keysize doesnt
+ * match existing ones, this function returns -1
+ * else it returns 0 on success.
+ */
+int ssl_sock_update_tlskey_ref(struct tls_keys_ref *ref,
                                struct buffer *tlskey)
 {
+       if (ref->key_size_bits == 128) {
+               if (tlskey->data != sizeof(struct tls_sess_key_128))
+                              return -1;
+       }
+       else if (ref->key_size_bits == 256) {
+               if (tlskey->data != sizeof(struct tls_sess_key_256))
+                              return -1;
+       }
+       else
+               return -1;
+
        HA_RWLOCK_WRLOCK(TLSKEYS_REF_LOCK, &ref->lock);
        memcpy((char *) (ref->tlskeys + ((ref->tls_ticket_enc_index + 2) % TLS_TICKETS_NO)),
               tlskey->area, tlskey->data);
        ref->tls_ticket_enc_index = (ref->tls_ticket_enc_index + 1) % TLS_TICKETS_NO;
        HA_RWLOCK_WRUNLOCK(TLSKEYS_REF_LOCK, &ref->lock);
+
+       return 0;
 }
 
 int ssl_sock_update_tlskey(char *filename, struct buffer *tlskey, char **err)
@@ -932,7 +969,11 @@ int ssl_sock_update_tlskey(char *filename, struct buffer *tlskey, char **err)
                memprintf(err, "Unable to locate the referenced filename: %s", filename);
                return 1;
        }
-       ssl_sock_update_tlskey_ref(ref, tlskey);
+       if (ssl_sock_update_tlskey_ref(ref, tlskey) < 0) {
+               memprintf(err, "Invalid key size");
+               return 1;
+       }
+
        return 0;
 }
 
@@ -7826,7 +7867,7 @@ static int bind_parse_tls_ticket_keys(char **args, int cur_arg, struct proxy *px
                return ERR_ALERT | ERR_FATAL;
        }
 
-       keys_ref->tlskeys = malloc(TLS_TICKETS_NO * sizeof(struct tls_sess_key));
+       keys_ref->tlskeys = malloc(TLS_TICKETS_NO * sizeof(union tls_sess_key));
        if (!keys_ref->tlskeys) {
                free(keys_ref);
                if (err)
@@ -7851,8 +7892,11 @@ static int bind_parse_tls_ticket_keys(char **args, int cur_arg, struct proxy *px
                return ERR_ALERT | ERR_FATAL;
        }
 
+       keys_ref->key_size_bits = 0;
        while (fgets(thisline, sizeof(thisline), f) != NULL) {
                int len = strlen(thisline);
+               int dec_size;
+
                /* Strip newline characters from the end */
                if(thisline[len - 1] == '\n')
                        thisline[--len] = 0;
@@ -7860,7 +7904,8 @@ static int bind_parse_tls_ticket_keys(char **args, int cur_arg, struct proxy *px
                if(thisline[len - 1] == '\r')
                        thisline[--len] = 0;
 
-               if (base64dec(thisline, len, (char *) (keys_ref->tlskeys + i % TLS_TICKETS_NO), sizeof(struct tls_sess_key)) != sizeof(struct tls_sess_key)) {
+               dec_size = base64dec(thisline, len, (char *) (keys_ref->tlskeys + i % TLS_TICKETS_NO), sizeof(union tls_sess_key));
+               if (dec_size < 0) {
                        free(keys_ref->filename);
                        free(keys_ref->tlskeys);
                        free(keys_ref);
@@ -7869,6 +7914,23 @@ static int bind_parse_tls_ticket_keys(char **args, int cur_arg, struct proxy *px
                        fclose(f);
                        return ERR_ALERT | ERR_FATAL;
                }
+               else if (!keys_ref->key_size_bits && (dec_size == sizeof(struct tls_sess_key_128))) {
+                       keys_ref->key_size_bits = 128;
+               }
+               else if (!keys_ref->key_size_bits && (dec_size == sizeof(struct tls_sess_key_256))) {
+                       keys_ref->key_size_bits = 256;
+               }
+               else if (((dec_size != sizeof(struct tls_sess_key_128)) && (dec_size != sizeof(struct tls_sess_key_256)))
+                        || ((dec_size == sizeof(struct tls_sess_key_128) && (keys_ref->key_size_bits != 128)))
+                        || ((dec_size == sizeof(struct tls_sess_key_256) && (keys_ref->key_size_bits != 256)))) {
+                       free(keys_ref->filename);
+                       free(keys_ref->tlskeys);
+                       free(keys_ref);
+                       if (err)
+                               memprintf(err, "'%s' : wrong sized key on line %d", args[cur_arg+1], i + 1);
+                       fclose(f);
+                       return ERR_ALERT | ERR_FATAL;
+               }
                i++;
        }
 
@@ -8814,11 +8876,24 @@ static int cli_io_handler_tlskeys_files(struct appctx *appctx) {
 
                                        chunk_reset(t2);
                                        /* should never fail here because we dump only a key in the t2 buffer */
-                                       t2->data = a2base64((char *)(ref->tlskeys + (head + 2 + appctx->ctx.cli.i1) % TLS_TICKETS_NO),
-                                                          sizeof(struct tls_sess_key),
-                                                          t2->area, t2->size);
-                                       chunk_appendf(&trash, "%d.%d %s\n", ref->unique_id, appctx->ctx.cli.i1,
-                                                     t2->area);
+                                       if (ref->key_size_bits == 128) {
+                                               t2->data = a2base64((char *)(ref->tlskeys + (head + 2 + appctx->ctx.cli.i1) % TLS_TICKETS_NO),
+                                                                  sizeof(struct tls_sess_key_128),
+                                                                  t2->area, t2->size);
+                                               chunk_appendf(&trash, "%d.%d %s\n", ref->unique_id, appctx->ctx.cli.i1,
+                                                             t2->area);
+                                       }
+                                       else if (ref->key_size_bits == 256) {
+                                               t2->data = a2base64((char *)(ref->tlskeys + (head + 2 + appctx->ctx.cli.i1) % TLS_TICKETS_NO),
+                                                                  sizeof(struct tls_sess_key_256),
+                                                                  t2->area, t2->size);
+                                               chunk_appendf(&trash, "%d.%d %s\n", ref->unique_id, appctx->ctx.cli.i1,
+                                                             t2->area);
+                                       }
+                                       else {
+                                               /* This case should never happen */
+                                               chunk_appendf(&trash, "%d.%d <unknown>\n", ref->unique_id, appctx->ctx.cli.i1);
+                                       }
 
                                        if (ci_putchk(si_ic(si), &trash) == -1) {
                                                /* let's try again later from this stream. We add ourselves into
@@ -8906,14 +8981,20 @@ static int cli_parse_set_tlskeys(char **args, char *payload, struct appctx *appc
        }
 
        ret = base64dec(args[4], strlen(args[4]), trash.area, trash.size);
-       if (ret != sizeof(struct tls_sess_key)) {
+       if (ret < 0) {
                appctx->ctx.cli.severity = LOG_ERR;
                appctx->ctx.cli.msg = "'set ssl tls-key' received invalid base64 encoded TLS key.\n";
                appctx->st0 = CLI_ST_PRINT;
                return 1;
        }
+
        trash.data = ret;
-       ssl_sock_update_tlskey_ref(ref, &trash);
+       if (ssl_sock_update_tlskey_ref(ref, &trash) < 0) {
+               appctx->ctx.cli.severity = LOG_ERR;
+               appctx->ctx.cli.msg = "'set ssl tls-key' received a key of wrong size.\n";
+               appctx->st0 = CLI_ST_PRINT;
+               return 1;
+       }
        appctx->ctx.cli.severity = LOG_INFO;
        appctx->ctx.cli.msg = "TLS ticket key updated!\n";
        appctx->st0 = CLI_ST_PRINT;