From 82adb5ce0f584a802f74c15432a1880f70b4b1be Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 4 Jun 2025 17:14:08 +0200 Subject: [PATCH 1/5] unit-tests: Serial number tests depend on X.509 certificate parsing Requires additional plugin features, but if this is available, the others are usually as well. --- src/libstrongswan/tests/tests.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstrongswan/tests/tests.h b/src/libstrongswan/tests/tests.h index 9ad712d53..bcd8dffd2 100644 --- a/src/libstrongswan/tests/tests.h +++ b/src/libstrongswan/tests/tests.h @@ -44,7 +44,7 @@ TEST_SUITE_DEPEND(rsa_oaep_sha512_suite_create, PRIVKEY_DECRYPT, ENCRYPT_RSA_OAE TEST_SUITE_DEPEND(certpolicy_suite_create, CERT_ENCODE, CERT_X509) TEST_SUITE_DEPEND(certnames_suite_create, CERT_ENCODE, CERT_X509) TEST_SUITE_DEPEND(serial_gen_suite_create, CERT_ENCODE, CERT_X509) -TEST_SUITE(serial_parse_suite_create) +TEST_SUITE_DEPEND(serial_parse_suite_create, CERT_DECODE, CERT_X509) TEST_SUITE(host_suite_create) TEST_SUITE(printf_suite_create) TEST_SUITE(auth_cfg_suite_create) From e864b8a8b197efd29bf58c7a46fc5ed4930afdac Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 3 Jun 2025 17:37:24 +0200 Subject: [PATCH 2/5] fetcher: Remove unused FETCH_HTTP_VERSION_1_0 option Was only used by the removed scepclient and does not serve any purpose nowadays anyway. --- src/libstrongswan/fetcher/fetcher.h | 6 --- src/libstrongswan/fetcher/fetcher_manager.c | 3 -- src/libstrongswan/plugins/curl/curl_fetcher.c | 6 --- src/libstrongswan/plugins/soup/soup_fetcher.c | 10 ----- .../plugins/winhttp/winhttp_fetcher.c | 11 +----- .../tests/suites/test_fetch_http.c | 37 ++++++++----------- 6 files changed, 17 insertions(+), 56 deletions(-) diff --git a/src/libstrongswan/fetcher/fetcher.h b/src/libstrongswan/fetcher/fetcher.h index b4bd78809..dc8e16a73 100644 --- a/src/libstrongswan/fetcher/fetcher.h +++ b/src/libstrongswan/fetcher/fetcher.h @@ -69,12 +69,6 @@ enum fetcher_option_t { */ FETCH_REQUEST_HEADER, - /** - * Use HTTP Version 1.0 instead of 1.1. - * No additional argument is needed. - */ - FETCH_HTTP_VERSION_1_0, - /** * Timeout to use for fetch, in seconds. * Additional argument is u_int diff --git a/src/libstrongswan/fetcher/fetcher_manager.c b/src/libstrongswan/fetcher/fetcher_manager.c index fe371a6d6..cd636f45a 100644 --- a/src/libstrongswan/fetcher/fetcher_manager.c +++ b/src/libstrongswan/fetcher/fetcher_manager.c @@ -103,9 +103,6 @@ METHOD(fetcher_manager_t, fetch, status_t, good = fetcher->set_option(fetcher, opt, va_arg(args, char*)); continue; - case FETCH_HTTP_VERSION_1_0: - good = fetcher->set_option(fetcher, opt); - continue; case FETCH_TIMEOUT: good = fetcher->set_option(fetcher, opt, va_arg(args, u_int)); diff --git a/src/libstrongswan/plugins/curl/curl_fetcher.c b/src/libstrongswan/plugins/curl/curl_fetcher.c index c788e3f68..80d989c2d 100644 --- a/src/libstrongswan/plugins/curl/curl_fetcher.c +++ b/src/libstrongswan/plugins/curl/curl_fetcher.c @@ -205,12 +205,6 @@ METHOD(fetcher_t, set_option, bool, this->headers = curl_slist_append(this->headers, header); break; } - case FETCH_HTTP_VERSION_1_0: - { - curl_easy_setopt(this->curl, CURLOPT_HTTP_VERSION, - CURL_HTTP_VERSION_1_0); - break; - } case FETCH_TIMEOUT: { this->timeout = va_arg(args, u_int); diff --git a/src/libstrongswan/plugins/soup/soup_fetcher.c b/src/libstrongswan/plugins/soup/soup_fetcher.c index 072dfd7b2..40b92e14f 100644 --- a/src/libstrongswan/plugins/soup/soup_fetcher.c +++ b/src/libstrongswan/plugins/soup/soup_fetcher.c @@ -55,11 +55,6 @@ struct private_soup_fetcher_t { */ u_int timeout; - /** - * HTTP request version - */ - SoupHTTPVersion version; - /** * Fetcher callback function */ @@ -116,7 +111,6 @@ METHOD(fetcher_t, fetch, status_t, soup_message_set_request(message, this->type, SOUP_MEMORY_STATIC, this->data.ptr, this->data.len); } - soup_message_set_http_version(message, this->version); soup_message_body_set_accumulate(message->response_body, FALSE); g_signal_connect(message, "got-chunk", G_CALLBACK(soup_cb), &data); data.session = soup_session_new(); @@ -158,9 +152,6 @@ METHOD(fetcher_t, set_option, bool, case FETCH_REQUEST_TYPE: this->type = va_arg(args, char*); break; - case FETCH_HTTP_VERSION_1_0: - this->version = SOUP_HTTP_1_0; - break; case FETCH_TIMEOUT: this->timeout = va_arg(args, u_int); break; @@ -200,7 +191,6 @@ soup_fetcher_t *soup_fetcher_create() }, }, .method = SOUP_METHOD_GET, - .version = SOUP_HTTP_1_1, .timeout = DEFAULT_TIMEOUT, .cb = fetcher_default_callback, ); diff --git a/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c b/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c index e387bbb93..0be9b3eff 100644 --- a/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c +++ b/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c @@ -54,11 +54,6 @@ struct private_winhttp_fetcher_t { */ chunk_t request; - /** - * HTTP version string to use - */ - LPWSTR version; - /** * Optional HTTP headers, as allocated LPWSTR */ @@ -264,7 +259,7 @@ METHOD(fetcher_t, fetch, status_t, connection = WinHttpConnect(this->session, host, port, 0); if (connection) { - request = WinHttpOpenRequest(connection, method, path, this->version, + request = WinHttpOpenRequest(connection, method, path, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, flags); if (request) @@ -334,9 +329,6 @@ METHOD(fetcher_t, set_option, bool, case FETCH_REQUEST_HEADER: supported = append_header(this, va_arg(args, char*)); break; - case FETCH_HTTP_VERSION_1_0: - this->version = L"HTTP/1.0"; - break; case FETCH_TIMEOUT: this->timeout = va_arg(args, u_int) * 1000; break; @@ -381,7 +373,6 @@ winhttp_fetcher_t *winhttp_fetcher_create() .destroy = _destroy, }, }, - .version = L"HTTP/1.1", .cb = fetcher_default_callback, .headers = linked_list_create(), .session = WinHttpOpen(L"strongSwan WinHTTP fetcher", diff --git a/src/libstrongswan/tests/suites/test_fetch_http.c b/src/libstrongswan/tests/suites/test_fetch_http.c index b087a1e79..38460297c 100644 --- a/src/libstrongswan/tests/suites/test_fetch_http.c +++ b/src/libstrongswan/tests/suites/test_fetch_http.c @@ -27,8 +27,6 @@ typedef struct { /* HTTP Method */ char *meth; - /* HTTP 1.x minor version */ - int minor; /* host to connect to */ char *host; /* HTTP service port */ @@ -117,8 +115,8 @@ static bool servicing(void *data, stream_t *stream) switch (nr++) { case 0: - snprintf(hdr, sizeof(hdr), "%s %s HTTP/1.%u", - test->meth, test->path, test->minor); + snprintf(hdr, sizeof(hdr), "%s %s HTTP/1.1", + test->meth, test->path); ck_assert_str_eq(hdr, start); break; default: @@ -158,7 +156,7 @@ static bool servicing(void *data, stream_t *stream) } /* response headers */ - snprintf(buf, sizeof(buf), "HTTP/1.%u %u OK\r\n", test->minor, test->code); + snprintf(buf, sizeof(buf), "HTTP/1.1 %u OK\r\n", test->code); ck_assert(stream->write_all(stream, buf, strlen(buf))); /* if the response code indicates an error the following write operations @@ -188,13 +186,13 @@ static bool servicing(void *data, stream_t *stream) } static test_service_t gtests[] = { - { "GET", 1, "127.0.0.1", 6543, "/a/test/?b=c", NULL, + { "GET", "127.0.0.1", 6543, "/a/test/?b=c", NULL, NULL, 0, "\x12\x34", 2, 0 }, - { "GET", 0, "localhost", 6543, "/", NULL, + { "GET", "localhost", 6543, "/", NULL, NULL, 0, NULL, 0, 0 }, - { "GET", 0, "127.0.0.1", 6543, "/largefile", NULL, + { "GET", "127.0.0.1", 6543, "/largefile", NULL, NULL, 0, large, sizeof(large), 0 }, - { "GET", 1, "[::1]", 6543, "/ipv6-url", NULL, + { "GET", "[::1]", 6543, "/ipv6-url", NULL, NULL, 0, "\x00\r\n\r\x00testdatablabla", 20, 0 }, }; @@ -214,9 +212,7 @@ START_TEST(test_get) snprintf(uri, sizeof(uri), "http://%s:%u%s", gtests[_i].host, gtests[_i].port, gtests[_i].path); - status = lib->fetcher->fetch(lib->fetcher, uri, &data, - !gtests[_i].minor ? FETCH_HTTP_VERSION_1_0 : FETCH_END, - FETCH_END); + status = lib->fetcher->fetch(lib->fetcher, uri, &data, FETCH_END); ck_assert_int_eq(status, SUCCESS); expected = chunk_create(gtests[_i].res, gtests[_i].res_len); ck_assert_msg(chunk_compare(expected, data) == 0, @@ -229,11 +225,11 @@ END_TEST static test_service_t ptests[] = { - { "POST", 1, "127.0.0.1", 6543, "/a/test/?b=c", "application/binary", + { "POST", "127.0.0.1", 6543, "/a/test/?b=c", "application/binary", "\x23\x45", 2, "\x12\x34", 2, 0 }, - { "POST", 0, "localhost", 6543, "/largefile", "application/x-large", + { "POST", "localhost", 6543, "/largefile", "application/x-large", large, sizeof(large), large, sizeof(large), 0 }, - { "POST", 1, "[::1]", 6543, "/ipv6-url", "text/plain", + { "POST", "[::1]", 6543, "/ipv6-url", "text/plain", "\x00\r\n\r\x00testdatablabla", 20, "\x00\r\n\r\x00testdatablabla", 20, 0 }, }; @@ -257,7 +253,6 @@ START_TEST(test_post) FETCH_REQUEST_TYPE, ptests[_i].type, FETCH_REQUEST_DATA, chunk_create(ptests[_i].req, ptests[_i].req_len), - !ptests[_i].minor ? FETCH_HTTP_VERSION_1_0 : FETCH_END, FETCH_END); ck_assert_int_eq(status, SUCCESS); expected = chunk_create(ptests[_i].res, ptests[_i].res_len); @@ -271,11 +266,11 @@ END_TEST static test_service_t rtests[] = { - { "GET", 1, "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 200 }, - { "GET", 1, "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 204 }, - { "GET", 1, "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 400 }, - { "GET", 1, "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 404 }, - { "GET", 1, "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 500 }, + { "GET", "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 200 }, + { "GET", "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 204 }, + { "GET", "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 400 }, + { "GET", "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 404 }, + { "GET", "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 500 }, }; START_TEST(test_response_code) From 6ddabf52d5f335147601c7c2797da21820efafb8 Mon Sep 17 00:00:00 2001 From: Mike Gorse Date: Tue, 3 Jun 2025 16:13:41 -0500 Subject: [PATCH 3/5] soup: Port to libsoup 3 --- configure.ac | 2 +- src/libstrongswan/plugins/soup/soup_fetcher.c | 63 ++++++++----------- 2 files changed, 27 insertions(+), 38 deletions(-) diff --git a/configure.ac b/configure.ac index bf054e1e3..db8579416 100644 --- a/configure.ac +++ b/configure.ac @@ -1044,7 +1044,7 @@ if test x$unbound = xtrue; then fi if test x$soup = xtrue; then - PKG_CHECK_MODULES(soup, [libsoup-2.4]) + PKG_CHECK_MODULES(soup, [libsoup-3.0]) AC_SUBST(soup_CFLAGS) AC_SUBST(soup_LIBS) fi diff --git a/src/libstrongswan/plugins/soup/soup_fetcher.c b/src/libstrongswan/plugins/soup/soup_fetcher.c index 40b92e14f..6c0cba2cb 100644 --- a/src/libstrongswan/plugins/soup/soup_fetcher.c +++ b/src/libstrongswan/plugins/soup/soup_fetcher.c @@ -66,36 +66,13 @@ struct private_soup_fetcher_t { u_int *result; }; -/** - * Data to pass to soup callback - */ -typedef struct { - fetcher_callback_t cb; - void *user; - SoupSession *session; -} cb_data_t; - -/** - * Soup callback invoking our callback - */ -static void soup_cb(SoupMessage *message, SoupBuffer *chunk, cb_data_t *data) -{ - if (!data->cb(data->user, chunk_create((u_char*)chunk->data, chunk->length))) - { - soup_session_cancel_message(data->session, message, - SOUP_STATUS_CANCELLED); - } -} - METHOD(fetcher_t, fetch, status_t, private_soup_fetcher_t *this, char *uri, void *userdata) { SoupMessage *message; status_t status = FAILED; - cb_data_t data = { - .cb = this->cb, - .user = userdata, - }; + GBytes *request_body, *res; + SoupSession *session; message = soup_message_new(this->method, uri); if (!message) @@ -108,31 +85,43 @@ METHOD(fetcher_t, fetch, status_t, } if (this->type) { - soup_message_set_request(message, this->type, SOUP_MEMORY_STATIC, - this->data.ptr, this->data.len); + request_body = g_bytes_new_static(this->data.ptr, this->data.len); + soup_message_set_request_body_from_bytes(message, this->type, + request_body); + g_bytes_unref(request_body); } - soup_message_body_set_accumulate(message->response_body, FALSE); - g_signal_connect(message, "got-chunk", G_CALLBACK(soup_cb), &data); - data.session = soup_session_new(); - g_object_set(G_OBJECT(data.session), - SOUP_SESSION_TIMEOUT, (guint)this->timeout, NULL); + session = soup_session_new_with_options("timeout", (guint)this->timeout, + NULL); DBG2(DBG_LIB, "sending http request to '%s'...", uri); - soup_session_send_message(data.session, message); + res = soup_session_send_and_read(session, message, NULL, NULL); if (this->result) { - *this->result = message->status_code; + *this->result = soup_message_get_status(message); } - if (SOUP_STATUS_IS_SUCCESSFUL(message->status_code)) + if (SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(message))) { status = SUCCESS; } else if (!this->result) { /* only log an error if the code is not returned */ - DBG1(DBG_LIB, "HTTP request failed: %s", message->reason_phrase); + DBG1(DBG_LIB, "HTTP request failed: %s", + soup_message_get_reason_phrase(message)); + } + if (res) + { + gpointer data; + gsize data_len; + + data = g_bytes_unref_to_data(res, &data_len); + if (!this->cb(userdata, chunk_create(data, data_len))) + { + status = FAILED; + } + g_free(data); } g_object_unref(G_OBJECT(message)); - g_object_unref(G_OBJECT(data.session)); + g_object_unref(G_OBJECT(session)); return status; } From 9dbb15dea97b15d5fd8c1db56da41c91df91fb4d Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 4 Jun 2025 17:51:55 +0200 Subject: [PATCH 4/5] leak-detective: Remove whitelisted libsoup2.x functions As mentioned in 0f141fb095a41a9fdfe5c111269eb643dc643494, we can't really whitelist the "leaks" in GLib, so don't even try to do anything with libsoup3.x. --- src/libstrongswan/utils/leak_detective.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c index 999c39167..038cd0a0c 100644 --- a/src/libstrongswan/utils/leak_detective.c +++ b/src/libstrongswan/utils/leak_detective.c @@ -563,16 +563,6 @@ static char *whitelist[] = { "xmlInitParserCtxt", /* libcurl */ "Curl_client_write", - /* libsoup */ - "soup_add_timeout", - "soup_headers_parse_response", - "soup_message_headers_append", - "soup_message_headers_clear", - "soup_message_headers_get_list", - "soup_message_headers_get_one", - "soup_session_abort", - "soup_session_get_type", - "soup_session_remove_feature", /* libldap */ "ldap_int_initialize", /* ClearSilver */ From b8108a4c3c9a4d4eb9bbc91781290921d07da203 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 4 Jun 2025 18:07:35 +0200 Subject: [PATCH 5/5] github: Use libsoup 3 for tests Requires installing libxml2-dev explicitly for the alpine build as libsoup-dev had a dependency on it. --- scripts/test.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/test.sh b/scripts/test.sh index 65545ea0d..3adce6c00 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -305,7 +305,7 @@ all|alpine|codeql|coverage|sonarcloud|no-dbg|no-testable-ke) if [ "$TEST" = "no-testable-ke" ]; then CONFIG="$CONFIG --without-testable-ke" fi - DEPS="$DEPS libcurl4-gnutls-dev libsoup2.4-dev libunbound-dev libldns-dev + DEPS="$DEPS libcurl4-gnutls-dev libsoup-3.0-dev libunbound-dev libldns-dev libmysqlclient-dev libsqlite3-dev clearsilver-dev libfcgi-dev libldap2-dev libpcsclite-dev libpam0g-dev binutils-dev libnm-dev libgcrypt20-dev libjson-c-dev libtspi-dev libsystemd-dev @@ -318,11 +318,11 @@ all|alpine|codeql|coverage|sonarcloud|no-dbg|no-testable-ke) fi if [ "$TEST" = "alpine" ]; then # override the whole list for alpine - DEPS="git gmp-dev openldap-dev curl-dev ldns-dev unbound-dev libsoup-dev - tpm2-tss-dev tpm2-tss-sys mariadb-dev wolfssl-dev libgcrypt-dev - botan3-dev pcsc-lite-dev networkmanager-dev linux-pam-dev - iptables-dev libselinux-dev binutils-dev libunwind-dev ruby - py3-setuptools py3-build py3-tox" + DEPS="git gmp-dev openldap-dev curl-dev ldns-dev unbound-dev libsoup3-dev + libxml2-dev tpm2-tss-dev tpm2-tss-sys mariadb-dev wolfssl-dev + libgcrypt-dev botan3-dev pcsc-lite-dev networkmanager-dev + linux-pam-dev iptables-dev libselinux-dev binutils-dev libunwind-dev + ruby py3-setuptools py3-build py3-tox" # musl does not provide backtrace(), so use libunwind CONFIG="$CONFIG --enable-unwind-backtraces" # alpine doesn't have systemd