diff options
-rw-r--r-- | NEWS | 8 | ||||
-rw-r--r-- | lib/Makefile.am | 4 | ||||
-rw-r--r-- | lib/cert-cred-x509.c | 22 | ||||
-rw-r--r-- | lib/db.c | 30 | ||||
-rw-r--r-- | lib/errors.c | 2 | ||||
-rw-r--r-- | lib/ext/session_ticket.c | 141 | ||||
-rw-r--r-- | lib/gnutls_int.h | 36 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 6 | ||||
-rw-r--r-- | lib/includes/gnutls/x509.h | 6 | ||||
-rw-r--r-- | lib/libgnutls.map | 2 | ||||
-rw-r--r-- | lib/session_pack.c | 11 | ||||
-rw-r--r-- | lib/state.c | 3 | ||||
-rw-r--r-- | lib/stek.c | 357 | ||||
-rw-r--r-- | lib/stek.h | 38 | ||||
-rw-r--r-- | lib/tls13/session_ticket.c | 8 | ||||
-rw-r--r-- | lib/x509/verify-high.c | 2 | ||||
-rw-r--r-- | src/cli.c | 14 | ||||
-rw-r--r-- | tests/Makefile.am | 5 | ||||
-rwxr-xr-x | tests/gnutls-cli-invalid-crl.sh | 188 | ||||
-rwxr-xr-x | tests/gnutls-cli-self-signed.sh | 2 | ||||
-rw-r--r-- | tests/resume-with-previous-stek.c | 255 | ||||
-rw-r--r-- | tests/resume-with-stek-expiration.c | 326 | ||||
-rw-r--r-- | tests/resume.c | 12 |
23 files changed, 1362 insertions, 116 deletions
@@ -17,6 +17,11 @@ See the end for copying conditions. certificate is presented by client and the gnutls_init() flag GNUTLS_ENABLE_EARLY_START is specified. +** libgnutls: Added session ticket key rotation on server side with TOTP. + The key set with gnutls_session_ticket_enable_server() is used as a + master key to generate time-based keys for tickets. The rotation + relates to the gnutls_db_set_cache_expiration() period. + ** libgnutls: The 'record size limit' extension is added and preferred to the 'max record size' extension when possible. @@ -28,6 +33,7 @@ See the end for copying conditions. defined in RFC7250. This mechanism must be explicitly enabled via the GNUTLS_ENABLE_CERT_TYPE_NEG flag in gnutls_init(). +** gnutls-cli: enable CRL validation on startup (#564) ** API and ABI modifications: GNUTLS_ENABLE_EARLY_START: Added @@ -35,6 +41,8 @@ gnutls_record_set_max_early_data_size: Added gnutls_certificate_type_get2: Added gnutls_priority_certificate_type_list2: Added GNUTLS_ENABLE_CERT_TYPE_NEG: Added +GNUTLS_TL_FAIL_ON_INVALID_CRL: Added +GNUTLS_CERTIFICATE_VERIFY_CRLS: Added gnutls_ctype_target_t: New enumeration diff --git a/lib/Makefile.am b/lib/Makefile.am index 11de0a05bf..e101ec68f2 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -80,7 +80,7 @@ COBJECTS = range.c record.c compress.c debug.c cipher.c gthreads.h handshake-tls system-keys.h urls.c urls.h prf.c auto-verify.c dh-session.c \ cert-session.c handshake-checks.c dtls-sw.c dh-primes.c openpgp_compat.c \ crypto-selftests.c crypto-selftests-pk.c secrets.c extv.c extv.h \ - hello_ext_lib.c hello_ext_lib.h ocsp-api.c + hello_ext_lib.c hello_ext_lib.h ocsp-api.c stek.c if WINDOWS COBJECTS += system/keys-win.c @@ -122,7 +122,7 @@ HFILES = abstract_int.h debug.h cipher.h \ srp.h auth/srp_kx.h auth/srp_passwd.h \ file.h supplemental.h crypto.h random.h system.h\ locks.h mbuffers.h ecc.h pin.h fips.h \ - priority_options.h secrets.h + priority_options.h secrets.h stek.h if ENABLE_PKCS11 HFILES += pkcs11_int.h pkcs11x.h diff --git a/lib/cert-cred-x509.c b/lib/cert-cred-x509.c index 99a0b366e7..f342a420b5 100644 --- a/lib/cert-cred-x509.c +++ b/lib/cert-cred-x509.c @@ -1493,10 +1493,14 @@ gnutls_certificate_set_x509_crl_mem(gnutls_certificate_credentials_t res, const gnutls_datum_t * CRL, gnutls_x509_crt_fmt_t type) { -int ret; + unsigned flags = GNUTLS_TL_USE_IN_TLS; + int ret; + + if (res->flags & GNUTLS_CERTIFICATE_VERIFY_CRLS) + flags |= GNUTLS_TL_VERIFY_CRL|GNUTLS_TL_FAIL_ON_INVALID_CRL; ret = gnutls_x509_trust_list_add_trust_mem(res->tlist, NULL, CRL, - type, GNUTLS_TL_USE_IN_TLS, 0); + type, flags, 0); if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND) return 0; @@ -1526,6 +1530,10 @@ gnutls_certificate_set_x509_crl(gnutls_certificate_credentials_t res, { int ret, i, j; gnutls_x509_crl_t *new_crl = gnutls_malloc(crl_list_size * sizeof(gnutls_x509_crl_t)); + unsigned flags = GNUTLS_TL_USE_IN_TLS; + + if (res->flags & GNUTLS_CERTIFICATE_VERIFY_CRLS) + flags |= GNUTLS_TL_VERIFY_CRL|GNUTLS_TL_FAIL_ON_INVALID_CRL; if (!new_crl) return GNUTLS_E_MEMORY_ERROR; @@ -1546,7 +1554,7 @@ gnutls_certificate_set_x509_crl(gnutls_certificate_credentials_t res, ret = gnutls_x509_trust_list_add_crls(res->tlist, new_crl, - crl_list_size, GNUTLS_TL_USE_IN_TLS, 0); + crl_list_size, flags, 0); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -1582,10 +1590,14 @@ gnutls_certificate_set_x509_crl_file(gnutls_certificate_credentials_t res, const char *crlfile, gnutls_x509_crt_fmt_t type) { -int ret; + int ret; + unsigned flags = GNUTLS_TL_USE_IN_TLS; + + if (res->flags & GNUTLS_CERTIFICATE_VERIFY_CRLS) + flags |= GNUTLS_TL_VERIFY_CRL|GNUTLS_TL_FAIL_ON_INVALID_CRL; ret = gnutls_x509_trust_list_add_trust_file(res->tlist, NULL, crlfile, - type, GNUTLS_TL_USE_IN_TLS, 0); + type, flags, 0); if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND) return 0; @@ -260,12 +260,28 @@ int _gnutls_server_register_current_session(gnutls_session_t session) int _gnutls_check_resumed_params(gnutls_session_t session) { - if (session->internals.resumed_security_parameters.ext_master_secret != - session->security_parameters.ext_master_secret) - return gnutls_assert_val(GNUTLS_E_INVALID_SESSION); - - if (!_gnutls_server_name_matches_resumed(session)) - return gnutls_assert_val(GNUTLS_E_INVALID_SESSION); + time_t timestamp = gnutls_time(0); + const version_entry_st *vers; + + /* check whether the session is expired */ + if (timestamp - + session->internals.resumed_security_parameters.timestamp > + session->internals.expire_time + || session->internals.resumed_security_parameters.timestamp > + timestamp) + return gnutls_assert_val(GNUTLS_E_EXPIRED); + + /* check various parameters applicable to resumption in TLS1.2 or earlier + */ + vers = get_version(session); + if (!vers || !vers->tls13_sem) { + if (session->internals.resumed_security_parameters.ext_master_secret != + session->security_parameters.ext_master_secret) + return gnutls_assert_val(GNUTLS_E_INVALID_SESSION); + + if (!_gnutls_server_name_matches_resumed(session)) + return gnutls_assert_val(GNUTLS_E_INVALID_SESSION); + } return 0; } @@ -311,7 +327,6 @@ _gnutls_server_restore_session(gnutls_session_t session, return GNUTLS_E_INVALID_SESSION; } - /* expiration check is performed inside */ ret = gnutls_session_set_data(session, data.data, data.size); gnutls_free(data.data); @@ -320,6 +335,7 @@ _gnutls_server_restore_session(gnutls_session_t session, return ret; } + /* expiration check is performed inside */ ret = _gnutls_check_resumed_params(session); if (ret < 0) return gnutls_assert_val(ret); diff --git a/lib/errors.c b/lib/errors.c index c4bc8c72b9..fb6b54b4b0 100644 --- a/lib/errors.c +++ b/lib/errors.c @@ -165,6 +165,8 @@ static const gnutls_error_entry error_entries[] = { GNUTLS_E_CERTIFICATE_TIME_ERROR), ERROR_ENTRY(N_("Error in the certificate verification."), GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR), + ERROR_ENTRY(N_("Error in the CRL verification."), + GNUTLS_E_CRL_VERIFICATION_ERROR), ERROR_ENTRY(N_("Error in the private key verification; seed doesn't match."), GNUTLS_E_PRIVKEY_VERIFICATION_ERROR), ERROR_ENTRY(N_("Could not authenticate peer."), diff --git a/lib/ext/session_ticket.c b/lib/ext/session_ticket.c index d2a15e5eb3..3eb63818b9 100644 --- a/lib/ext/session_ticket.c +++ b/lib/ext/session_ticket.c @@ -34,16 +34,9 @@ #include <hello_ext.h> #include <constate.h> #include <dtls.h> +#include "stek.h" #include "db.h" -/* They are restricted by TICKET_CIPHER_KEY_SIZE and TICKET_MAC_SECRET_SIZE */ -#define CIPHER GNUTLS_CIPHER_AES_256_CBC -#define IV_SIZE 16 -#define BLOCK_SIZE 16 - -#define MAC_ALGO GNUTLS_MAC_SHA1 -#define MAC_SIZE 20 /* HMAC-SHA1 */ - static int session_ticket_recv_params(gnutls_session_t session, const uint8_t * data, size_t data_size); @@ -70,23 +63,11 @@ const hello_ext_entry_st ext_mod_session_ticket = { .cannot_be_overriden = 1 }; -#define NAME_POS (0) -#define KEY_POS (TICKET_KEY_NAME_SIZE) -#define MAC_SECRET_POS (TICKET_KEY_NAME_SIZE+TICKET_CIPHER_KEY_SIZE) - typedef struct { uint8_t *session_ticket; int session_ticket_len; } session_ticket_ext_st; -struct ticket_st { - uint8_t key_name[TICKET_KEY_NAME_SIZE]; - uint8_t IV[IV_SIZE]; - uint8_t *encrypted_state; - uint16_t encrypted_state_len; - uint8_t mac[MAC_SIZE]; -}; - static void deinit_ticket(struct ticket_st *ticket) { @@ -111,9 +92,9 @@ unpack_ticket(const gnutls_datum_t *ticket_data, struct ticket_st *ticket) memcpy(ticket->key_name, data, TICKET_KEY_NAME_SIZE); data += TICKET_KEY_NAME_SIZE; - DECR_LEN(data_size, IV_SIZE); - memcpy(ticket->IV, data, IV_SIZE); - data += IV_SIZE; + DECR_LEN(data_size, TICKET_IV_SIZE); + memcpy(ticket->IV, data, TICKET_IV_SIZE); + data += TICKET_IV_SIZE; DECR_LEN(data_size, 2); ticket->encrypted_state_len = _gnutls_read_uint16(data); @@ -124,8 +105,8 @@ unpack_ticket(const gnutls_datum_t *ticket_data, struct ticket_st *ticket) DECR_LEN(data_size, ticket->encrypted_state_len); data += ticket->encrypted_state_len; - DECR_LEN(data_size, MAC_SIZE); - memcpy(ticket->mac, data, MAC_SIZE); + DECR_LEN(data_size, TICKET_MAC_SIZE); + memcpy(ticket->mac, data, TICKET_MAC_SIZE); ticket->encrypted_state = gnutls_malloc(ticket->encrypted_state_len); @@ -149,8 +130,8 @@ pack_ticket(const struct ticket_st *ticket, gnutls_datum_t *ticket_data) memcpy(p, ticket->key_name, TICKET_KEY_NAME_SIZE); p += TICKET_KEY_NAME_SIZE; - memcpy(p, ticket->IV, IV_SIZE); - p += IV_SIZE; + memcpy(p, ticket->IV, TICKET_IV_SIZE); + p += TICKET_IV_SIZE; _gnutls_write_uint16(ticket->encrypted_state_len, p); p += 2; @@ -158,7 +139,7 @@ pack_ticket(const struct ticket_st *ticket, gnutls_datum_t *ticket_data) memcpy(p, ticket->encrypted_state, ticket->encrypted_state_len); p += ticket->encrypted_state_len; - memcpy(p, ticket->mac, MAC_SIZE); + memcpy(p, ticket->mac, TICKET_MAC_SIZE); } static @@ -169,7 +150,7 @@ int digest_ticket(const gnutls_datum_t * key, struct ticket_st *ticket, uint16_t length16; int ret; - ret = _gnutls_mac_init(&digest_hd, mac_to_entry(MAC_ALGO), + ret = _gnutls_mac_init(&digest_hd, mac_to_entry(TICKET_MAC_ALGO), key->data, key->size); if (ret < 0) { gnutls_assert(); @@ -177,7 +158,7 @@ int digest_ticket(const gnutls_datum_t * key, struct ticket_st *ticket, } _gnutls_mac(&digest_hd, ticket->key_name, TICKET_KEY_NAME_SIZE); - _gnutls_mac(&digest_hd, ticket->IV, IV_SIZE); + _gnutls_mac(&digest_hd, ticket->IV, TICKET_IV_SIZE); length16 = _gnutls_conv_uint16(ticket->encrypted_state_len); _gnutls_mac(&digest_hd, &length16, 2); _gnutls_mac(&digest_hd, ticket->encrypted_state, @@ -193,50 +174,60 @@ _gnutls_decrypt_session_ticket(gnutls_session_t session, gnutls_datum_t *state) { cipher_hd_st cipher_hd; - gnutls_datum_t key, IV, mac_secret; - uint8_t cmac[MAC_SIZE]; + gnutls_datum_t IV; + gnutls_datum_t stek_key_name, stek_cipher_key, stek_mac_key; + uint8_t cmac[TICKET_MAC_SIZE]; struct ticket_st ticket; int ret; - /* If the key name of the ticket does not match the one that we - hold, issue a new ticket. */ - if (ticket_data->size < TICKET_KEY_NAME_SIZE || - memcmp(ticket_data->data, &session->key.session_ticket_key[NAME_POS], - TICKET_KEY_NAME_SIZE)) + /* callers must have that checked */ + assert(!(session->internals.flags & GNUTLS_NO_TICKETS)); + + /* Retrieve ticket decryption keys */ + if (_gnutls_get_session_ticket_decryption_key(session, + ticket_data, + &stek_key_name, + &stek_mac_key, + &stek_cipher_key) < 0) return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); ret = unpack_ticket(ticket_data, &ticket); if (ret < 0) return ret; + /* If the key name of the ticket does not match the one that is currently active, + issue a new ticket. */ + if (memcmp + (ticket.key_name, stek_key_name.data, + stek_key_name.size)) { + ret = GNUTLS_E_DECRYPTION_FAILED; + goto cleanup; + } + /* Check the integrity of ticket */ - mac_secret.data = (void *) &session->key.session_ticket_key[MAC_SECRET_POS]; - mac_secret.size = TICKET_MAC_SECRET_SIZE; - ret = digest_ticket(&mac_secret, &ticket, cmac); + ret = digest_ticket(&stek_mac_key, &ticket, cmac); if (ret < 0) { gnutls_assert(); goto cleanup; } - if (memcmp(ticket.mac, cmac, MAC_SIZE)) { + if (memcmp(ticket.mac, cmac, TICKET_MAC_SIZE)) { ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); goto cleanup; } - if (ticket.encrypted_state_len % BLOCK_SIZE != 0) { + if (ticket.encrypted_state_len % TICKET_BLOCK_SIZE != 0) { ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); goto cleanup; } /* Decrypt encrypted_state */ - key.data = (void *) &session->key.session_ticket_key[KEY_POS]; - key.size = TICKET_CIPHER_KEY_SIZE; IV.data = ticket.IV; - IV.size = IV_SIZE; + IV.size = TICKET_IV_SIZE; ret = _gnutls_cipher_init(&cipher_hd, - cipher_to_entry(CIPHER), - &key, &IV, 0); + cipher_to_entry(TICKET_CIPHER), + &stek_cipher_key, &IV, 0); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -272,32 +263,39 @@ _gnutls_encrypt_session_ticket(gnutls_session_t session, gnutls_datum_t *ticket_data) { cipher_hd_st cipher_hd; - gnutls_datum_t key, IV; + gnutls_datum_t IV; gnutls_datum_t encrypted_state = {NULL,0}; - uint8_t iv[IV_SIZE]; - gnutls_datum_t mac_secret; + uint8_t iv[TICKET_IV_SIZE]; + gnutls_datum_t stek_cipher_key, stek_mac_key, stek_key_name; struct ticket_st ticket; int ret; - encrypted_state.size = ((state->size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE; - ticket_data->size = TICKET_KEY_NAME_SIZE + IV_SIZE + 2 + - encrypted_state.size + MAC_SIZE; + encrypted_state.size = ((state->size + TICKET_BLOCK_SIZE - 1) / TICKET_BLOCK_SIZE) * TICKET_BLOCK_SIZE; + ticket_data->size = TICKET_KEY_NAME_SIZE + TICKET_IV_SIZE + 2 + + encrypted_state.size + TICKET_MAC_SIZE; ticket_data->data = gnutls_calloc(1, ticket_data->size); if (!ticket_data->data) { gnutls_assert(); ret = GNUTLS_E_MEMORY_ERROR; goto cleanup; } - encrypted_state.data = ticket_data->data + TICKET_KEY_NAME_SIZE + IV_SIZE + 2; + encrypted_state.data = ticket_data->data + TICKET_KEY_NAME_SIZE + TICKET_IV_SIZE + 2; memcpy(encrypted_state.data, state->data, state->size); + /* Retrieve ticket encryption keys */ + if (_gnutls_get_session_ticket_encryption_key(session, + &stek_key_name, + &stek_mac_key, + &stek_cipher_key) < 0) { + ret = GNUTLS_E_ENCRYPTION_FAILED; + goto cleanup; + } + /* Encrypt state */ - key.data = (void *) &session->key.session_ticket_key[KEY_POS]; - key.size = TICKET_CIPHER_KEY_SIZE; IV.data = iv; - IV.size = IV_SIZE; + IV.size = TICKET_IV_SIZE; - ret = gnutls_rnd(GNUTLS_RND_NONCE, iv, IV_SIZE); + ret = gnutls_rnd(GNUTLS_RND_NONCE, iv, TICKET_IV_SIZE); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -305,8 +303,8 @@ _gnutls_encrypt_session_ticket(gnutls_session_t session, ret = _gnutls_cipher_init(&cipher_hd, - cipher_to_entry(CIPHER), - &key, &IV, 1); + cipher_to_entry(TICKET_CIPHER), + &stek_cipher_key, &IV, 1); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -321,14 +319,12 @@ _gnutls_encrypt_session_ticket(gnutls_session_t session, /* Fill the ticket structure to compute MAC. */ - memcpy(ticket.key_name, &session->key.session_ticket_key[NAME_POS], TICKET_KEY_NAME_SIZE); + memcpy(ticket.key_name, stek_key_name.data, stek_key_name.size); memcpy(ticket.IV, IV.data, IV.size); ticket.encrypted_state_len = encrypted_state.size; ticket.encrypted_state = encrypted_state.data; - mac_secret.data = &session->key.session_ticket_key[MAC_SECRET_POS]; - mac_secret.size = TICKET_MAC_SECRET_SIZE; - ret = digest_ticket(&mac_secret, &ticket, ticket.mac); + ret = digest_ticket(&stek_mac_key, &ticket, ticket.mac); if (ret < 0) { gnutls_assert(); goto cleanup2; @@ -353,7 +349,6 @@ static int unpack_session(gnutls_session_t session, const gnutls_datum_t *state) { int ret; - time_t timestamp = gnutls_time(0); if (unlikely(!state)) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); @@ -362,13 +357,6 @@ unpack_session(gnutls_session_t session, const gnutls_datum_t *state) if (ret < 0) return gnutls_assert_val(ret); - if (timestamp - - session->internals.resumed_security_parameters.timestamp > - session->internals.expire_time - || session->internals.resumed_security_parameters.timestamp > - timestamp) - return gnutls_assert_val(GNUTLS_E_EXPIRED); - ret = _gnutls_check_resumed_params(session); if (ret < 0) return gnutls_assert_val(ret); @@ -606,12 +594,17 @@ int gnutls_session_ticket_enable_server(gnutls_session_t session, const gnutls_datum_t * key) { - if (!session || !key || key->size != TICKET_MASTER_KEY_SIZE) { + int ret; + + if (!session || !key || key->size != TICKET_MASTER_KEY_SIZE || !key->data) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } - memcpy(session->key.session_ticket_key, key->data, key->size); + ret = _gnutls_initialize_session_ticket_key_rotation(session, key); + if (ret < 0) + return gnutls_assert_val(ret); + session->internals.flags &= ~GNUTLS_NO_TICKETS; return 0; diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index f35d6df5f5..2574f4e420 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -157,6 +157,7 @@ typedef struct { /* expire time for resuming sessions */ #define DEFAULT_EXPIRE_TIME 21600 +#define STEK_ROTATION_PERIOD_PRODUCT 3 #define DEFAULT_HANDSHAKE_TIMEOUT_MS 40*1000 /* The EC group to be used when the extension @@ -490,6 +491,22 @@ typedef struct auth_cred_st { #define TICKET_CIPHER_KEY_SIZE 32 #define TICKET_MAC_SECRET_SIZE 16 +/* These are restricted by TICKET_CIPHER_KEY_SIZE and TICKET_MAC_SECRET_SIZE */ +#define TICKET_CIPHER GNUTLS_CIPHER_AES_256_CBC +#define TICKET_IV_SIZE 16 +#define TICKET_BLOCK_SIZE 16 + +#define TICKET_MAC_ALGO GNUTLS_MAC_SHA1 +#define TICKET_MAC_SIZE 20 /* HMAC-SHA1 */ + +struct ticket_st { + uint8_t key_name[TICKET_KEY_NAME_SIZE]; + uint8_t IV[TICKET_IV_SIZE]; + uint8_t *encrypted_state; + uint16_t encrypted_state_len; + uint8_t mac[TICKET_MAC_SIZE]; +}; + struct binder_data_st { const struct mac_entry_st *prf; /* non-null if this struct is set */ gnutls_datum_t psk; @@ -501,6 +518,10 @@ struct binder_data_st { uint8_t resumption; /* whether it is a resumption binder */ }; +typedef void (* gnutls_stek_rotation_callback_t) (const gnutls_datum_t *prev_key, + const gnutls_datum_t *new_key, + uint64_t t); + struct gnutls_key_st { struct { /* These are kept outside the TLS1.3 union as they are * negotiated via extension, even before protocol is negotiated */ @@ -572,8 +593,13 @@ struct gnutls_key_st { /* TLS pre-master key; applies to 1.2 and 1.3 */ gnutls_datum_t key; - /* The key to encrypt and decrypt session tickets */ - uint8_t session_ticket_key[TICKET_MASTER_KEY_SIZE]; + uint8_t + /* The key to encrypt and decrypt session tickets */ + session_ticket_key[TICKET_MASTER_KEY_SIZE], + /* Static buffer for the previous key, whenever we need it */ + previous_ticket_key[TICKET_MASTER_KEY_SIZE], + /* Initial key supplied by the caller */ + initial_stek[TICKET_MASTER_KEY_SIZE]; /* this is used to hold the peers authentication data */ @@ -586,6 +612,12 @@ struct gnutls_key_st { int auth_info_size; /* needed in order to store to db for restoring */ auth_cred_st *cred; /* used to specify keys/certificates etc */ + + struct { + uint64_t last_result; + uint8_t was_rotated; + gnutls_stek_rotation_callback_t cb; + } totp; }; typedef struct gnutls_key_st gnutls_key_st; diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 2ee3e4fc83..49990b5f50 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -1928,13 +1928,16 @@ gnutls_certificate_get_verify_flags(gnutls_certificate_credentials_t res); * @GNUTLS_CERTIFICATE_API_V2: If set the gnutls_certificate_set_*key* functions will return an index of the added key pair instead of zero. * @GNUTLS_CERTIFICATE_SKIP_OCSP_RESPONSE_CHECK: If set, the gnutls_certificate_set_ocsp_status_request_file * function, will not check whether the response set matches any of the certificates. + * @GNUTLS_CERTIFICATE_VERIFY_CRLS: This will enable CRL verification when added in the certificate structure. + * When used, it requires CAs to be added before CRLs. * * Enumeration of different certificate credentials flags. */ typedef enum gnutls_certificate_flags { GNUTLS_CERTIFICATE_SKIP_KEY_CERT_MATCH = 1, GNUTLS_CERTIFICATE_API_V2 = (1<<1), - GNUTLS_CERTIFICATE_SKIP_OCSP_RESPONSE_CHECK = (1<<2) + GNUTLS_CERTIFICATE_SKIP_OCSP_RESPONSE_CHECK = (1<<2), + GNUTLS_CERTIFICATE_VERIFY_CRLS = (1<<3) } gnutls_certificate_flags; void gnutls_certificate_set_flags(gnutls_certificate_credentials_t, @@ -3214,6 +3217,7 @@ void gnutls_fips140_set_mode(gnutls_fips_mode_t mode, unsigned flags); #define GNUTLS_E_NO_COMMON_KEY_SHARE -423 #define GNUTLS_E_REAUTH_REQUEST -424 #define GNUTLS_E_TOO_MANY_MATCHES -425 +#define GNUTLS_E_CRL_VERIFICATION_ERROR -426 #define GNUTLS_E_UNIMPLEMENTED_FEATURE -1250 diff --git a/lib/includes/gnutls/x509.h b/lib/includes/gnutls/x509.h index cd54e8c4ca..1573577d96 100644 --- a/lib/includes/gnutls/x509.h +++ b/lib/includes/gnutls/x509.h @@ -1562,6 +1562,8 @@ int gnutls_x509_trust_list_get_issuer_by_subject_key_id(gnutls_x509_trust_list_t * @GNUTLS_TL_GET_COPY: The semantics of this flag are documented to the functions which * are applicable. In general, on returned value, the function will provide a copy * if this flag is provided, rather than a pointer to internal data. + * @GNUTLS_TL_FAIL_ON_INVALID_CRL: If an CRL is added which cannot be validated return + * an error instead of ignoring (must be used with %GNUTLS_TL_VERIFY_CRL). * * Enumeration of different certificate trust list flags. */ @@ -1574,8 +1576,10 @@ typedef enum gnutls_trust_list_flags_t { #define GNUTLS_TL_NO_DUPLICATES (1<<2) GNUTLS_TL_NO_DUPLICATE_KEY = (1<<3), #define GNUTLS_TL_NO_DUPLICATE_KEY (1<<3) - GNUTLS_TL_GET_COPY = (1<<4) + GNUTLS_TL_GET_COPY = (1<<4), #define GNUTLS_TL_GET_COPY (1<<4) + GNUTLS_TL_FAIL_ON_INVALID_CRL = (1<<5) +#define GNUTLS_TL_FAIL_ON_INVALID_CRL (1<<5) } gnutls_trust_list_flags_t; int diff --git a/lib/libgnutls.map b/lib/libgnutls.map index dd77025f07..041fda7b80 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1324,4 +1324,6 @@ GNUTLS_PRIVATE_3_4 { # Internal symbols needed by tests/name-constraints-merge: _gnutls_x509_name_constraints_merge; _gnutls_server_name_set_raw; + # Internal symbols needed by tests/suite/resume-with-stek-expiration + _gnutls_set_session_ticket_key_rotation_callback; } GNUTLS_3_4; diff --git a/lib/session_pack.c b/lib/session_pack.c index f8b1830568..c5801fb32e 100644 --- a/lib/session_pack.c +++ b/lib/session_pack.c @@ -960,8 +960,6 @@ unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps) session->internals.resumed_security_parameters.timestamp = timestamp; - timestamp = gnutls_time(0); - BUFFER_POP_NUM(ps, session->internals.resumed_security_parameters. entity); @@ -1071,15 +1069,6 @@ unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps) } } - if (timestamp - - session->internals.resumed_security_parameters.timestamp > - session->internals.expire_time - || session->internals.resumed_security_parameters.timestamp > - timestamp) { - gnutls_assert(); - return GNUTLS_E_EXPIRED; - } - ret = 0; error: diff --git a/lib/state.c b/lib/state.c index 58db8f9a32..86edd3c4c4 100644 --- a/lib/state.c +++ b/lib/state.c @@ -491,6 +491,9 @@ int gnutls_init(gnutls_session_t * session, unsigned int flags) (*session)->internals.expire_time = DEFAULT_EXPIRE_TIME; + /* Ticket key rotation - set the default X to 3 times the ticket expire time */ + (*session)->key.totp.last_result = 0; + gnutls_handshake_set_max_packet_length((*session), MAX_HANDSHAKE_PACKET_SIZE); diff --git a/lib/stek.c b/lib/stek.c new file mode 100644 index 0000000000..2248e51b6f --- /dev/null +++ b/lib/stek.c @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2018 Free Software Foundation, Inc. + * + * Author: Ander Juaristi + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ +#include "gnutls_int.h" +#include "stek.h" + +#define NAME_POS (0) +#define KEY_POS (TICKET_KEY_NAME_SIZE) +#define MAC_SECRET_POS (TICKET_KEY_NAME_SIZE+TICKET_CIPHER_KEY_SIZE) + +static int totp_sha3(gnutls_session_t session, + uint64_t t, + const gnutls_datum_t *secret, + uint8_t out[TICKET_MASTER_KEY_SIZE]) +{ + int retval; + uint8_t t_be[8]; + digest_hd_st hd; + /* + * We choose SHA3-512 because it outputs 64 bytes, + * just the same length as the ticket key. + */ + const gnutls_digest_algorithm_t algo = GNUTLS_DIG_SHA3_512; +#if TICKET_MASTER_KEY_SIZE != 64 +#error "TICKET_MASTER_KEY_SIZE must be 64 bytes" +#endif + + if (unlikely(secret == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + if ((retval = _gnutls_hash_init(&hd, hash_to_entry(algo))) < 0) + return gnutls_assert_val(retval); + + _gnutls_write_uint64(t, t_be); + + if ((retval = _gnutls_hash(&hd, t_be, sizeof(t_be))) < 0) + return gnutls_assert_val(retval); + if ((retval = _gnutls_hash(&hd, secret->data, secret->size)) < 0) + return gnutls_assert_val(retval); + + _gnutls_hash_deinit(&hd, out); + return GNUTLS_E_SUCCESS; +} + +static uint64_t T(gnutls_session_t session, time_t t) +{ + uint64_t numeral = t; + unsigned int x = session->internals.expire_time * STEK_ROTATION_PERIOD_PRODUCT; + + if (numeral <= 0) + return 0; + + return (numeral / x); +} + +static int64_t totp_next(gnutls_session_t session) +{ + time_t t; + uint64_t result; + + t = gnutls_time(NULL); + if (unlikely(t == (time_t) -1)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + result = T(session, t); + if (result == 0) + return 0; + + if (result == session->key.totp.last_result) + return 0; + + return result; +} + +static int64_t totp_previous(gnutls_session_t session) +{ + uint64_t result; + + if (session->key.totp.last_result == 0) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + if (!session->key.totp.was_rotated) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + result = session->key.totp.last_result - 1; + if (result == 0) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + return result; +} + +static void call_rotation_callback(gnutls_session_t session, + uint8_t key[TICKET_MASTER_KEY_SIZE], uint64_t t) +{ + gnutls_datum_t prev_key, new_key; + + if (session->key.totp.cb) { + new_key.data = key; + new_key.size = TICKET_MASTER_KEY_SIZE; + prev_key.data = session->key.session_ticket_key; + prev_key.size = TICKET_MASTER_KEY_SIZE; + + session->key.totp.cb(&prev_key, &new_key, t); + } +} + +static int rotate(gnutls_session_t session) +{ + int64_t t; + gnutls_datum_t secret; + uint8_t key[TICKET_MASTER_KEY_SIZE]; + + /* Do we need to calculate new totp? */ + t = totp_next(session); + if (t > 0) { + secret.data = session->key.initial_stek; + secret.size = TICKET_MASTER_KEY_SIZE; + + /* Generate next key */ + if (totp_sha3(session, t, &secret, key) < 0) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + /* Replace old key with new one, and call callback if provided */ + call_rotation_callback(session, key, t); + session->key.totp.last_result = t; + memcpy(session->key.session_ticket_key, key, sizeof(key)); + + session->key.totp.was_rotated = 1; + } else if (t < 0) { + return gnutls_assert_val(t); + } + + return GNUTLS_E_SUCCESS; +} + +static int rotate_back_and_peek(gnutls_session_t session, + uint8_t key[TICKET_MASTER_KEY_SIZE]) +{ + int64_t t; + gnutls_datum_t secret; + + /* Get the previous TOTP */ + t = totp_previous(session); + if (t < 0) + return gnutls_assert_val(t); + + secret.data = session->key.initial_stek; + secret.size = TICKET_MASTER_KEY_SIZE; + + if (totp_sha3(session, t, &secret, key) < 0) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + return 0; +} + +/* + * _gnutls_get_session_ticket_encryption_key: + * @key_name: an empty datum that will receive the key name part of the STEK + * @mac_key: an empty datum that will receive the MAC key part of the STEK + * @enc_key: an empty datum that will receive the encryption key part of the STEK + * + * Get the currently active session ticket encryption key (STEK). + * + * The STEK is a 64-byte blob which is further divided in three parts, + * and this function requires the caller to supply three separate datums for each one. + * Though the caller might omit one or more of those if not interested in that part of the STEK. + * + * These are the three parts the STEK is divided in: + * + * - Key name: 16 bytes + * - Encryption key: 32 bytes + * - MAC key: 16 bytes + * + * This function will transparently rotate the key, if the time has come for that, + * before returning it to the caller. + */ +int _gnutls_get_session_ticket_encryption_key(gnutls_session_t session, + gnutls_datum_t *key_name, + gnutls_datum_t *mac_key, + gnutls_datum_t *enc_key) +{ + int retval; + + if (unlikely(session == NULL)) { + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; + } + + if ((retval = rotate(session)) < 0) + return gnutls_assert_val(retval); + + /* Copy key parts to user-supplied datums (if provided) */ + if (key_name) { + key_name->data = &session->key.session_ticket_key[NAME_POS]; + key_name->size = TICKET_KEY_NAME_SIZE; + } + if (mac_key) { + mac_key->data = &session->key.session_ticket_key[MAC_SECRET_POS]; + mac_key->size = TICKET_MAC_SECRET_SIZE; + } + if (enc_key) { + enc_key->data = &session->key.session_ticket_key[KEY_POS]; + enc_key->size = TICKET_CIPHER_KEY_SIZE; + } + + return retval; +} + +/* + * _gnutls_get_session_ticket_decryption_key: + * @ticket_data: the bytes of a session ticket that must be decrypted + * @key_name: an empty datum that will receive the key name part of the STEK + * @mac_key: an empty datum that will receive the MAC key part of the STEK + * @enc_key: an empty datum that will receive the encryption key part of the STEK + * + * Get the key (STEK) the given session ticket was encrypted with. + * + * As with its encryption counterpart (%_gnutls_get_session_ticket_encryption_key), + * this function will also transparently rotate + * the currently active STEK if time has come for that, and it also requires the different + * parts of the STEK to be obtained in different datums. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or a negative error code, such as + * %GNUTLS_E_REQUSTED_DATA_NOT_AVAILABLE if no key could be found for the supplied ticket. + */ +int _gnutls_get_session_ticket_decryption_key(gnutls_session_t session, + const gnutls_datum_t *ticket_data, + gnutls_datum_t *key_name, + gnutls_datum_t *mac_key, + gnutls_datum_t *enc_key) +{ + int retval; + gnutls_datum_t key = { + .data = session->key.session_ticket_key, + .size = TICKET_MASTER_KEY_SIZE + }; + + if (unlikely(session == NULL || ticket_data == NULL || ticket_data->data == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + if (ticket_data->size < TICKET_KEY_NAME_SIZE) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + if ((retval = rotate(session)) < 0) + return gnutls_assert_val(retval); + + /* + * Is current key valid? + * We compare the first 16 bytes --> The key_name field. + */ + if (memcmp(ticket_data->data, + &key.data[NAME_POS], + TICKET_KEY_NAME_SIZE) == 0) + goto key_found; + + key.size = TICKET_MASTER_KEY_SIZE; + key.data = session->key.previous_ticket_key; + + /* + * Current key is not valid. + * Compute previous key and see if that matches. + */ + if ((retval = rotate_back_and_peek(session, key.data)) < 0) + return gnutls_assert_val(retval); + + if (memcmp(ticket_data->data, + &key.data[NAME_POS], + TICKET_KEY_NAME_SIZE) == 0) + goto key_found; + + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + +key_found: + if (key_name) { + key_name->data = &key.data[NAME_POS]; + key_name->size = TICKET_KEY_NAME_SIZE; + } + if (mac_key) { + mac_key->data = &key.data[MAC_SECRET_POS]; + mac_key->size = TICKET_MAC_SECRET_SIZE; + } + if (enc_key) { + enc_key->data = &key.data[KEY_POS]; + enc_key->size = TICKET_CIPHER_KEY_SIZE; + } + + return GNUTLS_E_SUCCESS; +} + +/* + * _gnutls_initialize_session_ticket_key_rotation: + * @key: Initial session ticket key + * + * Initialize the session ticket key rotation. + * + * This function will not enable session ticket keys on the server side. That is done + * with the gnutls_session_ticket_enable_server() function. This function just initializes + * the internal state to support periodical rotation of the session ticket encryption key. + * + * Returns: %GNUTLS_E_SUCCESS (0) on success, or %GNUTLS_E_INVALID_REQUEST on error. + */ +int _gnutls_initialize_session_ticket_key_rotation(gnutls_session_t session, const gnutls_datum_t *key) +{ + if (unlikely(session == NULL || key == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + if (session->key.totp.last_result == 0) { + int64_t t; + memcpy(session->key.initial_stek, key->data, key->size); + t = totp_next(session); + if (t < 0) + return gnutls_assert_val(t); + + session->key.totp.last_result = t; + session->key.totp.was_rotated = 0; + + return GNUTLS_E_SUCCESS; + } + + return GNUTLS_E_INVALID_REQUEST; +} + +/* + * _gnutls_set_session_ticket_key_rotation_callback: + * @cb: the callback function + * + * Set a callback function that will be invoked every time the session ticket key + * is rotated. + * + * The function will take as arguments the previous key, the new key and the time + * step value that caused the key to rotate. + * + */ +void _gnutls_set_session_ticket_key_rotation_callback(gnutls_session_t session, gnutls_stek_rotation_callback_t cb) +{ + if (session) + session->key.totp.cb = cb; +} diff --git a/lib/stek.h b/lib/stek.h new file mode 100644 index 0000000000..bec781edf0 --- /dev/null +++ b/lib/stek.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2018 Free Software Foundation, Inc. + * + * Author: Ander Juaristi + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ +#include "gnutls_int.h" + +int _gnutls_get_session_ticket_encryption_key(gnutls_session_t session, + gnutls_datum_t *key_name, + gnutls_datum_t *mac_key, + gnutls_datum_t *enc_key); +int _gnutls_get_session_ticket_decryption_key(gnutls_session_t session, + const gnutls_datum_t *ticket_data, + gnutls_datum_t *key_name, + gnutls_datum_t *mac_key, + gnutls_datum_t *enc_key); + +void _gnutls_set_session_ticket_key_rotation_callback(gnutls_session_t session, + gnutls_stek_rotation_callback_t cb); + +int _gnutls_initialize_session_ticket_key_rotation(gnutls_session_t session, + const gnutls_datum_t *key); diff --git a/lib/tls13/session_ticket.c b/lib/tls13/session_ticket.c index 8087ba7a8b..36d5dc5260 100644 --- a/lib/tls13/session_ticket.c +++ b/lib/tls13/session_ticket.c @@ -31,6 +31,7 @@ #include "auth/cert.h" #include "tls13/session_ticket.h" #include "session_pack.h" +#include "db.h" static int pack_ticket(gnutls_session_t session, tls13_ticket_t *ticket, gnutls_datum_t *packed) @@ -422,9 +423,12 @@ int _gnutls13_unpack_session_ticket(gnutls_session_t session, /* Return ticket parameters */ ret = unpack_ticket(session, &decrypted, ticket_data); _gnutls_free_datum(&decrypted); - if (ret < 0) { + if (ret < 0) return ret; - } + + ret = _gnutls_check_resumed_params(session); + if (ret < 0) + return gnutls_assert_val(ret); return 0; } diff --git a/lib/x509/verify-high.c b/lib/x509/verify-high.c index 03c63c2687..7f640f1a13 100644 --- a/lib/x509/verify-high.c +++ b/lib/x509/verify-high.c @@ -749,6 +749,8 @@ gnutls_x509_trust_list_add_crls(gnutls_x509_trust_list_t list, _gnutls_debug_log("CRL verification failed, not adding it\n"); if (flags & GNUTLS_TL_NO_DUPLICATES) gnutls_x509_crl_deinit(crl_list[i]); + if (flags & GNUTLS_TL_FAIL_ON_INVALID_CRL) + return gnutls_assert_val(GNUTLS_E_CRL_VERIFICATION_ERROR); continue; } } @@ -881,7 +881,8 @@ static bool parse_for_inline_commands_in_buffer(char *buffer, size_t bytes, inline_cmds_st * inline_cmds) { - ssize_t local_bytes, match_bytes, prev_bytes_copied, ii, jj; + ssize_t local_bytes, match_bytes, prev_bytes_copied, ii; + unsigned jj; char *local_buffer_ptr, *ptr; char inline_command_string[MAX_INLINE_COMMAND_BYTES]; ssize_t l; @@ -1765,6 +1766,7 @@ static void init_global_tls_stuff(void) gnutls_certificate_set_pin_function(xcred, pin_callback, NULL); gnutls_certificate_set_verify_flags(xcred, global_vflags); + gnutls_certificate_set_flags(xcred, GNUTLS_CERTIFICATE_VERIFY_CRLS); if (x509_cafile != NULL) { ret = gnutls_certificate_set_x509_trust_file(xcred, @@ -1773,12 +1775,17 @@ static void init_global_tls_stuff(void) } else { if (insecure == 0) { ret = gnutls_certificate_set_x509_system_trust(xcred); + if (ret == GNUTLS_E_UNIMPLEMENTED_FEATURE) { + fprintf(stderr, "Warning: this system doesn't support a default trust store\n"); + ret = 0; + } } else { ret = 0; } } if (ret < 0) { - fprintf(stderr, "Error setting the x509 trust file\n"); + fprintf(stderr, "Error setting the x509 trust file: %s\n", gnutls_strerror(ret)); + exit(1); } else { printf("Processed %d CA certificate(s).\n", ret); } @@ -1790,7 +1797,8 @@ static void init_global_tls_stuff(void) x509ctype); if (ret < 0) { fprintf(stderr, - "Error setting the x509 CRL file\n"); + "Error setting the x509 CRL file: %s\n", gnutls_strerror(ret)); + exit(1); } else { printf("Processed %d CRL(s).\n", ret); } diff --git a/tests/Makefile.am b/tests/Makefile.am index 90e0fd71a1..3680437f16 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -204,7 +204,8 @@ ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniquei ip-check mini-x509-ipaddr trust-store base64-raw random-art dhex509self \ dss-sig-val sign-pk-api tls-session-ext-override record-pad \ tls13-server-kx-neg gnutls_ext_raw_parse_dtls key-export-pkcs8 \ - null_retrieve_function tls-record-size-limit tls-crt_type-neg + null_retrieve_function tls-record-size-limit tls-crt_type-neg \ + resume-with-stek-expiration resume-with-previous-stek if HAVE_SECCOMP_TESTS ctests += dtls-with-seccomp tls-with-seccomp dtls-client-with-seccomp tls-client-with-seccomp @@ -436,7 +437,7 @@ dist_check_SCRIPTS += fastopen.sh pkgconfig.sh starttls.sh starttls-ftp.sh start psktool.sh ocsp-tests/ocsp-load-chain gnutls-cli-save-data.sh gnutls-cli-debug.sh \ sni-resume.sh ocsp-tests/ocsptool cert-reencoding.sh pkcs7-cat.sh long-crl.sh -dist_check_SCRIPTS += gnutls-cli-self-signed.sh +dist_check_SCRIPTS += gnutls-cli-self-signed.sh gnutls-cli-invalid-crl.sh if ENABLE_PKCS11 dist_check_SCRIPTS += p11-kit-trust.sh testpkcs11.sh certtool-pkcs11.sh diff --git a/tests/gnutls-cli-invalid-crl.sh b/tests/gnutls-cli-invalid-crl.sh new file mode 100755 index 0000000000..7b285c4264 --- /dev/null +++ b/tests/gnutls-cli-invalid-crl.sh @@ -0,0 +1,188 @@ +#!/bin/sh + +# Copyright (C) 2018 Red Hat, Inc. +# +# Author: Nikos Mavrogiannopoulos +# +# This file is part of GnuTLS. +# +# GnuTLS is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 3 of the License, or (at +# your option) any later version. +# +# GnuTLS is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/> + +srcdir="${srcdir:-.}" +SERV="${SERV:-../src/gnutls-serv${EXEEXT}}" +CLI="${CLI:-../src/gnutls-cli${EXEEXT}}" +unset RETCODE +TMPFILE=crl-inv.$$.pem.tmp +CAFILE=crl-inv-ca.$$.pem.tmp +CRLFILE=crl-inv-crl.$$.pem.tmp + +if ! test -x "${SERV}"; then + exit 77 +fi + +if ! test -x "${CLI}"; then + exit 77 +fi + +if test "${WINDIR}" != ""; then + exit 77 +fi + +if ! test -z "${VALGRIND}"; then + VALGRIND="${LIBTOOL:-libtool} --mode=execute ${VALGRIND} --error-exitcode=15" +fi + +SERV="${SERV} -q" + +. "${srcdir}/scripts/common.sh" + +check_for_datefudge + +echo "Checking whether connecting to a server but with an invalid CRL provided, returns the expected error" + +cat <<__EOF__ >${TMPFILE} +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAxfNimQ1uOFXUSVCm0lBems4HpfLkW1Ykf5qLd9kdoHte7YAs +BHjFPaPSdXitYI36YMwqVcXT6RDJa0mcAV3QmMMxAnpKq7LIDVC9BNgjc7Dq5ou5 +X2wNKrs3ygqg6HR87nJaw9TFqKetoP9mX37igBc2QWg5Fx6/Gem57hwD+mBEs+Hv +jd7q4wDlLaNS/165DBECr5dDUAIVr0bh0+1s/rDzIpjuq1qtN7b0C1rmWlsyphYu +aYm07X7x6hZcjvAoM3w5FLzbOnS6QrBeQOc2J6VBHqaHMKEVc+Dwt+Ggn0De0QCK +ucQRUCO5DQDUZnVLZhUpObvm1cBvQd5Db15IiwIDAQABAoIBACUSqhqkC0p9uJ5q +fnPRHYa8o24PCXmZrog/d3NgtE3EDUlJwfSscbRTpCzgBwiYTpYmZp9dx4xU2oQ/ +avpOiayykdE2+hkiCJmFz4DCwhD+x1+aN4OJhwXDvnUWfIBMoME/pYQbq1Ek5j3K +1293IhB/SGgDjv2ngn7l6S6RDKWtYZry61oMEoVRy96FJ+88o5khlvfWE2zF2+M6 +T2qFbzO29oq++cDSIlgm9eSN6gG5uzZcxqTapEvWrRdKZfEqcyGJuysQbShrASvI +GvJclewdnguBW2+X/bwABSEaG7AdPZJdfQJayk97gKJ8xpFZLY9auub7O/0z1CJi +lFsj4LECgYEA5TY8Z73ODtR87HEE3uUqiix4wPO4yJXWfZUwxNAyet2Jx5e5HYvL +iEkbZdadlKtSoPTnVSu6OZxhWZVBS5WoxxijBneDvh7I6gN8eVtch9EJVmJig6Eg +kHTo5Z2ZwheGe/RxB3ml3IT2IAdr5+QE6CfVBNA0fzVTItCLgO3YI/8CgYEA3RXZ +yskckcbCr1rceRmQ8CPbKg1bWGujLMpTILW0/Ii51PMredyG3E063G4kbMOFRmVj +eI5AFgZX7w5N4vjaf8PbOhsqrQvQ/UglB1fD0tLX8LgF9xwh7P1Y4VLHFMEGJUy1 +PEGVCT0FIe2REGxAmyELaP8SSvW8fGjXJSp2K3UCgYBSlq5BOxTKJyo0D60Pm0cu +rkN8UtUcAVFdwqnl4Javyq9gaXzb9okJvD3Q/fmdnfWR5WyNNcpOA9jX7H2wfGZq +BqiHJf0kPfdqyoLJP3Ahx+IzbBPPFfmj01wvkA/c7ZkZhMRNSznGMWp1s/bfgTt7 +Yw7QQy0HQPGJs9bwR8L/hQKBgQCXFvvEbjSsG12pYTsTN7mpo5d/4ajvgH//eDXf +QM7zVq1JLvYjTeaMX+s+Abe67NQEC/4ywWRiqOsnYGsyFkec0UjdKPu9TzoAHnHP +1tbpGVaiF+Fbw0ocH/fB5URQlqmQjB+/kkI8EguT6DsfMhvk6GxX0Rm7SL0LeMqv +h5lCkQKBgAR2U6cjbzJRhDyEOmUJH2keYHDwWUMx8ypvfhbPiPJyTC2sDcRrMrnO +WB3NtiB88aLFPjZ7sFZYE5plCESGkxK4Y21/UJHlw3I7X4JKYslE7dMq8Qzbv58r +23fZkHop4UJ1bHk7O4FRL3brU6KlIzZTOXzEeP+MRRehhwzkwpxf +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIDiTCCAkGgAwIBAgIUEOtG5aJHVFm4ARA8uv4bJ/OqL4YwPQYJKoZIhvcNAQEK +MDCgDTALBglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogMC +AUAwDzENMAsGA1UEAxMEQ0EtMDAgFw0xODA5MTgwNjQyMzdaGA85OTk5MTIzMTIz +NTk1OVowEzERMA8GA1UEAxMIc2VydmVyLTEwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDF82KZDW44VdRJUKbSUF6azgel8uRbViR/mot32R2ge17tgCwE +eMU9o9J1eK1gjfpgzCpVxdPpEMlrSZwBXdCYwzECekqrssgNUL0E2CNzsOrmi7lf +bA0quzfKCqDodHzuclrD1MWop62g/2ZffuKAFzZBaDkXHr8Z6bnuHAP6YESz4e+N +3urjAOUto1L/XrkMEQKvl0NQAhWvRuHT7Wz+sPMimO6rWq03tvQLWuZaWzKmFi5p +ibTtfvHqFlyO8CgzfDkUvNs6dLpCsF5A5zYnpUEepocwoRVz4PC34aCfQN7RAIq5 +xBFQI7kNANRmdUtmFSk5u+bVwG9B3kNvXkiLAgMBAAGjdzB1MAwGA1UdEwEB/wQC +MAAwFAYDVR0RBA0wC4IJbG9jYWxob3N0MA8GA1UdDwEB/wQFAwMHoAAwHQYDVR0O +BBYEFJVJTYVERYv5/qI31HwTDqATv4GRMB8GA1UdIwQYMBaAFBnn35UaLvLuW/YH +E3v2gKntMzNNMD0GCSqGSIb3DQEBCjAwoA0wCwYJYIZIAWUDBAIBoRowGAYJKoZI +hvcNAQEIMAsGCWCGSAFlAwQCAaIDAgFAA4IBAQCPVloFdhqdJqGjhxpl2Wv2ftD3 +w+IeHSqURyCeijUCIOkqMlA3085nuoULiJ0p1ryi8rRWOvNjRsRQ30/lnLsxfmMh +oVR+g9uq0YZcFqkeRL5aDTrfJZWFeVSqXuuJvhyw1el5hs4bDSahMFB+dx2G+3zX +Ycd4Sq3sXDkdLnfD9GSeVvvbzAb7Z7qD1cdh1HaEnX2fsXT69czsFiaTgknr3Vxc +P0yFZVNCT360EVsduLkLWnCqZYVWWDFUlut7SOwhsYUx2ZOoM4RuBy+uDF2PM8BP +BkgYEHeWFA31nnwBNePyvWrAZ1DguOvnETSMB/+8zDX3+teNZNNdTVTQ6ypQ +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDgTCCAjmgAwIBAgIUE8klaC2IZj3Tr2/jEVEiJGj8piYwPQYJKoZIhvcNAQEK +MDCgDTALBglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogMC +AUAwDzENMAsGA1UEAxMEQ0EtMDAgFw0xODA5MTgwNjQyMzdaGA85OTk5MTIzMTIz +NTk1OVowDzENMAsGA1UEAxMEQ0EtMDCCAVIwPQYJKoZIhvcNAQEKMDCgDTALBglg +hkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogMCAUADggEPADCC +AQoCggEBALUJWYFxTq3vWG+hZq8KsRe0YRf5pqftxR21uZ7MSr25Muo7/s69toZG +7SaV1ZFp2n+Njm96nRLDqCc7cnaPLpKeMBFI84pQOYMdJs2mxs7wrBvejTBpxw3f +o1L2cJWznXZwvDQd+iz3qt62kF53tjpUzQ0Cqn6AMU961+H99Tq39iONcAvmTYeT +Bf+P4jhg3h5cOkdhsB4zrr0ek0OdgSdHiTIWvmYbEvizwhBc8pLOc007FkslqlQ5 +b7Fplx/B+v/etqUoW7/742phxJhTjhRW75BWoCiQyhglwUfpDv0tXnMXousXdwaQ +Ao1EM1v/OCsYj/U2u10Bo/5y1q6Jjz8CAwEAAaNDMEEwDwYDVR0TAQH/BAUwAwEB +/zAPBgNVHQ8BAf8EBQMDBwQAMB0GA1UdDgQWBBQZ59+VGi7y7lv2BxN79oCp7TMz +TTA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAaEaMBgGCSqGSIb3DQEBCDAL +BglghkgBZQMEAgGiAwIBQAOCAQEAMAgvcHqmjz1Ox5USoup5pe6HWPKtOR5pVGX2 +1zAk1wq7GoTKvo5QA6HtNR0ex1A2//XhklAKcqsIv1ELEh/3K/L0dEuaN4Zs784e +zaP0g/Ax6X3ClrHgARA4FA6MtaQblezj+7Zfc6cg1gKtfYleiOoK/Q+kk6JxOYAH +Lz9MF/6bZ8mYJQv8DURSp2p5NVWSEjbQV5IG2dw/eknZtbFaN5b+db3eVtrK0ZeS +l1e3hTwopCLNoh4qHUW/qKl0l1Gt7kPPxAsRReOxdcb1Pv73iuK7w5wbPyyWp0kM +FQj9tqRIMQZIer3gaURWG8OZfntCAvtlSSwc1PjwLBXO9ZvNBw== +-----END CERTIFICATE----- +__EOF__ + +cat <<__EOF__ >${CRLFILE} +-----BEGIN X509 CRL----- +MIIB/TCBtgIBATA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAaEaMBgGCSqG +SIb3DQEBCDALBglghkgBZQMEAgGiAwIBQDAPMQ0wCwYDVQQDEwRDQS0wFw0xODA5 +MTgwNjQyMzdaFw0xOTA5MTgwNjQyMzdaMACgQTA/MB8GA1UdIwQYMBaAFBnn35Ua +LvLuW/YHE3v2gKntMzNNMBwGA1UdFAQVAhNboJ5dKaGvdv1Vo9o1XXTbeiMKMD0G +CSqGSIb3DQEBCjAwoA0wCwYJYIZIAWUDBAIBoRowGAYJKoZIhvcNAQEIMAsGCWCG +SAFlAwQCAaIDAgFAA4IBAQBgodBpVGTDHV4HBSgNPUnz7BH/BdRX1OPB8oYclDtv +l0xTzRR4qm/dMU3N3iH7vMk2y8U/TwD7NueyUnumt0vATTfjR2cle5lu2czksYsR +e4As9cI5cb4Sk+cf3/HyAVwnmZemTAA+cAJHkL6p7E+mSUoBVB6m8h8d6RH8jXmO +BXBE3z1xVITqahDdD6sLaR5jpnOtg/1nBAW8Hzr2p8tjEwhI8TCfZXbL9Q6fZtTr +apDrIx0D/G4hDKmmtQeY2q3RCOSJldg4YzUjjuhWs6BahHj9jDJpz02180ao7bda +eoNetNEqNvBvFvkO9gtgSzOzS34taiMpkIBwBbCNkm4p +-----END X509 CRL----- +__EOF__ + +cat <<__EOF__ >${CAFILE} +-----BEGIN CERTIFICATE----- +MIIDgTCCAjmgAwIBAgIUE8klaC2IZj3Tr2/jEVEiJGj8piYwPQYJKoZIhvcNAQEK +MDCgDTALBglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogMC +AUAwDzENMAsGA1UEAxMEQ0EtMDAgFw0xODA5MTgwNjQyMzdaGA85OTk5MTIzMTIz +NTk1OVowDzENMAsGA1UEAxMEQ0EtMDCCAVIwPQYJKoZIhvcNAQEKMDCgDTALBglg +hkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogMCAUADggEPADCC +AQoCggEBALUJWYFxTq3vWG+hZq8KsRe0YRf5pqftxR21uZ7MSr25Muo7/s69toZG +7SaV1ZFp2n+Njm96nRLDqCc7cnaPLpKeMBFI84pQOYMdJs2mxs7wrBvejTBpxw3f +o1L2cJWznXZwvDQd+iz3qt62kF53tjpUzQ0Cqn6AMU961+H99Tq39iONcAvmTYeT +Bf+P4jhg3h5cOkdhsB4zrr0ek0OdgSdHiTIWvmYbEvizwhBc8pLOc007FkslqlQ5 +b7Fplx/B+v/etqUoW7/742phxJhTjhRW75BWoCiQyhglwUfpDv0tXnMXousXdwaQ +Ao1EM1v/OCsYj/U2u10Bo/5y1q6Jjz8CAwEAAaNDMEEwDwYDVR0TAQH/BAUwAwEB +/zAPBgNVHQ8BAf8EBQMDBwQAMB0GA1UdDgQWBBQZ59+VGi7y7lv2BxN79oCp7TMz +TTA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAaEaMBgGCSqGSIb3DQEBCDAL +BglghkgBZQMEAgGiAwIBQAOCAQEAMAgvcHqmjz1Ox5USoup5pe6HWPKtOR5pVGX2 +1zAk1wq7GoTKvo5QA6HtNR0ex1A2//XhklAKcqsIv1ELEh/3K/L0dEuaN4Zs784e +zaP0g/Ax6X3ClrHgARA4FA6MtaQblezj+7Zfc6cg1gKtfYleiOoK/Q+kk6JxOYAH +Lz9MF/6bZ8mYJQv8DURSp2p5NVWSEjbQV5IG2dw/eknZtbFaN5b+db3eVtrK0ZeS +l1e3hTwopCLNoh4qHUW/qKl0l1Gt7kPPxAsRReOxdcb1Pv73iuK7w5wbPyyWp0kM +FQj9tqRIMQZIer3gaURWG8OZfntCAvtlSSwc1PjwLBXO9ZvNBw== +-----END CERTIFICATE----- +__EOF__ + +eval "${GETPORT}" +launch_server $$ --echo --x509keyfile ${TMPFILE} --x509certfile ${TMPFILE} +PID=$! +wait_server ${PID} + +datefudge "2018-9-19" \ +${VALGRIND} "${CLI}" -p "${PORT}" localhost --x509crlfile ${CRLFILE} --x509cafile ${CAFILE} >${TMPFILE} 2>&1 </dev/null && \ + fail ${PID} "1. handshake should have failed!" + + +kill ${PID} +wait + +grep -E "Error setting the x509 CRL file: Error in the CRL verification.[[:space:]]*\$" ${TMPFILE} +if ! test $? = 0;then + echo "Did not find the expected error code" + cat ${TMPFILE} + exit 1 +fi + +rm -f ${TMPFILE} ${CAFILE} ${CRLFILE} + +exit 0 diff --git a/tests/gnutls-cli-self-signed.sh b/tests/gnutls-cli-self-signed.sh index 7ed1c212db..b5058f69ae 100755 --- a/tests/gnutls-cli-self-signed.sh +++ b/tests/gnutls-cli-self-signed.sh @@ -124,7 +124,7 @@ PID=$! wait_server ${PID} datefudge "2018-1-1" \ -${VALGRIND} "${CLI}" -p "${PORT}" localhost >${TMPFILE} 2>&1 && \ +${VALGRIND} "${CLI}" -p "${PORT}" localhost >${TMPFILE} 2>&1 </dev/null && \ fail ${PID} "1. handshake should have failed!" diff --git a/tests/resume-with-previous-stek.c b/tests/resume-with-previous-stek.c new file mode 100644 index 0000000000..2b2a4794a0 --- /dev/null +++ b/tests/resume-with-previous-stek.c @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2018 Free Software Foundation, Inc. + * + * Author: Ander Juaristi + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> + +#if defined(_WIN32) +int main(int argc, char **argv) +{ + exit(77); +} +#else + +#include <stdint.h> +#include <unistd.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <gnutls/gnutls.h> +#include <assert.h> +#include "utils.h" +#include "cert-common.h" +#include "virt-time.h" + +#define TICKET_EXPIRATION 1 /* seconds */ +#define TICKET_ROTATION_PERIOD 3 /* seconds */ + +unsigned num_stek_rotations; + +static void stek_rotation_callback(const gnutls_datum_t *prev_key, + const gnutls_datum_t *new_key, + uint64_t t) +{ + num_stek_rotations++; + success("STEK was rotated!\n"); +} + +static int client_handshake(gnutls_session_t session, gnutls_datum_t *session_data, + int resume) +{ + int ret; + + if (resume) { + if ((ret = gnutls_session_set_data(session, + session_data->data, + session_data->size)) < 0) { + fail("client: Could not get session data\n"); + } + } + + do { + ret = gnutls_handshake(session); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + + if (ret < 0) { + fail("client: Handshake failed\n"); + } else { + success("client: Handshake was completed\n"); + } + + if (gnutls_session_is_resumed(session)) + fail("client: Session was resumed (but should not)\n"); + else + success("client: Success: Session was NOT resumed\n"); + + if (!resume) { + if ((ret = gnutls_session_get_data2(session, session_data)) < 0) { + fail("client: Could not get session data\n"); + } + } + + do { + ret = gnutls_bye(session, GNUTLS_SHUT_RDWR); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + + return 0; +} + +static void client(int fd, int *resume, unsigned rounds, const char *prio) +{ + gnutls_session_t session; + gnutls_datum_t session_data; + gnutls_certificate_credentials_t clientx509cred = NULL; + + for (unsigned i = 0; i < rounds; i++) { + assert(gnutls_certificate_allocate_credentials(&clientx509cred)>=0); + + assert(gnutls_init(&session, GNUTLS_CLIENT)>=0); + assert(gnutls_priority_set_direct(session, prio, NULL)>=0); + + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, + clientx509cred); + + gnutls_transport_set_int(session, fd); + gnutls_handshake_set_timeout(session, 20 * 1000); + + /* Perform TLS handshake and obtain session ticket */ + if (client_handshake(session, &session_data, + resume[i]) < 0) + return; + + if (clientx509cred) { + gnutls_certificate_free_credentials(clientx509cred); + clientx509cred = NULL; + } + + gnutls_deinit(session); + } +} + +typedef void (* gnutls_stek_rotation_callback_t) (const gnutls_datum_t *prev_key, + const gnutls_datum_t *new_key, + uint64_t t); +void _gnutls_set_session_ticket_key_rotation_callback(gnutls_session_t session, + gnutls_stek_rotation_callback_t cb); + +static void server(int fd, unsigned rounds, const char *prio) +{ + int retval; + gnutls_session_t session; + gnutls_datum_t session_ticket_key = { NULL, 0 }; + gnutls_certificate_credentials_t serverx509cred = NULL; + + virt_time_init(); + + if (gnutls_session_ticket_key_generate(&session_ticket_key) < 0) { + fail("server: Could not generate session ticket key\n"); + } + + for (unsigned i = 0; i < rounds; i++) { + assert(gnutls_init(&session, GNUTLS_SERVER)>=0); + + assert(gnutls_certificate_allocate_credentials(&serverx509cred)>=0); + retval = gnutls_certificate_set_x509_key_mem(serverx509cred, + &server_cert, &server_key, + GNUTLS_X509_FMT_PEM); + if (retval < 0) + fail("error setting key: %s\n", gnutls_strerror(retval)); + + assert(gnutls_priority_set_direct(session, prio, NULL)>=0); + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, serverx509cred); + + gnutls_db_set_cache_expiration(session, TICKET_EXPIRATION); + _gnutls_set_session_ticket_key_rotation_callback(session, stek_rotation_callback); + + retval = gnutls_session_ticket_enable_server(session, + &session_ticket_key); + if (retval != GNUTLS_E_SUCCESS) { + fail("server: Could not enable session tickets: %s\n", gnutls_strerror(retval)); + } + + gnutls_transport_set_int(session, fd); + gnutls_handshake_set_timeout(session, 20 * 1000); + + virt_sec_sleep(TICKET_ROTATION_PERIOD-1); + + do { + retval = gnutls_handshake(session); + } while (retval == GNUTLS_E_AGAIN || retval == GNUTLS_E_INTERRUPTED); + + if (retval < 0) { + fail("server: Handshake failed: %s\n", gnutls_strerror(retval)); + } else { + success("server: Handshake was completed\n"); + } + + if (gnutls_session_is_resumed(session)) + fail("server: Session was resumed (but should not)\n"); + else + success("server: Success: Session was NOT resumed\n"); + + gnutls_bye(session, GNUTLS_SHUT_RDWR); + gnutls_deinit(session); + gnutls_certificate_free_credentials(serverx509cred); + serverx509cred = NULL; + } + + if (num_stek_rotations != 2) + fail("STEK should be rotated exactly twice (%d)!\n", num_stek_rotations); + + if (serverx509cred) + gnutls_certificate_free_credentials(serverx509cred); + gnutls_free(session_ticket_key.data); +} + +static void run(const char *name, const char *prio, int resume[], int rounds) +{ + pid_t child; + int retval, sockets[2], status = 0; + + success("\ntesting %s\n\n", name); + + retval = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets); + if (retval == -1) { + perror("socketpair"); + fail("socketpair failed"); + return; + } + + child = fork(); + if (child < 0) { + perror("fork"); + fail("fork failed"); + return; + } + + if (child) { + /* We are the parent */ + server(sockets[0], rounds, prio); + waitpid(child, &status, 0); + check_wait_status(status); + } else { + /* We are the child */ + client(sockets[1], resume, rounds, prio); + exit(0); + } +} + +void doit(void) +{ + int resume[] = { 0, 1, 0 }; + + signal(SIGCHLD, SIG_IGN); + signal(SIGPIPE, SIG_IGN); + + num_stek_rotations = 0; + run("tls1.2 resumption", "NORMAL:-VERS-ALL:+VERS-TLS1.2:+VERS-TLS1.1:+VERS-TLS1.0", resume, 3); + + num_stek_rotations = 0; + run("tls1.3 resumption", "NORMAL:-VERS-ALL:+VERS-TLS1.3", resume, 3); +} + +#endif + diff --git a/tests/resume-with-stek-expiration.c b/tests/resume-with-stek-expiration.c new file mode 100644 index 0000000000..7a6b532d63 --- /dev/null +++ b/tests/resume-with-stek-expiration.c @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2018 Free Software Foundation, Inc. + * + * Author: Ander Juaristi + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> + +#if defined(_WIN32) +int main(int argc, char **argv) +{ + exit(77); +} +#else + +#include <stdint.h> +#include <unistd.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <gnutls/gnutls.h> +#include <assert.h> +#include "utils.h" +#include "cert-common.h" +#include "virt-time.h" + +/* + * This will set the following values: + * + * - Ticket key expiration: 1 second. + * - Session ticket key rotation period: 3 seconds. + */ +#define TICKET_EXPIRATION 1 /* seconds */ + +unsigned num_stek_rotations; + +static void stek_rotation_callback(const gnutls_datum_t *prev_key, + const gnutls_datum_t *new_key, + uint64_t t) +{ + num_stek_rotations++; + success("STEK was rotated!\n"); +} + +typedef void (* gnutls_stek_rotation_callback_t) (const gnutls_datum_t *prev_key, + const gnutls_datum_t *new_key, + uint64_t t); +void _gnutls_set_session_ticket_key_rotation_callback(gnutls_session_t session, + gnutls_stek_rotation_callback_t cb); + +static int handshake(gnutls_session_t session, gnutls_datum_t *session_data, + int resumption_should_succeed) +{ + int ret; + + do { + ret = gnutls_handshake(session); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + + if (ret < 0) { + gnutls_perror(ret); + fail("client: Handshake failed\n"); + } else { + success("client: Handshake was completed\n"); + } + + if (gnutls_session_is_resumed(session)) { + if (!resumption_should_succeed) + fail("client: Session was resumed (but should not)\n"); + else + success("client: Success: Session was resumed\n"); + } else { + if (resumption_should_succeed) + fail("client: Session was not resumed (but should)\n"); + else + success("client: Success: Session was NOT resumed\n"); + } + + ret = gnutls_session_get_data2(session, session_data); + if (ret < 0) { + gnutls_perror(ret); + fail("client: Could not get session data\n"); + } + + do { + ret = gnutls_bye(session, GNUTLS_SHUT_RDWR); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + + return 0; +} + +static int resume_and_close(gnutls_session_t session, gnutls_datum_t *session_data, + int resumption_should_succeed) +{ + int ret; + + ret = gnutls_session_set_data(session, session_data->data, session_data->size); + if (ret < 0) { + gnutls_perror(ret); + fail("client: Could not get session data\n"); + } + + do { + ret = gnutls_handshake(session); + } while (ret < 0 && !gnutls_error_is_fatal(ret)); + + if (ret < 0) { + fail("client: Handshake failed: %s\n", gnutls_strerror(ret)); + } else { + success("client: Handshake was completed\n"); + } + + if (gnutls_session_is_resumed(session)) { + if (!resumption_should_succeed) + fail("client: Session was resumed (but should not)\n"); + else + success("client: Success: Session was resumed\n"); + } else { + if (resumption_should_succeed) + fail("client: Session was not resumed (but should)\n"); + else + success("client: Success: Session was NOT resumed\n"); + } + + do { + ret = gnutls_bye(session, GNUTLS_SHUT_RDWR); + } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); + + return 0; +} + +static void client(int fd, int *resumption_should_succeed, unsigned num_sessions, const char *prio) +{ + gnutls_session_t session; + gnutls_datum_t session_data; + gnutls_certificate_credentials_t clientx509cred = NULL; + + gnutls_certificate_allocate_credentials(&clientx509cred); + + /* Initialize TLS layer */ + gnutls_init(&session, GNUTLS_CLIENT); + gnutls_priority_set_direct(session, prio, NULL); + + /* put the anonymous credentials to the current session */ + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, + clientx509cred); + + gnutls_transport_set_int(session, fd); + gnutls_handshake_set_timeout(session, 20 * 1000); + + if (handshake(session, &session_data, resumption_should_succeed[0]) < 0) + return; + + if (clientx509cred) + gnutls_certificate_free_credentials(clientx509cred); + gnutls_deinit(session); + + for (unsigned i = 1; i < num_sessions; i++) { + assert(gnutls_certificate_allocate_credentials(&clientx509cred)>=0); + + /* Initialize TLS layer */ + assert(gnutls_init(&session, GNUTLS_CLIENT)>=0); + assert(gnutls_priority_set_direct(session, prio, NULL)>=0); + + /* put the anonymous credentials to the current session */ + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, + clientx509cred); + + gnutls_transport_set_int(session, fd); + + if (resume_and_close(session, &session_data, resumption_should_succeed[i]) < 0) + return; + + if (clientx509cred) + gnutls_certificate_free_credentials(clientx509cred); + gnutls_deinit(session); + } +} + +static void server(int fd, int *resumption_should_succeed, unsigned num_sessions, const char *prio) +{ + int retval; + gnutls_session_t session; + gnutls_certificate_credentials_t serverx509cred; + gnutls_datum_t session_ticket_key = { NULL, 0 }; + + virt_time_init(); + + if (gnutls_session_ticket_key_generate(&session_ticket_key) < 0) + fail("server: Could not generate session ticket key\n"); + + for (unsigned i = 0; i < num_sessions; i++) { + if ((retval = gnutls_init(&session, GNUTLS_SERVER)) < 0) { + gnutls_perror(retval); + fail("gnutls_init() failed\n"); + } + + assert(gnutls_certificate_allocate_credentials(&serverx509cred)>=0); + assert(gnutls_certificate_set_x509_key_mem(serverx509cred, + &server_cert, &server_key, + GNUTLS_X509_FMT_PEM)>=0); + + assert(gnutls_priority_set_direct(session, prio, NULL)>=0); + + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, serverx509cred); + + retval = gnutls_session_ticket_enable_server(session, &session_ticket_key); + if (retval != GNUTLS_E_SUCCESS) { + gnutls_perror(retval); + fail("server: Could not enable session tickets\n"); + } + + + gnutls_db_set_cache_expiration(session, TICKET_EXPIRATION); + + _gnutls_set_session_ticket_key_rotation_callback(session, stek_rotation_callback); + + gnutls_transport_set_int(session, fd); + gnutls_handshake_set_timeout(session, 20 * 1000); + + do { + retval = gnutls_handshake(session); + } while(retval == GNUTLS_E_AGAIN || retval == GNUTLS_E_INTERRUPTED); + + if (retval < 0) { + fail("server: Handshake failed: %s\n", gnutls_strerror(retval)); + } else { + success("server: Handshake was completed\n"); + } + + if (gnutls_session_is_resumed(session)) { + if (!resumption_should_succeed[i]) + fail("server: Session was resumed (but should not)\n"); + else + success("server: Success: Session was resumed\n"); + } else { + if (resumption_should_succeed[i]) + fail("server: Session was not resumed (but should)\n"); + else + success("server: Success: Session was NOT resumed\n"); + } + + gnutls_bye(session, GNUTLS_SHUT_RDWR); + gnutls_deinit(session); + gnutls_certificate_free_credentials(serverx509cred); + serverx509cred = NULL; + + if (i != 0) + virt_sec_sleep(TICKET_EXPIRATION); + } + + if (num_stek_rotations != 4) + fail("STEK should be rotated exactly 4 times!\n"); + + if (serverx509cred) + gnutls_certificate_free_credentials(serverx509cred); + gnutls_free(session_ticket_key.data); +} + +static void run(const char *name, const char *prio, int resumption_should_succeed[], int rounds) +{ + pid_t child; + int retval, sockets[2], status = 0; + + success("\ntesting %s\n\n", name); + + retval = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets); + if (retval == -1) { + perror("socketpair"); + fail("socketpair failed"); + } + + child = fork(); + if (child < 0) { + perror("fork"); + fail("fork failed"); + } + + if (child) { + /* We are the parent */ + server(sockets[0], resumption_should_succeed, rounds, prio); + waitpid(child, &status, 0); + check_wait_status(status); + } else { + /* We are the child */ + client(sockets[1], resumption_should_succeed, rounds, prio); + exit(0); + } +} + +void doit(void) +{ + int resumption_should_succeed[] = { 0, 1, 1, 0 }; + + signal(SIGCHLD, SIG_IGN); + signal(SIGPIPE, SIG_IGN); + + num_stek_rotations = 0; + run("tls1.2 resumption", "NORMAL:-VERS-ALL:+VERS-TLS1.2:+VERS-TLS1.1:+VERS-TLS1.0", + resumption_should_succeed, 4); + + num_stek_rotations = 0; + run("tls1.3 resumption", "NORMAL:-VERS-ALL:+VERS-TLS1.3", + resumption_should_succeed, 4); +} + +#endif diff --git a/tests/resume.c b/tests/resume.c index 953c017388..84314b836c 100644 --- a/tests/resume.c +++ b/tests/resume.c @@ -56,6 +56,7 @@ int main(int argc, char **argv) #include <assert.h> #include "utils.h" #include "cert-common.h" +#include "virt-time.h" static void wrap_db_init(void); static void wrap_db_deinit(void); @@ -528,8 +529,6 @@ static void client(int sds[], struct params_res *params) else if (params->try_sni) gnutls_server_name_set(session, GNUTLS_NAME_DNS, dns_name2, strlen(dns_name2)); - if (params->expire_ticket) - sleep(2); } else { if (params->try_sni) gnutls_server_name_set(session, GNUTLS_NAME_DNS, dns_name2, strlen(dns_name2)); @@ -722,6 +721,8 @@ static void server(int sds[], struct params_res *params) gnutls_group_t pgroup; unsigned iflags = GNUTLS_SERVER; + virt_time_init(); + if (params->early_start || params->no_early_start) iflags |= GNUTLS_ENABLE_EARLY_START; @@ -794,9 +795,10 @@ static void server(int sds[], struct params_res *params) append_alpn(session, params, t); - if (params->expire_ticket) - gnutls_db_set_cache_expiration(session, 1); - + if (params->expire_ticket) { + gnutls_db_set_cache_expiration(session, 45); + virt_sec_sleep(60); + } #ifdef USE_PSK gnutls_credentials_set(session, GNUTLS_CRD_PSK, pskcred); #elif defined(USE_ANON) |