From: Daan De Meyer Date: Mon, 20 Apr 2026 19:47:38 +0000 (+0000) Subject: shared: load libgnutls and libmicrohttpd via dlopen X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=909efb6f2e85b74e8b982027f42a344f7199158e;p=thirdparty%2Fsystemd.git shared: load libgnutls and libmicrohttpd via dlopen Convert the GnuTLS and libmicrohttpd usage in journal-remote to the dlopen pattern used by other optional shared libraries. A new src/shared/gnutls-util.{h,c} declares the GnuTLS entry points via DLSYM_PROTOTYPE and resolves them in dlopen_gnutls(); microhttpd-util is moved from src/journal-remote to src/shared and gains analogous DLSYM_PROTOTYPEs plus dlopen_microhttpd(). Callers in journal-gatewayd, journal-remote-main and microhttpd-util itself call the sym_* wrappers and invoke dlopen_gnutls()/dlopen_microhttpd() at their entry points. setup_gnutls_logger() no longer fails when libgnutls is missing at runtime; it logs a notice and returns 0 so journal-gatewayd starts up without TLS dependencies installed. The meson files gain libgnutls_cflags and libmicrohttpd_cflags partial dependencies that expose include paths and compile flags only. Every systemd-journal-{gatewayd,remote,upload} target switches to the cflags variant, dropping the direct libgnutls/libmicrohttpd link. The gatewayd->remote object-sharing dance for microhttpd-util.o goes away since the code now lives in libshared. test-dlopen-so gains assertions for dlopen_gnutls and dlopen_microhttpd. --- diff --git a/meson.build b/meson.build index 287456ad6eb..95d95c43cd2 100644 --- a/meson.build +++ b/meson.build @@ -1181,6 +1181,7 @@ libmicrohttpd = dependency('libmicrohttpd', version : '>= 0.9.33', required : get_option('microhttpd')) conf.set10('HAVE_MICROHTTPD', libmicrohttpd.found()) +libmicrohttpd_cflags = libmicrohttpd.partial_dependency(includes: true, compile_args: true) libcryptsetup = get_option('libcryptsetup') libcryptsetup_plugins = get_option('libcryptsetup-plugins') @@ -1252,6 +1253,7 @@ libgnutls = dependency('gnutls', version : '>= 3.1.4', required : get_option('gnutls')) conf.set10('HAVE_GNUTLS', libgnutls.found()) +libgnutls_cflags = libgnutls.partial_dependency(includes: true, compile_args: true) libopenssl = dependency('openssl', version : '>= 3.0.0', diff --git a/mkosi/mkosi.conf.d/centos-fedora/mkosi.conf b/mkosi/mkosi.conf.d/centos-fedora/mkosi.conf index dd00fa737cf..fc9ffd58c96 100644 --- a/mkosi/mkosi.conf.d/centos-fedora/mkosi.conf +++ b/mkosi/mkosi.conf.d/centos-fedora/mkosi.conf @@ -43,6 +43,7 @@ Packages= kernel-core knot libcap-ng-utils + libmicrohttpd libucontext man-db nmap-ncat diff --git a/mkosi/mkosi.conf.d/opensuse/mkosi.conf b/mkosi/mkosi.conf.d/opensuse/mkosi.conf index b0593e3f1ab..1198d2c15c4 100644 --- a/mkosi/mkosi.conf.d/opensuse/mkosi.conf +++ b/mkosi/mkosi.conf.d/opensuse/mkosi.conf @@ -57,6 +57,7 @@ Packages= libcap-progs libdw-devel libdw1 + libmicrohttpd12 libtss2-tcti-device0 libz1 man diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index 7cb88462249..42e3f6df295 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -311,7 +311,7 @@ static int request_parse_accept( assert(m); assert(connection); - header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Accept"); + header = sym_MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Accept"); if (!header) return 0; @@ -459,7 +459,7 @@ static int request_parse_range( assert(m); assert(connection); - range = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Range"); + range = sym_MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Range"); if (!range) return 0; @@ -566,7 +566,7 @@ static int request_parse_arguments( assert(connection); m->argument_parse_error = 0; - MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, request_parse_arguments_iterator, m); + sym_MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, request_parse_arguments_iterator, m); return m->argument_parse_error; } @@ -616,14 +616,14 @@ static int request_handler_entries( if (r < 0) return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to seek in journal."); - response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_entries, m, NULL); + response = sym_MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_entries, m, NULL); if (!response) return respond_oom(connection); - if (MHD_add_response_header(response, "Content-Type", mime_types[m->mode]) == MHD_NO) + if (sym_MHD_add_response_header(response, "Content-Type", mime_types[m->mode]) == MHD_NO) return respond_oom(connection); - return MHD_queue_response(connection, MHD_HTTP_OK, response); + return sym_MHD_queue_response(connection, MHD_HTTP_OK, response); } static int output_field(FILE *f, OutputMode m, const char *d, size_t l) { @@ -744,14 +744,14 @@ static int request_handler_fields( if (r < 0) return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to query unique fields."); - response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_fields, m, NULL); + response = sym_MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_fields, m, NULL); if (!response) return respond_oom(connection); - if (MHD_add_response_header(response, "Content-Type", mime_types[m->mode == OUTPUT_JSON ? OUTPUT_JSON : OUTPUT_SHORT]) == MHD_NO) + if (sym_MHD_add_response_header(response, "Content-Type", mime_types[m->mode == OUTPUT_JSON ? OUTPUT_JSON : OUTPUT_SHORT]) == MHD_NO) return respond_oom(connection); - return MHD_queue_response(connection, MHD_HTTP_OK, response); + return sym_MHD_queue_response(connection, MHD_HTTP_OK, response); } static int request_handler_redirect( @@ -767,16 +767,16 @@ static int request_handler_redirect( if (asprintf(&page, "Please continue to the journal browser.", target) < 0) return respond_oom(connection); - response = MHD_create_response_from_buffer(strlen(page), page, MHD_RESPMEM_MUST_FREE); + response = sym_MHD_create_response_from_buffer(strlen(page), page, MHD_RESPMEM_MUST_FREE); if (!response) return respond_oom(connection); TAKE_PTR(page); - if (MHD_add_response_header(response, "Content-Type", "text/html") == MHD_NO || - MHD_add_response_header(response, "Location", target) == MHD_NO) + if (sym_MHD_add_response_header(response, "Content-Type", "text/html") == MHD_NO || + sym_MHD_add_response_header(response, "Location", target) == MHD_NO) return respond_oom(connection); - return MHD_queue_response(connection, MHD_HTTP_MOVED_PERMANENTLY, response); + return sym_MHD_queue_response(connection, MHD_HTTP_MOVED_PERMANENTLY, response); } static int request_handler_file( @@ -799,15 +799,15 @@ static int request_handler_file( if (fstat(fd, &st) < 0) return mhd_respondf(connection, errno, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to stat file: %m"); - response = MHD_create_response_from_fd_at_offset64(st.st_size, fd, 0); + response = sym_MHD_create_response_from_fd_at_offset64(st.st_size, fd, 0); if (!response) return respond_oom(connection); TAKE_FD(fd); - if (MHD_add_response_header(response, "Content-Type", mime_type) == MHD_NO) + if (sym_MHD_add_response_header(response, "Content-Type", mime_type) == MHD_NO) return respond_oom(connection); - return MHD_queue_response(connection, MHD_HTTP_OK, response); + return sym_MHD_queue_response(connection, MHD_HTTP_OK, response); } static int get_virtualization(char **v) { @@ -899,15 +899,15 @@ static int request_handler_machine( if (r < 0) return respond_oom(connection); - response = MHD_create_response_from_buffer(strlen(json), json, MHD_RESPMEM_MUST_FREE); + response = sym_MHD_create_response_from_buffer(strlen(json), json, MHD_RESPMEM_MUST_FREE); if (!response) return respond_oom(connection); TAKE_PTR(json); - if (MHD_add_response_header(response, "Content-Type", "application/json") == MHD_NO) + if (sym_MHD_add_response_header(response, "Content-Type", "application/json") == MHD_NO) return respond_oom(connection); - return MHD_queue_response(connection, MHD_HTTP_OK, response); + return sym_MHD_queue_response(connection, MHD_HTTP_OK, response); } static int output_boot(FILE *f, LogId boot, int boot_display_index) { @@ -1026,14 +1026,14 @@ static int request_handler_boots( if (r < 0) return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to seek in journal: %m"); - response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_boots, m, NULL); + response = sym_MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_boots, m, NULL); if (!response) return respond_oom(connection); - if (MHD_add_response_header(response, "Content-Type", "application/json-seq") == MHD_NO) + if (sym_MHD_add_response_header(response, "Content-Type", "application/json-seq") == MHD_NO) return respond_oom(connection); - return MHD_queue_response(connection, MHD_HTTP_OK, response); + return sym_MHD_queue_response(connection, MHD_HTTP_OK, response); } static mhd_result request_handler( @@ -1290,6 +1290,10 @@ static int run(int argc, char *argv[]) { if (r <= 0) return r; + r = dlopen_microhttpd(LOG_ERR); + if (r < 0) + return r; + journal_browse_prepare(); assert_se(sigaction(SIGTERM, &sigterm, NULL) >= 0); @@ -1323,11 +1327,16 @@ static int run(int argc, char *argv[]) { { MHD_OPTION_HTTPS_MEM_TRUST, 0, arg_trust_pem }; } - d = MHD_start_daemon(flags, 19531, - NULL, NULL, - request_handler, NULL, - MHD_OPTION_ARRAY, opts, - MHD_OPTION_END); + d = sym_MHD_start_daemon( + flags, + /* port= */ 19531, + /* acp= */ NULL, + /* acp_cls= */ NULL, + request_handler, + /* dh_cls= */ NULL, + MHD_OPTION_ARRAY, + opts, + MHD_OPTION_END); if (!d) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to start daemon!"); diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c index 4867bf36094..1fbcc278152 100644 --- a/src/journal-remote/journal-remote-main.c +++ b/src/journal-remote/journal-remote-main.c @@ -108,7 +108,7 @@ static MHDDaemonWrapper* MHDDaemonWrapper_free(MHDDaemonWrapper *d) { d->timer_event = sd_event_source_unref(d->timer_event); if (d->daemon) - MHD_stop_daemon(d->daemon); + sym_MHD_stop_daemon(d->daemon); return mfree(d); } @@ -361,7 +361,7 @@ static mhd_result request_handler( if (*connection_cls) { RemoteSource *source = *connection_cls; - header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-Encoding"); + header = sym_MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-Encoding"); if (header) { Compression c = compression_from_string_harder(header); if (c <= 0 || !compression_supported(c)) @@ -382,12 +382,12 @@ static mhd_result request_handler( if (!streq(url, "/upload")) return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found."); - header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-Type"); + header = sym_MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-Type"); if (!header || !streq(header, "application/vnd.fdo.journal")) return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE, "Content-Type: application/vnd.fdo.journal is required."); - header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Transfer-Encoding"); + header = sym_MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Transfer-Encoding"); if (header) { if (!strcaseeq(header, "chunked")) return mhd_respondf(connection, 0, MHD_HTTP_BAD_REQUEST, @@ -396,7 +396,7 @@ static mhd_result request_handler( chunked = true; } - header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-Length"); + header = sym_MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-Length"); if (header) { size_t len; @@ -420,8 +420,8 @@ static mhd_result request_handler( { const union MHD_ConnectionInfo *ci; - ci = MHD_get_connection_info(connection, - MHD_CONNECTION_INFO_CONNECTION_FD); + ci = sym_MHD_get_connection_info(connection, + MHD_CONNECTION_INFO_CONNECTION_FD); if (!ci) { log_error("MHD_get_connection_info failed: cannot get remote fd"); return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, @@ -464,6 +464,12 @@ static int setup_microhttpd_server(RemoteServer *s, const char *trust) { #if HAVE_MICROHTTPD + int r; + + r = dlopen_microhttpd(LOG_ERR); + if (r < 0) + return r; + struct MHD_OptionItem opts[] = { { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger}, { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free}, @@ -483,7 +489,7 @@ static int setup_microhttpd_server(RemoteServer *s, _cleanup_(MHDDaemonWrapper_freep) MHDDaemonWrapper *d = NULL; const union MHD_DaemonInfo *info; - int r, epoll_fd; + int epoll_fd; assert(fd >= 0); @@ -526,18 +532,23 @@ static int setup_microhttpd_server(RemoteServer *s, d->fd = (uint64_t) fd; - d->daemon = MHD_start_daemon(flags, 0, - NULL, NULL, - request_handler, NULL, - MHD_OPTION_ARRAY, opts, - MHD_OPTION_END); + d->daemon = sym_MHD_start_daemon( + flags, + /* port= */ 0, + /* acp= */ NULL, + /* acp_cls= */ NULL, + request_handler, + /* dh_cls= */ NULL, + MHD_OPTION_ARRAY, + opts, + MHD_OPTION_END); if (!d->daemon) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to start μhttp daemon"); log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)", key ? "HTTPS" : "HTTP", fd, d); - info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY); + info = sym_MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY); if (!info) return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "μhttp returned NULL daemon info"); @@ -609,12 +620,12 @@ static int dispatch_http_event(sd_event_source *event, int r; MHD_UNSIGNED_LONG_LONG timeout = ULLONG_MAX; - r = MHD_run(d->daemon); + r = sym_MHD_run(d->daemon); if (r == MHD_NO) // FIXME: unregister daemon return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "MHD_run failed!"); - if (MHD_get_timeout(d->daemon, &timeout) == MHD_NO) + if (sym_MHD_get_timeout(d->daemon, &timeout) == MHD_NO) timeout = ULLONG_MAX; r = sd_event_source_set_time(d->timer_event, timeout); diff --git a/src/journal-remote/meson.build b/src/journal-remote/meson.build index c8d97526d37..f6aa71349c2 100644 --- a/src/journal-remote/meson.build +++ b/src/journal-remote/meson.build @@ -3,9 +3,6 @@ systemd_journal_gatewayd_sources = files( 'journal-gatewayd.c', ) -systemd_journal_gatewayd_extract_sources = files( - 'microhttpd-util.c', -) systemd_journal_remote_sources = files('journal-remote-main.c') systemd_journal_remote_extract_sources = files( @@ -24,7 +21,7 @@ systemd_journal_upload_extract_sources = files( ) common_deps = [ - libgnutls, + libgnutls_cflags, liblz4_cflags, libxz_cflags, libzstd_cflags, @@ -40,8 +37,7 @@ executables += [ 'HAVE_MICROHTTPD', ], 'sources' : systemd_journal_gatewayd_sources, - 'extract' : systemd_journal_gatewayd_extract_sources, - 'dependencies' : common_deps + [libmicrohttpd], + 'dependencies' : common_deps + [libmicrohttpd_cflags], }, libexec_template + { 'name' : 'systemd-journal-remote', @@ -52,8 +48,7 @@ executables += [ 'install' : conf.get('ENABLE_REMOTE') == 1, 'sources' : systemd_journal_remote_sources, 'extract' : systemd_journal_remote_extract_sources, - 'objects' : conf.get('HAVE_MICROHTTPD') == 1 ? ['systemd-journal-gatewayd'] : [], - 'dependencies' : common_deps + [libmicrohttpd], + 'dependencies' : common_deps + [libmicrohttpd_cflags], }, libexec_template + { 'name' : 'systemd-journal-upload', @@ -75,7 +70,7 @@ executables += [ fuzz_template + { 'sources' : files('fuzz-journal-remote.c'), 'objects' : ['systemd-journal-remote'], - 'dependencies' : common_deps + [libmicrohttpd], + 'dependencies' : common_deps + [libmicrohttpd_cflags], }, ] diff --git a/src/shared/gnutls-util.c b/src/shared/gnutls-util.c new file mode 100644 index 00000000000..1f17c4f5a16 --- /dev/null +++ b/src/shared/gnutls-util.c @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "sd-dlopen.h" + +#include "gnutls-util.h" +#include "log.h" /* IWYU pragma: keep */ + +#if HAVE_GNUTLS +static void *gnutls_dl = NULL; + +DLSYM_PROTOTYPE(gnutls_certificate_get_peers) = NULL; +DLSYM_PROTOTYPE(gnutls_certificate_type_get) = NULL; +DLSYM_PROTOTYPE(gnutls_certificate_verification_status_print) = NULL; +DLSYM_PROTOTYPE(gnutls_certificate_verify_peers2) = NULL; +DLSYM_PROTOTYPE(gnutls_free) = NULL; +DLSYM_PROTOTYPE(gnutls_global_set_log_function) = NULL; +DLSYM_PROTOTYPE(gnutls_global_set_log_level) = NULL; +DLSYM_PROTOTYPE(gnutls_x509_crt_deinit) = NULL; +DLSYM_PROTOTYPE(gnutls_x509_crt_get_dn) = NULL; +DLSYM_PROTOTYPE(gnutls_x509_crt_import) = NULL; +DLSYM_PROTOTYPE(gnutls_x509_crt_init) = NULL; +#endif + +int dlopen_gnutls(int log_level) { +#if HAVE_GNUTLS + SD_ELF_NOTE_DLOPEN( + "gnutls", + "Support for TLS via GnuTLS", + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + "libgnutls.so.30"); + + return dlopen_many_sym_or_warn( + &gnutls_dl, + "libgnutls.so.30", + log_level, + DLSYM_ARG(gnutls_certificate_get_peers), + DLSYM_ARG(gnutls_certificate_type_get), + DLSYM_ARG(gnutls_certificate_verification_status_print), + DLSYM_ARG(gnutls_certificate_verify_peers2), + DLSYM_ARG(gnutls_free), + DLSYM_ARG(gnutls_global_set_log_function), + DLSYM_ARG(gnutls_global_set_log_level), + DLSYM_ARG(gnutls_x509_crt_deinit), + DLSYM_ARG(gnutls_x509_crt_get_dn), + DLSYM_ARG(gnutls_x509_crt_import), + DLSYM_ARG(gnutls_x509_crt_init)); +#else + return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP), + "gnutls support is not compiled in."); +#endif +} diff --git a/src/shared/gnutls-util.h b/src/shared/gnutls-util.h new file mode 100644 index 00000000000..a110b437c38 --- /dev/null +++ b/src/shared/gnutls-util.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "shared-forward.h" + +int dlopen_gnutls(int log_level); + +#if HAVE_GNUTLS +# include /* IWYU pragma: export */ +# include /* IWYU pragma: export */ + +/* gnutls.h installs a function-like macro that wraps gnutls_free() and NULLs the passed pointer. We use + * dlsym to resolve the underlying function pointer variable, so undef the macro here to keep the variable + * name visible for DLSYM_PROTOTYPE/DLSYM_ARG. */ +# ifdef gnutls_free +# undef gnutls_free +# endif + +# include "dlfcn-util.h" + +extern DLSYM_PROTOTYPE(gnutls_certificate_get_peers); +extern DLSYM_PROTOTYPE(gnutls_certificate_type_get); +extern DLSYM_PROTOTYPE(gnutls_certificate_verification_status_print); +extern DLSYM_PROTOTYPE(gnutls_certificate_verify_peers2); +extern DLSYM_PROTOTYPE(gnutls_free); +extern DLSYM_PROTOTYPE(gnutls_global_set_log_function); +extern DLSYM_PROTOTYPE(gnutls_global_set_log_level); +extern DLSYM_PROTOTYPE(gnutls_x509_crt_deinit); +extern DLSYM_PROTOTYPE(gnutls_x509_crt_get_dn); +extern DLSYM_PROTOTYPE(gnutls_x509_crt_import); +extern DLSYM_PROTOTYPE(gnutls_x509_crt_init); +#endif diff --git a/src/shared/meson.build b/src/shared/meson.build index 07b504797af..1ab14a5c92a 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -90,6 +90,7 @@ shared_sources = files( 'fstab-util.c', 'generator.c', 'geneve-util.c', + 'gnutls-util.c', 'gpt.c', 'group-record.c', 'hibernate-util.c', @@ -134,6 +135,7 @@ shared_sources = files( 'macvlan-util.c', 'main-func.c', 'metrics.c', + 'microhttpd-util.c', 'mkdir-label.c', 'mkfs-util.c', 'module-util.c', @@ -393,8 +395,10 @@ libshared_deps = [threads, libfdisk_cflags, libfido2_cflags, libgcrypt_cflags, + libgnutls_cflags, libidn2_cflags, libkmod_cflags, + libmicrohttpd_cflags, libmount_cflags, libopenssl, libp11kit_cflags, diff --git a/src/journal-remote/microhttpd-util.c b/src/shared/microhttpd-util.c similarity index 65% rename from src/journal-remote/microhttpd-util.c rename to src/shared/microhttpd-util.c index 32751e85e1c..6dd55d8a076 100644 --- a/src/journal-remote/microhttpd-util.c +++ b/src/shared/microhttpd-util.c @@ -2,17 +2,74 @@ #include -#if HAVE_GNUTLS -#include -#include -#endif +#include "sd-dlopen.h" #include "alloc-util.h" +#include "gnutls-util.h" #include "log.h" #include "microhttpd-util.h" #include "string-util.h" #include "strv.h" +#if HAVE_MICROHTTPD +static void *microhttpd_dl = NULL; + +DLSYM_PROTOTYPE(MHD_add_response_header) = NULL; +DLSYM_PROTOTYPE(MHD_create_response_from_buffer) = NULL; +DLSYM_PROTOTYPE(MHD_create_response_from_callback) = NULL; +#if MHD_VERSION < 0x00094203 +DLSYM_PROTOTYPE(MHD_create_response_from_fd_at_offset) = NULL; +#else +DLSYM_PROTOTYPE(MHD_create_response_from_fd_at_offset64) = NULL; +#endif +DLSYM_PROTOTYPE(MHD_destroy_response) = NULL; +DLSYM_PROTOTYPE(MHD_get_connection_info) = NULL; +DLSYM_PROTOTYPE(MHD_get_connection_values) = NULL; +DLSYM_PROTOTYPE(MHD_get_daemon_info) = NULL; +DLSYM_PROTOTYPE(MHD_get_timeout) = NULL; +DLSYM_PROTOTYPE(MHD_lookup_connection_value) = NULL; +DLSYM_PROTOTYPE(MHD_queue_response) = NULL; +DLSYM_PROTOTYPE(MHD_run) = NULL; +DLSYM_PROTOTYPE(MHD_start_daemon) = NULL; +DLSYM_PROTOTYPE(MHD_stop_daemon) = NULL; +#endif + +int dlopen_microhttpd(int log_level) { +#if HAVE_MICROHTTPD + SD_ELF_NOTE_DLOPEN( + "microhttpd", + "Support for embedded HTTP server via libmicrohttpd", + SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + "libmicrohttpd.so.12"); + + return dlopen_many_sym_or_warn( + µhttpd_dl, + "libmicrohttpd.so.12", + log_level, + DLSYM_ARG(MHD_add_response_header), + DLSYM_ARG(MHD_create_response_from_buffer), + DLSYM_ARG(MHD_create_response_from_callback), +#if MHD_VERSION < 0x00094203 + DLSYM_ARG(MHD_create_response_from_fd_at_offset), +#else + DLSYM_ARG(MHD_create_response_from_fd_at_offset64), +#endif + DLSYM_ARG(MHD_destroy_response), + DLSYM_ARG(MHD_get_connection_info), + DLSYM_ARG(MHD_get_connection_values), + DLSYM_ARG(MHD_get_daemon_info), + DLSYM_ARG(MHD_get_timeout), + DLSYM_ARG(MHD_lookup_connection_value), + DLSYM_ARG(MHD_queue_response), + DLSYM_ARG(MHD_run), + DLSYM_ARG(MHD_start_daemon), + DLSYM_ARG(MHD_stop_daemon)); +#else + return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP), + "libmicrohttpd support is not compiled in."); +#endif +} + #if HAVE_MICROHTTPD void microhttpd_logger(void *arg, const char *fmt, va_list ap) { @@ -36,18 +93,18 @@ int mhd_respond_internal( assert(connection); _cleanup_(MHD_destroy_responsep) struct MHD_Response *response - = MHD_create_response_from_buffer(size, (char*) buffer, mode); + = sym_MHD_create_response_from_buffer(size, (char*) buffer, mode); if (!response) return MHD_NO; log_debug("Queueing response %u: %s", code, buffer); if (encoding) - if (MHD_add_response_header(response, "Accept-Encoding", encoding) == MHD_NO) + if (sym_MHD_add_response_header(response, "Accept-Encoding", encoding) == MHD_NO) return MHD_NO; - if (MHD_add_response_header(response, "Content-Type", "text/plain") == MHD_NO) + if (sym_MHD_add_response_header(response, "Content-Type", "text/plain") == MHD_NO) return MHD_NO; - return MHD_queue_response(connection, code, response); + return sym_MHD_queue_response(connection, code, response); } int mhd_respond_oom(struct MHD_Connection *connection) { @@ -116,7 +173,7 @@ static void log_reset_gnutls_level(void) { for (i = ELEMENTSOF(gnutls_log_map) - 1; i >= 0; i--) if (gnutls_log_map[i].enabled) { log_debug("Setting gnutls log level to %d", i); - gnutls_global_set_log_level(i); + sym_gnutls_global_set_log_level(i); break; } } @@ -140,7 +197,16 @@ static int log_enable_gnutls_category(const char *cat) { int setup_gnutls_logger(char **categories) { int r; - gnutls_global_set_log_function(log_func_gnutls); + r = dlopen_gnutls(LOG_DEBUG); + if (r < 0) { + if (categories) + log_notice("Ignoring specified gnutls logging categories -- gnutls not available."); + else + log_debug("GnuTLS not available, skipping logger setup."); + return 0; + } + + sym_gnutls_global_set_log_function(log_func_gnutls); if (categories) STRV_FOREACH(cat, categories) { @@ -160,17 +226,19 @@ static int verify_cert_authorized(gnutls_session_t session) { gnutls_datum_t out; int r; - r = gnutls_certificate_verify_peers2(session, &status); + r = sym_gnutls_certificate_verify_peers2(session, &status); if (r < 0) return log_error_errno(r, "gnutls_certificate_verify_peers2 failed: %m"); - type = gnutls_certificate_type_get(session); - r = gnutls_certificate_verification_status_print(status, type, &out, 0); + type = sym_gnutls_certificate_type_get(session); + r = sym_gnutls_certificate_verification_status_print(status, type, &out, 0); if (r < 0) return log_error_errno(r, "gnutls_certificate_verification_status_print failed: %m"); log_debug("Certificate status: %s", out.data); - gnutls_free(out.data); + /* gnutls_free is declared as a function pointer variable (not a function), so sym_gnutls_free + * ends up as a pointer-to-function-pointer and must be explicitly dereferenced to be called. */ + (*sym_gnutls_free)(out.data); return status == 0 ? 0 : -EPERM; } @@ -184,12 +252,12 @@ static int get_client_cert(gnutls_session_t session, gnutls_x509_crt_t *client_c assert(session); assert(client_cert); - pcert = gnutls_certificate_get_peers(session, &listsize); + pcert = sym_gnutls_certificate_get_peers(session, &listsize); if (!pcert || !listsize) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to retrieve certificate chain"); - r = gnutls_x509_crt_init(&cert); + r = sym_gnutls_x509_crt_init(&cert); if (r < 0) { log_error("Failed to initialize client certificate"); return r; @@ -197,10 +265,10 @@ static int get_client_cert(gnutls_session_t session, gnutls_x509_crt_t *client_c /* Note that by passing values between 0 and listsize here, you can get access to the CA's certs */ - r = gnutls_x509_crt_import(cert, &pcert[0], GNUTLS_X509_FMT_DER); + r = sym_gnutls_x509_crt_import(cert, &pcert[0], GNUTLS_X509_FMT_DER); if (r < 0) { log_error("Failed to import client certificate"); - gnutls_x509_crt_deinit(cert); + sym_gnutls_x509_crt_deinit(cert); return r; } @@ -215,7 +283,7 @@ static int get_auth_dn(gnutls_x509_crt_t client_cert, char **buf) { assert(buf); assert(*buf == NULL); - r = gnutls_x509_crt_get_dn(client_cert, NULL, &len); + r = sym_gnutls_x509_crt_get_dn(client_cert, NULL, &len); if (r != GNUTLS_E_SHORT_MEMORY_BUFFER) { log_error("gnutls_x509_crt_get_dn failed"); return r; @@ -225,14 +293,15 @@ static int get_auth_dn(gnutls_x509_crt_t client_cert, char **buf) { if (!*buf) return log_oom(); - gnutls_x509_crt_get_dn(client_cert, *buf, &len); + sym_gnutls_x509_crt_get_dn(client_cert, *buf, &len); return 0; } static void gnutls_x509_crt_deinitp(gnutls_x509_crt_t *p) { assert(p); - gnutls_x509_crt_deinit(*p); + if (*p) + sym_gnutls_x509_crt_deinit(*p); } int check_permissions(struct MHD_Connection *connection, int *code, char **hostname) { @@ -247,8 +316,12 @@ int check_permissions(struct MHD_Connection *connection, int *code, char **hostn *code = 0; - ci = MHD_get_connection_info(connection, - MHD_CONNECTION_INFO_GNUTLS_SESSION); + r = dlopen_gnutls(LOG_ERR); + if (r < 0) + return r; + + ci = sym_MHD_get_connection_info(connection, + MHD_CONNECTION_INFO_GNUTLS_SESSION); if (!ci) { log_error("MHD_get_connection_info failed: session is unencrypted"); *code = mhd_respond(connection, MHD_HTTP_FORBIDDEN, diff --git a/src/journal-remote/microhttpd-util.h b/src/shared/microhttpd-util.h similarity index 76% rename from src/journal-remote/microhttpd-util.h rename to src/shared/microhttpd-util.h index 80142e24f59..488ba7ea6e8 100644 --- a/src/journal-remote/microhttpd-util.h +++ b/src/shared/microhttpd-util.h @@ -1,11 +1,15 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include "shared-forward.h" + +int dlopen_microhttpd(int log_level); + #if HAVE_MICROHTTPD #include -#include "shared-forward.h" +#include "dlfcn-util.h" /* Those defines are added when options are renamed. If the old names * are not '#define'd, then they are not deprecated yet and there are @@ -58,6 +62,26 @@ # define mhd_result int #endif +extern DLSYM_PROTOTYPE(MHD_add_response_header); +extern DLSYM_PROTOTYPE(MHD_create_response_from_buffer); +extern DLSYM_PROTOTYPE(MHD_create_response_from_callback); +#if MHD_VERSION < 0x00094203 +extern DLSYM_PROTOTYPE(MHD_create_response_from_fd_at_offset); +# define sym_MHD_create_response_from_fd_at_offset64 sym_MHD_create_response_from_fd_at_offset +#else +extern DLSYM_PROTOTYPE(MHD_create_response_from_fd_at_offset64); +#endif +extern DLSYM_PROTOTYPE(MHD_destroy_response); +extern DLSYM_PROTOTYPE(MHD_get_connection_info); +extern DLSYM_PROTOTYPE(MHD_get_connection_values); +extern DLSYM_PROTOTYPE(MHD_get_daemon_info); +extern DLSYM_PROTOTYPE(MHD_get_timeout); +extern DLSYM_PROTOTYPE(MHD_lookup_connection_value); +extern DLSYM_PROTOTYPE(MHD_queue_response); +extern DLSYM_PROTOTYPE(MHD_run); +extern DLSYM_PROTOTYPE(MHD_start_daemon); +extern DLSYM_PROTOTYPE(MHD_stop_daemon); + void microhttpd_logger(void *arg, const char *fmt, va_list ap) _printf_(2, 0); /* respond_oom() must be usable with return, hence this form. */ @@ -107,7 +131,7 @@ int check_permissions(struct MHD_Connection *connection, int *code, char **hostn */ int setup_gnutls_logger(char **categories); -DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct MHD_Daemon*, MHD_stop_daemon, NULL); -DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct MHD_Response*, MHD_destroy_response, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(struct MHD_Daemon*, sym_MHD_stop_daemon, MHD_stop_daemonp, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(struct MHD_Response*, sym_MHD_destroy_response, MHD_destroy_responsep, NULL); #endif diff --git a/src/test/meson.build b/src/test/meson.build index 7089d623d18..9544c166a59 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -302,7 +302,9 @@ executables += [ 'dependencies' : [ libblkid_cflags, libfdisk_cflags, + libgnutls_cflags, libkmod_cflags, + libmicrohttpd_cflags, libmount_cflags, libp11kit_cflags, libseccomp_cflags, diff --git a/src/test/test-dlopen-so.c b/src/test/test-dlopen-so.c index c2eaac7b53d..d3121981cf5 100644 --- a/src/test/test-dlopen-so.c +++ b/src/test/test-dlopen-so.c @@ -10,6 +10,7 @@ #include "elf-util.h" #include "fdisk-util.h" #include "gcrypt-util.h" +#include "gnutls-util.h" #include "idn-util.h" #include "libarchive-util.h" #include "libaudit-util.h" @@ -17,6 +18,7 @@ #include "libfido2-util.h" #include "libmount-util.h" #include "main-func.h" +#include "microhttpd-util.h" #include "module-util.h" #include "pam-util.h" #include "password-quality-util-passwdqc.h" @@ -52,6 +54,7 @@ static int run(int argc, char **argv) { ASSERT_DLOPEN(dlopen_elf, HAVE_ELFUTILS); ASSERT_DLOPEN(dlopen_fdisk, HAVE_LIBFDISK); ASSERT_DLOPEN(dlopen_gcrypt, HAVE_GCRYPT); + ASSERT_DLOPEN(dlopen_gnutls, HAVE_GNUTLS); ASSERT_DLOPEN(dlopen_idn, HAVE_LIBIDN2); ASSERT_DLOPEN(dlopen_libacl, HAVE_ACL); ASSERT_DLOPEN(dlopen_libapparmor, HAVE_APPARMOR); @@ -67,6 +70,7 @@ static int run(int argc, char **argv) { ASSERT_DLOPEN(dlopen_libselinux, HAVE_SELINUX); ASSERT_DLOPEN(dlopen_xz, HAVE_XZ); ASSERT_DLOPEN(dlopen_lz4, HAVE_LZ4); + ASSERT_DLOPEN(dlopen_microhttpd, HAVE_MICROHTTPD); ASSERT_DLOPEN(dlopen_p11kit, HAVE_P11KIT); ASSERT_DLOPEN(dlopen_passwdqc, HAVE_PASSWDQC); ASSERT_DLOPEN(dlopen_pcre2, HAVE_PCRE2);