static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { u8 *pos, *rhdr; size_t rlen; pos = *msgpos; wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; *pos = TLS_CHANGE_CIPHER_SPEC; if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, rhdr, end - rhdr, 1, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } if (tlsv1_record_change_write_cipher(&conn->rl) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for " "record layer"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } *msgpos = rhdr + rlen; return 0; }
static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { size_t rlen; u8 payload[1]; wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); payload[0] = TLS_CHANGE_CIPHER_SPEC; if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, *msgpos, end - *msgpos, payload, sizeof(payload), &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } if (tlsv1_record_change_write_cipher(&conn->rl) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for " "record layer"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } *msgpos += rlen; return 0; }
static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos, *end; size_t left, len; u8 type; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " "received content type 0x%x", ct); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 4) { wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerHelloDone " "(left=%lu)", (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } type = *pos++; len = WPA_GET_BE24(pos); pos += 3; left -= 4; if (len > left) { wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerHelloDone " "length (len=%lu != left=%lu)", (unsigned long) len, (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } end = pos + len; if (type != TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) { wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " "message %d (expected ServerHelloDone)", type); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone"); *in_len = end - in_data; conn->state = CLIENT_KEY_EXCHANGE; return 0; }
static int tls_process_application_data(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len, u8 **out_data, size_t *out_len) { const u8 *pos; size_t left; if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Application Data; " "received content type 0x%x", ct); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; wpa_hexdump(MSG_DEBUG, "TLSv1: Application Data included in Handshake", pos, left); *out_data = os_malloc(left); if (*out_data) { os_memcpy(*out_data, pos, left); *out_len = left; } return 0; }
/** * tlsv1_client_decrypt - Decrypt data from TLS tunnel * @conn: TLSv1 client connection data from tlsv1_client_init() * @in_data: Pointer to input buffer (encrypted TLS data) * @in_len: Input buffer length * @out_data: Pointer to output buffer (decrypted data from TLS tunnel) * @out_len: Maximum out_data length * Returns: Number of bytes written to out_data, -1 on failure * * This function is used after TLS handshake has been completed successfully to * receive data from the encrypted tunnel. */ int tlsv1_client_decrypt(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len) { const u8 *in_end, *pos; int res; u8 alert, *out_end, *out_pos; size_t olen; pos = in_data; in_end = in_data + in_len; out_pos = out_data; out_end = out_data + out_len; while (pos < in_end) { if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " "0x%x", pos[0]); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } olen = out_end - out_pos; res = tlsv1_record_receive(&conn->rl, pos, in_end - pos, out_pos, &olen, &alert); if (res < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " "failed"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); return -1; } out_pos += olen; if (out_pos > out_end) { wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough " "for processing the received record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); } return out_pos - out_data; }
static int tlsv1_key_x_rsa(struct tlsv1_client *conn, u8 **pos, u8 *end) { u8 pre_master_secret[TLS_PRE_MASTER_SECRET_LEN]; size_t clen; int res; if (tls_derive_pre_master_secret(pre_master_secret) < 0 || tls_derive_keys(conn, pre_master_secret, TLS_PRE_MASTER_SECRET_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } /* EncryptedPreMasterSecret */ if (conn->server_rsa_key == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: No server RSA key to " "use for encrypting pre-master secret"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } /* RSA encrypted value is encoded with PKCS #1 v1.5 block type 2. */ *pos += 2; clen = end - *pos; res = crypto_public_key_encrypt_pkcs1_v15( conn->server_rsa_key, pre_master_secret, TLS_PRE_MASTER_SECRET_LEN, *pos, &clen); os_memset(pre_master_secret, 0, TLS_PRE_MASTER_SECRET_LEN); if (res < 0) { wpa_printf(MSG_DEBUG, "TLSv1: RSA encryption failed"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } WPA_PUT_BE16(*pos - 2, clen); wpa_hexdump(MSG_MSGDUMP, "TLSv1: Encrypted pre_master_secret", *pos, clen); *pos += clen; return 0; }
static int tls_write_client_key_exchange(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { u8 *pos, *rhdr, *hs_start, *hs_length; size_t rlen; tls_key_exchange keyx; const struct tls_cipher_suite *suite; suite = tls_get_cipher_suite(conn->rl.cipher_suite); if (suite == NULL) keyx = TLS_KEY_X_NULL; else keyx = suite->key_exchange; pos = *msgpos; wpa_printf(MSG_DEBUG, "TLSv1: Send ClientKeyExchange"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; /* opaque fragment[TLSPlaintext.length] */ /* Handshake */ hs_start = pos; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE; /* uint24 length (to be filled) */ hs_length = pos; pos += 3; /* body - ClientKeyExchange */ if (keyx == TLS_KEY_X_DH_anon) { if (tlsv1_key_x_anon_dh(conn, &pos, end) < 0) return -1; } else { if (tlsv1_key_x_rsa(conn, &pos, end) < 0) return -1; } WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, rhdr, end - rhdr, hs_start, pos - hs_start, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } pos = rhdr + rlen; tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); *msgpos = pos; return 0; }
/** * tlsv1_client_encrypt - Encrypt data into TLS tunnel * @conn: TLSv1 client connection data from tlsv1_client_init() * @in_data: Pointer to plaintext data to be encrypted * @in_len: Input buffer length * @out_data: Pointer to output buffer (encrypted TLS data) * @out_len: Maximum out_data length * Returns: Number of bytes written to out_data, -1 on failure * * This function is used after TLS handshake has been completed successfully to * send data in the encrypted tunnel. */ int tlsv1_client_encrypt(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len) { size_t rlen; wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData", in_data, in_len); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA, out_data, out_len, in_data, in_len, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } return rlen; }
static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos, *end; size_t left, len, hlen; u8 verify_data[TLS_VERIFY_DATA_LEN]; u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; " "received content type 0x%x", ct); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 4) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for " "Finished", (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received " "type 0x%x", pos[0]); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } len = WPA_GET_BE24(pos + 1); pos += 4; left -= 4; if (len > left) { wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished " "(len=%lu > left=%lu)", (unsigned long) len, (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } end = pos + len; if (len != TLS_VERIFY_DATA_LEN) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length " "in Finished: %lu (expected %d)", (unsigned long) len, TLS_VERIFY_DATA_LEN); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished", pos, TLS_VERIFY_DATA_LEN); #ifdef CONFIG_TLSV12 if (conn->rl.tls_version >= TLS_VERSION_1_2) { hlen = SHA256_MAC_LEN; if (conn->verify.sha256_server == NULL || crypto_hash_finish(conn->verify.sha256_server, hash, &hlen) < 0) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); conn->verify.sha256_server = NULL; return -1; } conn->verify.sha256_server = NULL; } else { #endif /* CONFIG_TLSV12 */ hlen = MD5_MAC_LEN; if (conn->verify.md5_server == NULL || crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); conn->verify.md5_server = NULL; crypto_hash_finish(conn->verify.sha1_server, NULL, NULL); conn->verify.sha1_server = NULL; return -1; } conn->verify.md5_server = NULL; hlen = SHA1_MAC_LEN; if (conn->verify.sha1_server == NULL || crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN, &hlen) < 0) { conn->verify.sha1_server = NULL; tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } conn->verify.sha1_server = NULL; hlen = MD5_MAC_LEN + SHA1_MAC_LEN; #ifdef CONFIG_TLSV12 } #endif /* CONFIG_TLSV12 */ if (tls_prf(conn->rl.tls_version, conn->master_secret, TLS_MASTER_SECRET_LEN, "server finished", hash, hlen, verify_data, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECRYPT_ERROR); return -1; } wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)", verify_data, TLS_VERIFY_DATA_LEN); if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECRYPT_ERROR); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received Finished"); *in_len = end - in_data; conn->state = (conn->session_resumed || conn->use_session_ticket) ? CHANGE_CIPHER_SPEC : ACK_FINISHED; return 0; }
static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) { #ifdef EAP_FAST /* ClientDiffieHellmanPublic */ u8 *csecret, *csecret_start, *dh_yc, *shared; size_t csecret_len, dh_yc_len, shared_len; csecret_len = conn->dh_p_len; csecret = os_malloc(csecret_len); if (csecret == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " "memory for Yc (Diffie-Hellman)"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } if (os_get_random(csecret, csecret_len)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random " "data for Diffie-Hellman"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(csecret); return -1; } if (os_memcmp(csecret, conn->dh_p, csecret_len) > 0) csecret[0] = 0; /* make sure Yc < p */ csecret_start = csecret; while (csecret_len > 1 && *csecret_start == 0) { csecret_start++; csecret_len--; } wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH client's secret value", csecret_start, csecret_len); /* Yc = g^csecret mod p */ dh_yc_len = conn->dh_p_len; dh_yc = os_malloc(dh_yc_len); if (dh_yc == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " "memory for Diffie-Hellman"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(csecret); return -1; } if (crypto_mod_exp(conn->dh_g, conn->dh_g_len, csecret_start, csecret_len, conn->dh_p, conn->dh_p_len, dh_yc, &dh_yc_len)) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(csecret); os_free(dh_yc); return -1; } wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)", dh_yc, dh_yc_len); WPA_PUT_BE16(*pos, dh_yc_len); *pos += 2; if (*pos + dh_yc_len > end) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the " "message buffer for Yc"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(csecret); os_free(dh_yc); return -1; } os_memcpy(*pos, dh_yc, dh_yc_len); *pos += dh_yc_len; os_free(dh_yc); shared_len = conn->dh_p_len; shared = os_malloc(shared_len); if (shared == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for " "DH"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(csecret); return -1; } /* shared = Ys^csecret mod p */ if (crypto_mod_exp(conn->dh_ys, conn->dh_ys_len, csecret_start, csecret_len, conn->dh_p, conn->dh_p_len, shared, &shared_len)) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(csecret); os_free(shared); return -1; } wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange", shared, shared_len); os_memset(csecret_start, 0, csecret_len); os_free(csecret); if (tls_derive_keys(conn, shared, shared_len)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(shared); return -1; } os_memset(shared, 0, shared_len); os_free(shared); tlsv1_client_free_dh(conn); return 0; #else /* EAP_FAST */ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; #endif /* EAP_FAST */ }
static int tls_write_client_certificate(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start; size_t rlen; struct x509_certificate *cert; pos = *msgpos; wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; /* opaque fragment[TLSPlaintext.length] */ /* Handshake */ hs_start = pos; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE; /* uint24 length (to be filled) */ hs_length = pos; pos += 3; /* body - Certificate */ /* uint24 length (to be filled) */ cert_start = pos; pos += 3; cert = conn->cred ? conn->cred->cert : NULL; while (cert) { if (pos + 3 + cert->cert_len > end) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space " "for Certificate (cert_len=%lu left=%lu)", (unsigned long) cert->cert_len, (unsigned long) (end - pos)); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } WPA_PUT_BE24(pos, cert->cert_len); pos += 3; os_memcpy(pos, cert->cert_start, cert->cert_len); pos += cert->cert_len; if (x509_certificate_self_signed(cert)) break; cert = x509_certificate_get_subject(conn->cred->trusted_certs, &cert->issuer); } if (conn->cred == NULL || cert == conn->cred->cert || cert == NULL) { /* * Client was not configured with all the needed certificates * to form a full certificate chain. The server may fail to * validate the chain unless it is configured with all the * missing CA certificates. */ wpa_printf(MSG_DEBUG, "TLSv1: Full client certificate chain " "not configured - validation may fail"); } WPA_PUT_BE24(cert_start, pos - cert_start - 3); WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } pos = rhdr + rlen; tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); *msgpos = pos; return 0; }
static int tls_write_client_finished(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { u8 *pos, *hs_start; size_t rlen, hlen; u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN]; u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; wpa_printf(MSG_DEBUG, "TLSv1: Send Finished"); /* Encrypted Handshake Message: Finished */ hlen = MD5_MAC_LEN; if (conn->verify.md5_client == NULL || crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); conn->verify.md5_client = NULL; crypto_hash_finish(conn->verify.sha1_client, NULL, NULL); conn->verify.sha1_client = NULL; return -1; } conn->verify.md5_client = NULL; hlen = SHA1_MAC_LEN; if (conn->verify.sha1_client == NULL || crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN, &hlen) < 0) { conn->verify.sha1_client = NULL; tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } conn->verify.sha1_client = NULL; if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN, verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)", verify_data + 1 + 3, TLS_VERIFY_DATA_LEN); /* Handshake */ pos = hs_start = verify_data; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_FINISHED; /* uint24 length */ WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN); pos += 3; pos += TLS_VERIFY_DATA_LEN; /* verify_data already in place */ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, *msgpos, end - *msgpos, hs_start, pos - hs_start, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } *msgpos += rlen; return 0; }
/** * tlsv1_client_handshake - Process TLS handshake * @conn: TLSv1 client connection data from tlsv1_client_init() * @in_data: Input data from TLS peer * @in_len: Input data length * @out_len: Length of the output buffer. * @appl_data: Pointer to application data pointer, or %NULL if dropped * @appl_data_len: Pointer to variable that is set to appl_data length * Returns: Pointer to output data, %NULL on failure */ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, size_t *out_len, u8 **appl_data, size_t *appl_data_len) { const u8 *pos, *end; u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct; size_t in_msg_len; int no_appl_data; if (conn->state == CLIENT_HELLO) { if (in_len) return NULL; return tls_send_client_hello(conn, out_len); } if (in_data == NULL || in_len == 0) return NULL; pos = in_data; end = in_data + in_len; in_msg = os_malloc(in_len); if (in_msg == NULL) return NULL; /* Each received packet may include multiple records */ while (pos < end) { in_msg_len = in_len; if (tlsv1_record_receive(&conn->rl, pos, end - pos, in_msg, &in_msg_len, &alert)) { wpa_printf(MSG_DEBUG, "TLSv1: Processing received " "record failed"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); goto failed; } ct = pos[0]; in_pos = in_msg; in_end = in_msg + in_msg_len; /* Each received record may include multiple messages of the * same ContentType. */ while (in_pos < in_end) { in_msg_len = in_end - in_pos; if (tlsv1_client_process_handshake(conn, ct, in_pos, &in_msg_len, appl_data, appl_data_len) < 0) goto failed; in_pos += in_msg_len; } pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); } os_free(in_msg); in_msg = NULL; no_appl_data = appl_data == NULL || *appl_data == NULL; msg = tlsv1_client_handshake_write(conn, out_len, no_appl_data); failed: os_free(in_msg); if (conn->alert_level) { conn->state = FAILED; os_free(msg); msg = tlsv1_client_send_alert(conn, conn->alert_level, conn->alert_description, out_len); } else if (msg == NULL) { msg = os_zalloc(1); *out_len = 0; } return msg; }
/** * tlsv1_client_decrypt - Decrypt data from TLS tunnel * @conn: TLSv1 client connection data from tlsv1_client_init() * @in_data: Pointer to input buffer (encrypted TLS data) * @in_len: Input buffer length * @need_more_data: Set to 1 if more data would be needed to complete * processing * Returns: Decrypted data or %NULL on failure * * This function is used after TLS handshake has been completed successfully to * receive data from the encrypted tunnel. */ struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, int *need_more_data) { const u8 *in_end, *pos; int used; u8 alert, *out_pos, ct; size_t olen; struct wpabuf *buf = NULL; if (need_more_data) *need_more_data = 0; if (conn->partial_input) { if (wpabuf_resize(&conn->partial_input, in_len) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " "memory for pending record"); alert = TLS_ALERT_INTERNAL_ERROR; goto fail; } wpabuf_put_data(conn->partial_input, in_data, in_len); in_data = wpabuf_head(conn->partial_input); in_len = wpabuf_len(conn->partial_input); } pos = in_data; in_end = in_data + in_len; while (pos < in_end) { ct = pos[0]; if (wpabuf_resize(&buf, in_end - pos) < 0) { alert = TLS_ALERT_INTERNAL_ERROR; goto fail; } out_pos = wpabuf_put(buf, 0); olen = wpabuf_tailroom(buf); used = tlsv1_record_receive(&conn->rl, pos, in_end - pos, out_pos, &olen, &alert); if (used < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " "failed"); goto fail; } if (used == 0) { struct wpabuf *partial; wpa_printf(MSG_DEBUG, "TLSv1: Need more data"); partial = wpabuf_alloc_copy(pos, in_end - pos); wpabuf_free(conn->partial_input); conn->partial_input = partial; if (conn->partial_input == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to " "allocate memory for pending " "record"); alert = TLS_ALERT_INTERNAL_ERROR; goto fail; } if (need_more_data) *need_more_data = 1; return buf; } if (ct == TLS_CONTENT_TYPE_ALERT) { if (olen < 2) { wpa_printf(MSG_DEBUG, "TLSv1: Alert " "underflow"); alert = TLS_ALERT_DECODE_ERROR; goto fail; } wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", out_pos[0], out_pos[1]); if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { /* Continue processing */ pos += used; continue; } alert = out_pos[1]; goto fail; } if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " "0x%x when decrypting application data", pos[0]); alert = TLS_ALERT_UNEXPECTED_MESSAGE; goto fail; } wpabuf_put(buf, olen); pos += used; } wpabuf_free(conn->partial_input); conn->partial_input = NULL; return buf; fail: wpabuf_free(buf); wpabuf_free(conn->partial_input); conn->partial_input = NULL; tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); return NULL; }
int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct, const u8 *buf, size_t *len, u8 **out_data, size_t *out_len) { if (ct == TLS_CONTENT_TYPE_ALERT) { if (*len < 2) { wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", buf[0], buf[1]); *len = 2; conn->state = FAILED; return -1; } if (ct == TLS_CONTENT_TYPE_HANDSHAKE && *len >= 4 && buf[0] == TLS_HANDSHAKE_TYPE_HELLO_REQUEST) { size_t hr_len = WPA_GET_BE24(buf + 1); if (hr_len > *len - 4) { wpa_printf(MSG_DEBUG, "TLSv1: HelloRequest underflow"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Ignored HelloRequest"); *len = 4 + hr_len; return 0; } switch (conn->state) { case SERVER_HELLO: if (tls_process_server_hello(conn, ct, buf, len)) return -1; break; case SERVER_CERTIFICATE: if (tls_process_certificate(conn, ct, buf, len)) return -1; break; case SERVER_KEY_EXCHANGE: if (tls_process_server_key_exchange(conn, ct, buf, len)) return -1; break; case SERVER_CERTIFICATE_REQUEST: if (tls_process_certificate_request(conn, ct, buf, len)) return -1; break; case SERVER_HELLO_DONE: if (tls_process_server_hello_done(conn, ct, buf, len)) return -1; break; case SERVER_CHANGE_CIPHER_SPEC: if (tls_process_server_change_cipher_spec(conn, ct, buf, len)) return -1; break; case SERVER_FINISHED: if (tls_process_server_finished(conn, ct, buf, len)) return -1; break; case ACK_FINISHED: if (out_data && tls_process_application_data(conn, ct, buf, len, out_data, out_len)) return -1; break; default: wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d " "while processing received message", conn->state); return -1; } if (ct == TLS_CONTENT_TYPE_HANDSHAKE) tls_verify_hash_add(&conn->verify, buf, *len); return 0; }
static int tls_write_client_certificate_verify(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start; size_t rlen, hlen, clen; u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos; enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; pos = *msgpos; wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateVerify"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; /* Handshake */ hs_start = pos; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY; /* uint24 length (to be filled) */ hs_length = pos; pos += 3; /* * RFC 2246: 7.4.3 and 7.4.8: * Signature signature * * RSA: * digitally-signed struct { * opaque md5_hash[16]; * opaque sha_hash[20]; * }; * * DSA: * digitally-signed struct { * opaque sha_hash[20]; * }; * * The hash values are calculated over all handshake messages sent or * received starting at ClientHello up to, but not including, this * CertificateVerify message, including the type and length fields of * the handshake messages. */ hpos = hash; if (alg == SIGN_ALG_RSA) { hlen = MD5_MAC_LEN; if (conn->verify.md5_cert == NULL || crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); conn->verify.md5_cert = NULL; crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL); conn->verify.sha1_cert = NULL; return -1; } hpos += MD5_MAC_LEN; } else crypto_hash_finish(conn->verify.md5_cert, NULL, NULL); conn->verify.md5_cert = NULL; hlen = SHA1_MAC_LEN; if (conn->verify.sha1_cert == NULL || crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) { conn->verify.sha1_cert = NULL; tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } conn->verify.sha1_cert = NULL; if (alg == SIGN_ALG_RSA) hlen += MD5_MAC_LEN; wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen); /* * RFC 2246, 4.7: * In digital signing, one-way hash functions are used as input for a * signing algorithm. A digitally-signed element is encoded as an * opaque vector <0..2^16-1>, where the length is specified by the * signing algorithm and key. * * In RSA signing, a 36-byte structure of two hashes (one SHA and one * MD5) is signed (encrypted with the private key). It is encoded with * PKCS #1 block type 0 or type 1 as described in [PKCS1]. */ signed_start = pos; /* length to be filled */ pos += 2; clen = end - pos; if (conn->cred == NULL || crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen, pos, &clen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } WPA_PUT_BE16(signed_start, clen); pos += clen; WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } pos = rhdr + rlen; tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); *msgpos = pos; return 0; }
static int tls_process_server_change_cipher_spec(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos; size_t left; if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " "received content type 0x%x", ct); if (conn->use_session_ticket) { int res; wpa_printf(MSG_DEBUG, "TLSv1: Server may have " "rejected SessionTicket"); conn->use_session_ticket = 0; /* Notify upper layers that SessionTicket failed */ res = conn->session_ticket_cb( conn->session_ticket_cb_ctx, NULL, 0, NULL, NULL, NULL); if (res < 0) { wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket " "callback indicated failure"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_HANDSHAKE_FAILURE); return -1; } conn->state = SERVER_CERTIFICATE; return tls_process_certificate(conn, ct, in_data, in_len); } tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 1) { wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } if (*pos != TLS_CHANGE_CIPHER_SPEC) { wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " "received data 0x%x", *pos); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec"); if (tlsv1_record_change_read_cipher(&conn->rl) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher " "for record layer"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } *in_len = pos + 1 - in_data; conn->state = SERVER_FINISHED; return 0; }
static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos, *end; size_t left, len, list_len, cert_len, idx; u8 type; struct x509_certificate *chain = NULL, *last = NULL, *cert; int reason; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " "received content type 0x%x", ct); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 4) { wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message " "(len=%lu)", (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } type = *pos++; len = WPA_GET_BE24(pos); pos += 3; left -= 4; if (len > left) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message " "length (len=%lu != left=%lu)", (unsigned long) len, (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } if (type == TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) return tls_process_server_key_exchange(conn, ct, in_data, in_len); if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) return tls_process_certificate_request(conn, ct, in_data, in_len); if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) return tls_process_server_hello_done(conn, ct, in_data, in_len); if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) { wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " "message %d (expected Certificate/" "ServerKeyExchange/CertificateRequest/" "ServerHelloDone)", type); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received Certificate (certificate_list len %lu)", (unsigned long) len); /* * opaque ASN.1Cert<2^24-1>; * * struct { * ASN.1Cert certificate_list<1..2^24-1>; * } Certificate; */ end = pos + len; if (end - pos < 3) { wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate " "(left=%lu)", (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } list_len = WPA_GET_BE24(pos); pos += 3; if ((size_t) (end - pos) != list_len) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list " "length (len=%lu left=%lu)", (unsigned long) list_len, (unsigned long) (end - pos)); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } idx = 0; while (pos < end) { if (end - pos < 3) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " "certificate_list"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); x509_certificate_chain_free(chain); return -1; } cert_len = WPA_GET_BE24(pos); pos += 3; if ((size_t) (end - pos) < cert_len) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate " "length (len=%lu left=%lu)", (unsigned long) cert_len, (unsigned long) (end - pos)); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); x509_certificate_chain_free(chain); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)", (unsigned long) idx, (unsigned long) cert_len); if (idx == 0) { crypto_public_key_free(conn->server_rsa_key); if (tls_parse_cert(pos, cert_len, &conn->server_rsa_key)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " "the certificate"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_BAD_CERTIFICATE); x509_certificate_chain_free(chain); return -1; } } cert = x509_certificate_parse(pos, cert_len); if (cert == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " "the certificate"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_BAD_CERTIFICATE); x509_certificate_chain_free(chain); return -1; } if (last == NULL) chain = cert; else last->next = cert; last = cert; idx++; pos += cert_len; } if (conn->cred && x509_certificate_chain_validate(conn->cred->trusted_certs, chain, &reason, conn->disable_time_checks) < 0) { int tls_reason; wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " "validation failed (reason=%d)", reason); switch (reason) { case X509_VALIDATE_BAD_CERTIFICATE: tls_reason = TLS_ALERT_BAD_CERTIFICATE; break; case X509_VALIDATE_UNSUPPORTED_CERTIFICATE: tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE; break; case X509_VALIDATE_CERTIFICATE_REVOKED: tls_reason = TLS_ALERT_CERTIFICATE_REVOKED; break; case X509_VALIDATE_CERTIFICATE_EXPIRED: tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED; break; case X509_VALIDATE_CERTIFICATE_UNKNOWN: tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN; break; case X509_VALIDATE_UNKNOWN_CA: tls_reason = TLS_ALERT_UNKNOWN_CA; break; default: tls_reason = TLS_ALERT_BAD_CERTIFICATE; break; } tls_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason); x509_certificate_chain_free(chain); return -1; } x509_certificate_chain_free(chain); *in_len = end - in_data; conn->state = SERVER_KEY_EXCHANGE; return 0; }
static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos, *end; size_t left, len; u8 type; const struct tls_cipher_suite *suite; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " "received content type 0x%x", ct); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 4) { wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerKeyExchange " "(Left=%lu)", (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } type = *pos++; len = WPA_GET_BE24(pos); pos += 3; left -= 4; if (len > left) { wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerKeyExchange " "length (len=%lu != left=%lu)", (unsigned long) len, (unsigned long) left); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } end = pos + len; if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) return tls_process_certificate_request(conn, ct, in_data, in_len); if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) return tls_process_server_hello_done(conn, ct, in_data, in_len); if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) { wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " "message %d (expected ServerKeyExchange/" "CertificateRequest/ServerHelloDone)", type); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received ServerKeyExchange"); if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) { wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not allowed " "with the selected cipher suite"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } wpa_hexdump(MSG_DEBUG, "TLSv1: ServerKeyExchange", pos, len); suite = tls_get_cipher_suite(conn->rl.cipher_suite); if (suite && (suite->key_exchange == TLS_KEY_X_DH_anon || suite->key_exchange == TLS_KEY_X_DHE_RSA)) { if (tlsv1_process_diffie_hellman(conn, pos, len, suite->key_exchange) < 0) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } } else { wpa_printf(MSG_DEBUG, "TLSv1: UnexpectedServerKeyExchange"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } *in_len = end - in_data; conn->state = SERVER_CERTIFICATE_REQUEST; return 0; }
static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { const u8 *pos, *end; size_t left, len, i; u16 cipher_suite; u16 tls_version; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " "received content type 0x%x", ct); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } pos = in_data; left = *in_len; if (left < 4) goto decode_error; /* HandshakeType msg_type */ if (*pos != TLS_HANDSHAKE_TYPE_SERVER_HELLO) { wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " "message %d (expected ServerHello)", *pos); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHello"); pos++; /* uint24 length */ len = WPA_GET_BE24(pos); pos += 3; left -= 4; if (len > left) goto decode_error; /* body - ServerHello */ wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello", pos, len); end = pos + len; /* ProtocolVersion server_version */ if (end - pos < 2) goto decode_error; tls_version = WPA_GET_BE16(pos); if (!tls_version_ok(tls_version)) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " "ServerHello %u.%u", pos[0], pos[1]); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_PROTOCOL_VERSION); return -1; } pos += 2; wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s", tls_version_str(tls_version)); conn->rl.tls_version = tls_version; /* Random random */ if (end - pos < TLS_RANDOM_LEN) goto decode_error; os_memcpy(conn->server_random, pos, TLS_RANDOM_LEN); pos += TLS_RANDOM_LEN; wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random", conn->server_random, TLS_RANDOM_LEN); /* SessionID session_id */ if (end - pos < 1) goto decode_error; if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) goto decode_error; if (conn->session_id_len && conn->session_id_len == *pos && os_memcmp(conn->session_id, pos + 1, conn->session_id_len) == 0) { pos += 1 + conn->session_id_len; wpa_printf(MSG_DEBUG, "TLSv1: Resuming old session"); conn->session_resumed = 1; } else { conn->session_id_len = *pos; pos++; os_memcpy(conn->session_id, pos, conn->session_id_len); pos += conn->session_id_len; } wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id", conn->session_id, conn->session_id_len); /* CipherSuite cipher_suite */ if (end - pos < 2) goto decode_error; cipher_suite = WPA_GET_BE16(pos); pos += 2; for (i = 0; i < conn->num_cipher_suites; i++) { if (cipher_suite == conn->cipher_suites[i]) break; } if (i == conn->num_cipher_suites) { wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected " "cipher suite 0x%04x", cipher_suite); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_ILLEGAL_PARAMETER); return -1; } if (conn->session_resumed && cipher_suite != conn->prev_cipher_suite) { wpa_printf(MSG_DEBUG, "TLSv1: Server selected a different " "cipher suite for a resumed connection (0x%04x != " "0x%04x)", cipher_suite, conn->prev_cipher_suite); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_ILLEGAL_PARAMETER); return -1; } if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for " "record layer"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } conn->prev_cipher_suite = cipher_suite; /* CompressionMethod compression_method */ if (end - pos < 1) goto decode_error; if (*pos != TLS_COMPRESSION_NULL) { wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected " "compression 0x%02x", *pos); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_ILLEGAL_PARAMETER); return -1; } pos++; if (end != pos) { /* TODO: ServerHello extensions */ wpa_hexdump(MSG_DEBUG, "TLSv1: Unexpected extra data in the " "end of ServerHello", pos, end - pos); goto decode_error; } if (conn->session_ticket_included && conn->session_ticket_cb) { /* TODO: include SessionTicket extension if one was included in * ServerHello */ int res = conn->session_ticket_cb( conn->session_ticket_cb_ctx, NULL, 0, conn->client_random, conn->server_random, conn->master_secret); if (res < 0) { wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback " "indicated failure"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_HANDSHAKE_FAILURE); return -1; } conn->use_session_ticket = !!res; } if ((conn->session_resumed || conn->use_session_ticket) && tls_derive_keys(conn, NULL, 0)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } *in_len = end - in_data; conn->state = (conn->session_resumed || conn->use_session_ticket) ? SERVER_CHANGE_CIPHER_SPEC : SERVER_CERTIFICATE; return 0; decode_error: wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ServerHello"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; }
u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) { u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr; struct os_time now; size_t len, i; wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello"); *out_len = 0; os_get_time(&now); WPA_PUT_BE32(conn->client_random, now.sec); if (os_get_random(conn->client_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " "client_random"); return NULL; } wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random", conn->client_random, TLS_RANDOM_LEN); len = 100 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len; hello = os_malloc(len); if (hello == NULL) return NULL; end = hello + len; rhdr = hello; pos = rhdr + TLS_RECORD_HEADER_LEN; /* opaque fragment[TLSPlaintext.length] */ /* Handshake */ hs_start = pos; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_CLIENT_HELLO; /* uint24 length (to be filled) */ hs_length = pos; pos += 3; /* body - ClientHello */ /* ProtocolVersion client_version */ WPA_PUT_BE16(pos, TLS_VERSION); pos += 2; /* Random random: uint32 gmt_unix_time, opaque random_bytes */ os_memcpy(pos, conn->client_random, TLS_RANDOM_LEN); pos += TLS_RANDOM_LEN; /* SessionID session_id */ *pos++ = conn->session_id_len; os_memcpy(pos, conn->session_id, conn->session_id_len); pos += conn->session_id_len; /* CipherSuite cipher_suites<2..2^16-1> */ WPA_PUT_BE16(pos, 2 * conn->num_cipher_suites); pos += 2; for (i = 0; i < conn->num_cipher_suites; i++) { WPA_PUT_BE16(pos, conn->cipher_suites[i]); pos += 2; } /* CompressionMethod compression_methods<1..2^8-1> */ *pos++ = 1; *pos++ = TLS_COMPRESSION_NULL; if (conn->client_hello_ext) { os_memcpy(pos, conn->client_hello_ext, conn->client_hello_ext_len); pos += conn->client_hello_ext_len; } WPA_PUT_BE24(hs_length, pos - hs_length - 3); tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, rhdr, end - rhdr, pos - hs_start, out_len) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(hello); return NULL; } conn->state = SERVER_HELLO; return hello; }
/** * tlsv1_client_handshake - Process TLS handshake * @conn: TLSv1 client connection data from tlsv1_client_init() * @in_data: Input data from TLS peer * @in_len: Input data length * @out_len: Length of the output buffer. * @appl_data: Pointer to application data pointer, or %NULL if dropped * @appl_data_len: Pointer to variable that is set to appl_data length * @need_more_data: Set to 1 if more data would be needed to complete * processing * Returns: Pointer to output data, %NULL on failure */ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, size_t *out_len, u8 **appl_data, size_t *appl_data_len, int *need_more_data) { const u8 *pos, *end; u8 *msg = NULL, *in_msg = NULL, *in_pos, *in_end, alert, ct; size_t in_msg_len; int no_appl_data; int used; if (need_more_data) *need_more_data = 0; if (conn->state == CLIENT_HELLO) { if (in_len) return NULL; return tls_send_client_hello(conn, out_len); } if (conn->partial_input) { if (wpabuf_resize(&conn->partial_input, in_len) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " "memory for pending record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); goto failed; } wpabuf_put_data(conn->partial_input, in_data, in_len); in_data = wpabuf_head(conn->partial_input); in_len = wpabuf_len(conn->partial_input); } if (in_data == NULL || in_len == 0) return NULL; pos = in_data; end = in_data + in_len; in_msg = os_malloc(in_len); if (in_msg == NULL) return NULL; /* Each received packet may include multiple records */ while (pos < end) { in_msg_len = in_len; used = tlsv1_record_receive(&conn->rl, pos, end - pos, in_msg, &in_msg_len, &alert); if (used < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Processing received " "record failed"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); goto failed; } if (used == 0) { struct wpabuf *partial; wpa_printf(MSG_DEBUG, "TLSv1: Need more data"); partial = wpabuf_alloc_copy(pos, end - pos); wpabuf_free(conn->partial_input); conn->partial_input = partial; if (conn->partial_input == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to " "allocate memory for pending " "record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); goto failed; } os_free(in_msg); if (need_more_data) *need_more_data = 1; return NULL; } ct = pos[0]; in_pos = in_msg; in_end = in_msg + in_msg_len; /* Each received record may include multiple messages of the * same ContentType. */ while (in_pos < in_end) { in_msg_len = in_end - in_pos; if (tlsv1_client_process_handshake(conn, ct, in_pos, &in_msg_len, appl_data, appl_data_len) < 0) goto failed; in_pos += in_msg_len; } pos += used; } os_free(in_msg); in_msg = NULL; no_appl_data = appl_data == NULL || *appl_data == NULL; msg = tlsv1_client_handshake_write(conn, out_len, no_appl_data); failed: os_free(in_msg); if (conn->alert_level) { wpabuf_free(conn->partial_input); conn->partial_input = NULL; conn->state = FAILED; os_free(msg); msg = tlsv1_client_send_alert(conn, conn->alert_level, conn->alert_description, out_len); } else if (msg == NULL) { msg = os_zalloc(1); *out_len = 0; } if (need_more_data == NULL || !(*need_more_data)) { wpabuf_free(conn->partial_input); conn->partial_input = NULL; } return msg; }
static int tls_write_client_finished(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { u8 *pos, *rhdr, *hs_start, *hs_length; size_t rlen, hlen; u8 verify_data[TLS_VERIFY_DATA_LEN]; u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; pos = *msgpos; wpa_printf(MSG_DEBUG, "TLSv1: Send Finished"); /* Encrypted Handshake Message: Finished */ hlen = MD5_MAC_LEN; if (conn->verify.md5_client == NULL || crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); conn->verify.md5_client = NULL; crypto_hash_finish(conn->verify.sha1_client, NULL, NULL); conn->verify.sha1_client = NULL; return -1; } conn->verify.md5_client = NULL; hlen = SHA1_MAC_LEN; if (conn->verify.sha1_client == NULL || crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN, &hlen) < 0) { conn->verify.sha1_client = NULL; tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } conn->verify.sha1_client = NULL; if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN, verify_data, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)", verify_data, TLS_VERIFY_DATA_LEN); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; /* Handshake */ hs_start = pos; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_FINISHED; /* uint24 length (to be filled) */ hs_length = pos; pos += 3; os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN); pos += TLS_VERIFY_DATA_LEN; WPA_PUT_BE24(hs_length, pos - hs_length - 3); tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, rhdr, end - rhdr, pos - hs_start, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } pos = rhdr + rlen; *msgpos = pos; return 0; }
static int tls_write_client_certificate_verify(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start; size_t rlen, hlen, clen; u8 hash[100], *hpos; enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; pos = *msgpos; wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateVerify"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; /* Handshake */ hs_start = pos; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY; /* uint24 length (to be filled) */ hs_length = pos; pos += 3; /* * RFC 2246: 7.4.3 and 7.4.8: * Signature signature * * RSA: * digitally-signed struct { * opaque md5_hash[16]; * opaque sha_hash[20]; * }; * * DSA: * digitally-signed struct { * opaque sha_hash[20]; * }; * * The hash values are calculated over all handshake messages sent or * received starting at ClientHello up to, but not including, this * CertificateVerify message, including the type and length fields of * the handshake messages. */ hpos = hash; #ifdef CONFIG_TLSV12 if (conn->rl.tls_version == TLS_VERSION_1_2) { hlen = SHA256_MAC_LEN; if (conn->verify.sha256_cert == NULL || crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) < 0) { conn->verify.sha256_cert = NULL; tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } conn->verify.sha256_cert = NULL; /* * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 * * DigestInfo ::= SEQUENCE { * digestAlgorithm DigestAlgorithm, * digest OCTET STRING * } * * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} * * DER encoded DigestInfo for SHA256 per RFC 3447: * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || * H */ os_memmove(hash + 19, hash, hlen); hlen += 19; os_memcpy(hash, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65" "\x03\x04\x02\x01\x05\x00\x04\x20", 19); } else { #endif /* CONFIG_TLSV12 */ if (alg == SIGN_ALG_RSA) { hlen = MD5_MAC_LEN; if (conn->verify.md5_cert == NULL || crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); conn->verify.md5_cert = NULL; crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL); conn->verify.sha1_cert = NULL; return -1; } hpos += MD5_MAC_LEN; } else crypto_hash_finish(conn->verify.md5_cert, NULL, NULL); conn->verify.md5_cert = NULL; hlen = SHA1_MAC_LEN; if (conn->verify.sha1_cert == NULL || crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) { conn->verify.sha1_cert = NULL; tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } conn->verify.sha1_cert = NULL; if (alg == SIGN_ALG_RSA) hlen += MD5_MAC_LEN; #ifdef CONFIG_TLSV12 } #endif /* CONFIG_TLSV12 */ wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen); #ifdef CONFIG_TLSV12 if (conn->rl.tls_version >= TLS_VERSION_1_2) { /* * RFC 5246, 4.7: * TLS v1.2 adds explicit indication of the used signature and * hash algorithms. * * struct { * HashAlgorithm hash; * SignatureAlgorithm signature; * } SignatureAndHashAlgorithm; */ *pos++ = TLS_HASH_ALG_SHA256; *pos++ = TLS_SIGN_ALG_RSA; } #endif /* CONFIG_TLSV12 */ /* * RFC 2246, 4.7: * In digital signing, one-way hash functions are used as input for a * signing algorithm. A digitally-signed element is encoded as an * opaque vector <0..2^16-1>, where the length is specified by the * signing algorithm and key. * * In RSA signing, a 36-byte structure of two hashes (one SHA and one * MD5) is signed (encrypted with the private key). It is encoded with * PKCS #1 block type 0 or type 1 as described in [PKCS1]. */ signed_start = pos; /* length to be filled */ pos += 2; clen = end - pos; if (conn->cred == NULL || crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen, pos, &clen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } WPA_PUT_BE16(signed_start, clen); pos += clen; WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, rhdr, end - rhdr, hs_start, pos - hs_start, &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } pos = rhdr + rlen; tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); *msgpos = pos; return 0; }