fix(esp_http_client): fix digest-auth leaks and credential/handle use-after-free

This commit is contained in:
Ashish Sharma
2026-06-26 18:20:05 +08:00
parent ea005e8316
commit 89f40a3b93
2 changed files with 30 additions and 5 deletions

View File

@@ -474,10 +474,17 @@ esp_err_t esp_http_client_set_username(esp_http_client_handle_t client, const ch
ESP_LOGE(TAG, "client must not be NULL");
return ESP_ERR_INVALID_ARG;
}
/* Duplicate first so that passing the current username back in (e.g. the pointer
* returned by esp_http_client_get_username()) is safe: the old buffer is freed only
* after the copy succeeds, avoiding a use-after-free on self-aliasing (CWE-416). */
char *new_username = username ? strdup(username) : NULL;
if (username != NULL && new_username == NULL) {
return ESP_ERR_NO_MEM;
}
if (client->connection_info.username != NULL) {
free(client->connection_info.username);
}
client->connection_info.username = username ? strdup(username) : NULL;
client->connection_info.username = new_username;
return ESP_OK;
}
@@ -520,11 +527,19 @@ esp_err_t esp_http_client_set_password(esp_http_client_handle_t client, const ch
ESP_LOGE(TAG, "client must not be NULL");
return ESP_ERR_INVALID_ARG;
}
/* Duplicate first so that passing the current password back in (e.g. the pointer
* returned by esp_http_client_get_password()) is safe: zeroize and free the old buffer
* only after the copy succeeds, avoiding a use-after-free / credential corruption
* (CWE-416) caused by memset zeroing the source before strdup reads it. */
char *new_password = password ? strdup(password) : NULL;
if (password != NULL && new_password == NULL) {
return ESP_ERR_NO_MEM;
}
if (client->connection_info.password != NULL) {
memset(client->connection_info.password, 0, strlen(client->connection_info.password));
free(client->connection_info.password);
}
client->connection_info.password = password ? strdup(password) : NULL;
client->connection_info.password = new_password;
return ESP_OK;
}
@@ -1720,14 +1735,17 @@ static esp_err_t esp_http_client_connect(esp_http_client_handle_t client)
}
}
client->state = HTTP_STATE_CONNECTED;
http_dispatch_event(client, HTTP_EVENT_ON_CONNECTED, NULL, 0);
http_dispatch_event_to_event_loop(HTTP_EVENT_ON_CONNECTED, &client, sizeof(esp_http_client_handle_t));
#ifdef CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS
/* Perform handle-dependent session-ticket bookkeeping before dispatching the user
* callback: a synchronous HTTP_EVENT_ON_CONNECTED handler is permitted to destroy
* the client, so dereferencing the handle afterwards would be a UAF (CWE-416). */
if (client->session_ticket_state != SESSION_TICKET_UNUSED) {
esp_transport_ssl_session_ticket_operation(client->transport, ESP_TRANSPORT_SESSION_TICKET_SAVE);
client->session_ticket_state = SESSION_TICKET_SAVED;
}
#endif
http_dispatch_event(client, HTTP_EVENT_ON_CONNECTED, NULL, 0);
http_dispatch_event_to_event_loop(HTTP_EVENT_ON_CONNECTED, &client, sizeof(esp_http_client_handle_t));
}
return ESP_OK;

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -201,11 +201,18 @@ char *http_auth_digest(const char *username, const char *password, esp_http_auth
if (rc < 0) {
ESP_LOGE(TAG, "asprintf() returned: %d", rc);
ret = ESP_FAIL;
free(auth_str);
auth_str = NULL;
goto _digest_exit;
}
/* http_utils_append_string() realloc()s auth_str; on failure it returns NULL
* without freeing the original buffer, so keep a handle to free it here. */
char *prev_auth_str = auth_str;
auth_str = http_utils_append_string(&auth_str, temp_auth_str, strlen(temp_auth_str));
if (!auth_str) {
ret = ESP_FAIL;
free(prev_auth_str);
free(temp_auth_str);
goto _digest_exit;
}
free(temp_auth_str);