/** * 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; }
/** * 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_server_decrypt - Decrypt data from TLS tunnel * @conn: TLSv1 server connection data from tlsv1_server_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_server_decrypt(struct tlsv1_server *conn, const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len) { const u8 *in_end, *pos; int used; u8 alert, *out_end, *out_pos, ct; 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) { ct = pos[0]; olen = out_end - out_pos; used = tlsv1_record_receive(&conn->rl, pos, in_end - pos, out_pos, &olen, &alert); if (used < 0) { tlsv1_server_log(conn, "Record layer processing failed"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); return -1; } if (used == 0) { /* need more data */ wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not " "yet supported"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); return -1; } if (ct == TLS_CONTENT_TYPE_ALERT) { if (olen < 2) { tlsv1_server_log(conn, "Alert underflow"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } tlsv1_server_log(conn, "Received alert %d:%d", out_pos[0], out_pos[1]); if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { /* Continue processing */ pos += used; continue; } tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, out_pos[1]); return -1; } if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { tlsv1_server_log(conn, "Unexpected content type 0x%x", pos[0]); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } #ifdef CONFIG_TESTING_OPTIONS if ((conn->test_flags && (TLS_BREAK_VERIFY_DATA | TLS_BREAK_SRV_KEY_X_HASH | TLS_BREAK_SRV_KEY_X_SIGNATURE)) && !conn->test_failure_reported) { tlsv1_server_log(conn, "TEST-FAILURE: Client ApplData received after invalid handshake"); conn->test_failure_reported = 1; } #endif /* CONFIG_TESTING_OPTIONS */ out_pos += olen; if (out_pos > out_end) { wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough " "for processing the received record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } pos += used; } return out_pos - out_data; }
/** * tlsv1_server_handshake - Process TLS handshake * @conn: TLSv1 server connection data from tlsv1_server_init() * @in_data: Input data from TLS peer * @in_len: Input data length * @out_len: Length of the output buffer. * Returns: Pointer to output data, %NULL on failure */ u8 * tlsv1_server_handshake(struct tlsv1_server *conn, const u8 *in_data, size_t in_len, size_t *out_len) { const u8 *pos, *end; u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct; size_t in_msg_len; int used; if (in_data == NULL || in_len == 0) { wpa_printf(MSG_DEBUG, "TLSv1: No input data to server"); 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"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); goto failed; } if (used == 0) { /* need more data */ wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not " "yet supported"); tlsv1_server_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_server_process_handshake(conn, ct, in_pos, &in_msg_len) < 0) goto failed; in_pos += in_msg_len; } pos += used; } os_free(in_msg); in_msg = NULL; msg = tlsv1_server_handshake_write(conn, out_len); failed: os_free(in_msg); if (conn->alert_level) { if (conn->state == FAILED) { /* Avoid alert loops */ wpa_printf(MSG_DEBUG, "TLSv1: Drop alert loop"); os_free(msg); return NULL; } conn->state = FAILED; os_free(msg); msg = tlsv1_server_send_alert(conn, conn->alert_level, conn->alert_description, out_len); } 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; }
/** * 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; }
/** * tlsv1_server_decrypt - Decrypt data from TLS tunnel * @conn: TLSv1 server connection data from tlsv1_server_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_server_decrypt(struct tlsv1_server *conn, const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len) { const u8 *in_end, *pos; int used; u8 alert, *out_end, *out_pos, ct; 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) { ct = pos[0]; olen = out_end - out_pos; 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"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); return -1; } if (used == 0) { /* need more data */ wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not " "yet supported"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); return -1; } if (ct == TLS_CONTENT_TYPE_ALERT) { if (olen < 2) { wpa_printf(MSG_DEBUG, "TLSv1: Alert " "underflow"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } 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; } tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, out_pos[1]); return -1; } if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " "0x%x", pos[0]); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } out_pos += olen; if (out_pos > out_end) { wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough " "for processing the received record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; } pos += used; } return out_pos - out_data; }