diff --git a/.gitignore b/.gitignore index 807b2b8..058e3c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ SOURCES/libguestfs.keyring -SOURCES/libnbd-1.18.1.tar.gz +SOURCES/libnbd-1.20.2.tar.gz diff --git a/.libnbd.metadata b/.libnbd.metadata index b6a02f4..323911f 100644 --- a/.libnbd.metadata +++ b/.libnbd.metadata @@ -1,2 +1,2 @@ cc1b37b9cfafa515aab3eefd345ecc59aac2ce7b SOURCES/libguestfs.keyring -4f99e6f21edffe62b394aa9c7fb68149e6d4d5e4 SOURCES/libnbd-1.18.1.tar.gz +d3abc6e794dd4b99640328bfd395a8913288e859 SOURCES/libnbd-1.20.2.tar.gz diff --git a/SOURCES/0001-generator-Fix-assertion-in-ext-mode-BLOCK_STATUS-CVE.patch b/SOURCES/0001-generator-Fix-assertion-in-ext-mode-BLOCK_STATUS-CVE.patch deleted file mode 100644 index d660188..0000000 --- a/SOURCES/0001-generator-Fix-assertion-in-ext-mode-BLOCK_STATUS-CVE.patch +++ /dev/null @@ -1,88 +0,0 @@ -From 4451e5b61ca07771ceef3e012223779e7a0c7701 Mon Sep 17 00:00:00 2001 -From: Eric Blake -Date: Mon, 30 Oct 2023 12:50:53 -0500 -Subject: [PATCH] generator: Fix assertion in ext-mode BLOCK_STATUS, - CVE-2023-5871 - -Another round of fuzz testing revealed that when a server negotiates -extended headers and replies with a 64-bit flag value where the client -used the 32-bit API command, we were correctly flagging the server's -response as being an EOVERFLOW condition, but then immediately failing -in an assertion failure instead of reporting it to the application. - -The following one-byte change to qemu.git at commit fd9a38fd43 allows -the creation of an intentionally malicious server: - -| diff --git i/nbd/server.c w/nbd/server.c -| index 859c163d19f..32e1e771a95 100644 -| --- i/nbd/server.c -| +++ w/nbd/server.c -| @@ -2178,7 +2178,7 @@ static void nbd_extent_array_convert_to_be(NBDExtentArray *ea) -| -| for (i = 0; i < ea->count; i++) { -| ea->extents[i].length = cpu_to_be64(ea->extents[i].length); -| - ea->extents[i].flags = cpu_to_be64(ea->extents[i].flags); -| + ea->extents[i].flags = ~cpu_to_be64(ea->extents[i].flags); -| } -| } - -and can then be detected with the following command line: - -$ nbdsh -c - <<\EOF -> def f(a,b,c,d): -> pass -> -> h.connect_systemd_socket_activation(["/path/to/bad/qemu-nbd", -> "-r", "-f", "raw", "TODO"]) -> h.block_staus(h.get_size(), 0, f) -> EOF -nbdsh: generator/states-reply-chunk.c:626: enter_STATE_REPLY_CHUNK_REPLY_RECV_BS_ENTRIES: Assertion `(len | flags) <= UINT32_MAX' failed. -Aborted (core dumped) - -whereas a fixed libnbd will give: - -nbdsh: command line script failed: nbd_block_status: block-status: command failed: Value too large for defined data type - -We can either relax the assertion (by changing to 'assert ((len | -flags) <= UINT32_MAX || cmd->error)'), or intentionally truncate flags -to make the existing assertion reliable. This patch goes with the -latter approach. - -Sadly, this crash is possible in all existing 1.18.x stable releases, -if they were built with assertions enabled (most distros do this by -default), meaning a malicious server has an easy way to cause a Denial -of Service attack by triggering the assertion failure in vulnerable -clients, so we have assigned this CVE-2023-5871. Mitigating factors: -the crash only happens for a server that sends a 64-bit status block -reply (no known production servers do so; qemu 8.2 will be the first -known server to support extended headers, but it is not yet released); -and as usual, a client can use TLS to guarantee it is connecting only -to a known-safe server. If libnbd is compiled without assertions, -there is no crash or other mistaken behavior; and when assertions are -enabled, the attacker cannot accomplish anything more than a denial of -service. - -Reported-by: Richard W.M. Jones -Fixes: 20dadb0e10 ("generator: Prepare for extent64 callback", v1.17.4) -Signed-off-by: Eric Blake -(cherry picked from commit 177308adb17e81fce7c0f2b2fcf655c5c0b6a4d6) -Signed-off-by: Eric Blake ---- - generator/states-reply-chunk.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/generator/states-reply-chunk.c b/generator/states-reply-chunk.c -index 5a31c19..8ab7e8b 100644 ---- a/generator/states-reply-chunk.c -+++ b/generator/states-reply-chunk.c -@@ -600,6 +600,7 @@ STATE_MACHINE { - break; /* Skip this and later extents; we already made progress */ - /* Expose this extent as an error; we made no progress */ - cmd->error = cmd->error ? : EOVERFLOW; -+ flags = (uint32_t)flags; - } - } - --- -2.39.3 - diff --git a/SOURCES/0001-generator-Print-full-error-in-handle_reply_error.patch b/SOURCES/0001-generator-Print-full-error-in-handle_reply_error.patch new file mode 100644 index 0000000..5ec5aea --- /dev/null +++ b/SOURCES/0001-generator-Print-full-error-in-handle_reply_error.patch @@ -0,0 +1,191 @@ +From 89f0e33f355681b7c5b1835a09c5fac93c08e71e Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Tue, 23 Jul 2024 17:22:12 +0100 +Subject: [PATCH] generator: Print full error in handle_reply_error + +Print the full error from the server during handshaking. This +modifies the contract of handle_reply_error so it calls set_error, +which can be overridden by callers or ignored completely. + +(cherry picked from commit cf49a49adc8abc8c917437db7461ed9956583877) +--- + generator/states-newstyle-opt-go.c | 32 +-------- + generator/states-newstyle-opt-list.c | 5 +- + generator/states-newstyle-opt-meta-context.c | 8 +-- + generator/states-newstyle.c | 68 ++++++++++++++++++-- + 4 files changed, 69 insertions(+), 44 deletions(-) + +diff --git a/generator/states-newstyle-opt-go.c b/generator/states-newstyle-opt-go.c +index 5bc9a9ae..f6eb8afc 100644 +--- a/generator/states-newstyle-opt-go.c ++++ b/generator/states-newstyle-opt-go.c +@@ -247,37 +247,9 @@ STATE_MACHINE { + SET_NEXT_STATE (%.DEAD); + return 0; + } +- /* Decode expected known errors into a nicer string */ +- switch (reply) { +- case NBD_REP_ERR_UNSUP: ++ if (reply == NBD_REP_ERR_UNSUP) + assert (h->opt_current == NBD_OPT_INFO); +- set_error (ENOTSUP, "handshake: server lacks NBD_OPT_INFO support"); +- break; +- case NBD_REP_ERR_POLICY: +- case NBD_REP_ERR_PLATFORM: +- set_error (0, "handshake: server policy prevents NBD_OPT_GO"); +- break; +- case NBD_REP_ERR_INVALID: +- case NBD_REP_ERR_TOO_BIG: +- set_error (EINVAL, "handshake: server rejected NBD_OPT_GO as invalid"); +- break; +- case NBD_REP_ERR_TLS_REQD: +- set_error (ENOTSUP, "handshake: server requires TLS encryption first"); +- break; +- case NBD_REP_ERR_UNKNOWN: +- set_error (ENOENT, "handshake: server has no export named '%s'", +- h->export_name); +- break; +- case NBD_REP_ERR_SHUTDOWN: +- set_error (ESHUTDOWN, "handshake: server is shutting down"); +- break; +- case NBD_REP_ERR_BLOCK_SIZE_REQD: +- set_error (EINVAL, "handshake: server requires specific block sizes"); +- break; +- default: +- set_error (0, "handshake: unknown reply from NBD_OPT_GO: 0x%" PRIx32, +- reply); +- } ++ + nbd_internal_reset_size_and_flags (h); + h->meta_valid = false; + err = nbd_get_errno () ? : ENOTSUP; +diff --git a/generator/states-newstyle-opt-list.c b/generator/states-newstyle-opt-list.c +index cdd4676e..6605ee0a 100644 +--- a/generator/states-newstyle-opt-list.c ++++ b/generator/states-newstyle-opt-list.c +@@ -127,9 +127,8 @@ STATE_MACHINE { + SET_NEXT_STATE (%.DEAD); + return 0; + } +- err = ENOTSUP; +- set_error (err, "unexpected response, possibly the server does not " +- "support listing exports"); ++ debug (h, "unexpected response, possibly the server does not " ++ "support listing exports"); + break; + } + +diff --git a/generator/states-newstyle-opt-meta-context.c b/generator/states-newstyle-opt-meta-context.c +index 6f016e66..3945411e 100644 +--- a/generator/states-newstyle-opt-meta-context.c ++++ b/generator/states-newstyle-opt-meta-context.c +@@ -270,12 +270,8 @@ STATE_MACHINE { + } + + if (opt == h->opt_current) { +- /* XXX Should we decode specific expected errors, like +- * REP_ERR_UNKNOWN to ENOENT or REP_ERR_TOO_BIG to ERANGE? +- */ +- err = ENOTSUP; +- set_error (err, "unexpected response, possibly the server does not " +- "support meta contexts"); ++ debug (h, "unexpected response, possibly the server does not " ++ "support meta contexts"); + CALL_CALLBACK (h->opt_cb.completion, &err); + nbd_internal_free_option (h); + SET_NEXT_STATE (%.NEGOTIATING); +diff --git a/generator/states-newstyle.c b/generator/states-newstyle.c +index 45893a8b..6c7cc45c 100644 +--- a/generator/states-newstyle.c ++++ b/generator/states-newstyle.c +@@ -79,14 +79,18 @@ prepare_for_reply_payload (struct nbd_handle *h, uint32_t opt) + return 0; + } + +-/* Check an unexpected server reply. If it is an error, log any +- * message from the server and return 0; otherwise, return -1. ++/* Check an unexpected server reply error. ++ * ++ * This calls set_error with a descriptive error message and returns ++ * 0. Unless there is a further unexpected error while processing ++ * this error, in which case it calls set_error and returns -1. + */ + static int + handle_reply_error (struct nbd_handle *h) + { + uint32_t len; + uint32_t reply; ++ char *msg = NULL; + + len = be32toh (h->sbuf.or.option_reply.replylen); + reply = be32toh (h->sbuf.or.option_reply.reply); +@@ -101,9 +105,63 @@ handle_reply_error (struct nbd_handle *h) + return -1; + } + +- if (len > 0) +- debug (h, "handshake: server error message: %.*s", (int)len, +- h->sbuf.or.payload.err_msg); ++ /* Decode expected errors into a nicer string. ++ * ++ * XXX Note this string comes directly from the server, and most ++ * libnbd users simply print the error using 'fprintf'. We really ++ * ought to quote this string somehow, but we don't have a useful ++ * function for that. ++ */ ++ if (len > 0) { ++ if (asprintf (&msg, ": %.*s", ++ (int)len, h->sbuf.or.payload.err_msg) == -1) { ++ set_error (errno, "asprintf"); ++ return -1; ++ } ++ } ++ ++ switch (reply) { ++ case NBD_REP_ERR_UNSUP: ++ set_error (ENOTSUP, "the operation is not supported by the server%s", ++ msg ? : ""); ++ break; ++ case NBD_REP_ERR_POLICY: ++ set_error (0, "server policy prevents the operation%s", ++ msg ? : ""); ++ break; ++ case NBD_REP_ERR_PLATFORM: ++ set_error (0, "the operation is not supported by the server platform%s", ++ msg ? : ""); ++ break; ++ case NBD_REP_ERR_INVALID: ++ set_error (EINVAL, "the server rejected this operation as invalid%s", ++ msg ? : ""); ++ break; ++ case NBD_REP_ERR_TOO_BIG: ++ set_error (EINVAL, "the operation is too large to process%s", ++ msg ? : ""); ++ break; ++ case NBD_REP_ERR_TLS_REQD: ++ set_error (ENOTSUP, "the server requires TLS encryption first%s", ++ msg ? : ""); ++ break; ++ case NBD_REP_ERR_UNKNOWN: ++ set_error (ENOENT, "the server has no export named '%s'%s", ++ h->export_name, msg ? : ""); ++ break; ++ case NBD_REP_ERR_SHUTDOWN: ++ set_error (ESHUTDOWN, "the server is shutting down%s", ++ msg ? : ""); ++ break; ++ case NBD_REP_ERR_BLOCK_SIZE_REQD: ++ set_error (EINVAL, "the server requires specific block sizes%s", ++ msg ? : ""); ++ break; ++ default: ++ set_error (0, "handshake: unknown reply from the server: 0x%" PRIx32 "%s", ++ reply, msg ? : ""); ++ } ++ free (msg); + + return 0; + } +-- +2.43.0 + diff --git a/SOURCES/0002-docs-Fix-incorrect-xref-in-libnbd-release-notes-for-.patch b/SOURCES/0002-docs-Fix-incorrect-xref-in-libnbd-release-notes-for-.patch deleted file mode 100644 index 37101da..0000000 --- a/SOURCES/0002-docs-Fix-incorrect-xref-in-libnbd-release-notes-for-.patch +++ /dev/null @@ -1,34 +0,0 @@ -From c39e31b7a20c7dc8aa12c5fa3f1742824e1e0c76 Mon Sep 17 00:00:00 2001 -From: "Richard W.M. Jones" -Date: Thu, 9 Nov 2023 09:40:30 +0000 -Subject: [libnbd PATCH] docs: Fix incorrect xref in libnbd-release-notes for - 1.18 -Content-type: text/plain - -LIBNBD_STRICT_AUTO_FLAG was added to nbd_set_strict_mode(3). - -Reported-by: Vera Wu -(cherry picked from commit 4fef3dbc07e631fce58487d25d991e83bbb424b1) -Signed-off-by: Eric Blake ---- - docs/libnbd-release-notes-1.18.pod | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/docs/libnbd-release-notes-1.18.pod b/docs/libnbd-release-notes-1.18.pod -index 935fab11..836ebe19 100644 ---- a/docs/libnbd-release-notes-1.18.pod -+++ b/docs/libnbd-release-notes-1.18.pod -@@ -84,8 +84,8 @@ Golang, OCaml and Python language bindings (Eric Blake). - - L now works correctly when in opt mode (Eric Blake). - --L adds C which allows the --client to test how servers behave when the payload length flag is -+L adds C which allows -+the client to test how servers behave when the payload length flag is - adjusted (Eric Blake). - - =head2 Protocol --- -2.41.0 - diff --git a/SOURCES/0002-lib-Don-t-overwrite-error-in-nbd_opt_-go-info.patch b/SOURCES/0002-lib-Don-t-overwrite-error-in-nbd_opt_-go-info.patch new file mode 100644 index 0000000..65e3a4a --- /dev/null +++ b/SOURCES/0002-lib-Don-t-overwrite-error-in-nbd_opt_-go-info.patch @@ -0,0 +1,38 @@ +From f31e31b32e662d0aadb77bc639308c8a65968886 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Tue, 23 Jul 2024 17:26:39 +0100 +Subject: [PATCH] lib: Don't overwrite error in nbd_opt_{go,info} + +We already set the error in handle_reply_error, so don't overwrite +that here. + +(cherry picked from commit 474a4ae6c8d11212a4a8c06ea3e8b3fd97a7e97d) +--- + lib/opt.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/lib/opt.c b/lib/opt.c +index 600265a0..5872dd54 100644 +--- a/lib/opt.c ++++ b/lib/opt.c +@@ -99,7 +99,7 @@ nbd_unlocked_opt_go (struct nbd_handle *h) + if (r == 0 && err) { + assert (nbd_internal_is_state_negotiating (get_next_state (h)) || + nbd_internal_is_state_dead (get_next_state (h))); +- set_error (err, "server replied with error to opt_go request"); ++ /* handle_reply_error already called set_error */ + return -1; + } + if (r == 0) +@@ -122,7 +122,7 @@ nbd_unlocked_opt_info (struct nbd_handle *h) + if (r == 0 && err) { + assert (nbd_internal_is_state_negotiating (get_next_state (h)) || + nbd_internal_is_state_dead (get_next_state (h))); +- set_error (err, "server replied with error to opt_info request"); ++ /* handle_reply_error already called set_error */ + return -1; + } + return r; +-- +2.43.0 + diff --git a/SOURCES/0003-generator-Restore-assignment-to-local-err.patch b/SOURCES/0003-generator-Restore-assignment-to-local-err.patch new file mode 100644 index 0000000..a27e2a7 --- /dev/null +++ b/SOURCES/0003-generator-Restore-assignment-to-local-err.patch @@ -0,0 +1,43 @@ +From ff5ee9308e66fe9fd19ea578623c4540f44942d6 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 25 Jul 2024 13:39:28 +0100 +Subject: [PATCH] generator: Restore assignment to local 'err' + +I accidentally removed the assignment of local variable 'err' along +these paths in commit cf49a49adc ("generator: Print full error in +handle_reply_error"). + +Fixes: commit cf49a49adc8abc8c917437db7461ed9956583877 +(cherry picked from commit e75d20b9e19143b1bd0d232fc49cb2e0287f824a) +--- + generator/states-newstyle-opt-list.c | 1 + + generator/states-newstyle-opt-meta-context.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/generator/states-newstyle-opt-list.c b/generator/states-newstyle-opt-list.c +index 6605ee0a..48559574 100644 +--- a/generator/states-newstyle-opt-list.c ++++ b/generator/states-newstyle-opt-list.c +@@ -129,6 +129,7 @@ STATE_MACHINE { + } + debug (h, "unexpected response, possibly the server does not " + "support listing exports"); ++ err = ENOTSUP; + break; + } + +diff --git a/generator/states-newstyle-opt-meta-context.c b/generator/states-newstyle-opt-meta-context.c +index 3945411e..699e24aa 100644 +--- a/generator/states-newstyle-opt-meta-context.c ++++ b/generator/states-newstyle-opt-meta-context.c +@@ -272,6 +272,7 @@ STATE_MACHINE { + if (opt == h->opt_current) { + debug (h, "unexpected response, possibly the server does not " + "support meta contexts"); ++ err = ENOTSUP; + CALL_CALLBACK (h->opt_cb.completion, &err); + nbd_internal_free_option (h); + SET_NEXT_STATE (%.NEGOTIATING); +-- +2.43.0 + diff --git a/SOURCES/0003-tests-Check-behavior-of-nbd_set_strict_mode-STRICT_A.patch b/SOURCES/0003-tests-Check-behavior-of-nbd_set_strict_mode-STRICT_A.patch deleted file mode 100644 index 9208a2f..0000000 --- a/SOURCES/0003-tests-Check-behavior-of-nbd_set_strict_mode-STRICT_A.patch +++ /dev/null @@ -1,206 +0,0 @@ -From 32cb9ab9f1701b1a1a826b48f2083cb75adf1e87 Mon Sep 17 00:00:00 2001 -From: Eric Blake -Date: Thu, 9 Nov 2023 20:11:08 -0600 -Subject: [libnbd PATCH] tests: Check behavior of - nbd_set_strict_mode(STRICT_AUTO_FLAG) -Content-type: text/plain - -While developing extended header support for qemu 8.2, I needed a way -to make libnbd quickly behave as a non-compliant client to test corner -cases in qemu's server code; so I wrote commit 5c1dae9236 ("api: Add -LIBNBD_STRICT_AUTO_FLAG to nbd_set_strict", v1.18.0) to meet my needs. -However, I failed to codify my manual tests of that bit into a unit -test for libnbd, until now. Most sane clients will never call -nbd_set_strict_mode() in the first place (after all, it is explicitly -documented as an integration tool, which is how I used it with my qemu -code development), but it never hurts to make sure we don't break it -even for the relatively small set of users that would ever use it. - -The test added here runs in two parts; if you get a SKIP despite -having qemu-nbd, then the first part ran successfully before the -second half gave up due to lack of extended headers in qemu -(presumably qemu 8.1 or older); if you get a PASS, then both parts -were run. However, both parts are inherently fragile, depending on -behavior known to be in qemu 8.2 - while it is unlikely to change in -future qemu releases (at least as long as I continue to maintain NBD -code there), the fact that we are intentionally violating the NBD -protocol means a different server is within its rights to behave -differently than qemu 8.2 did. Hence this test lives in interop/ -rather than tests/ because of its strong ties to a particular qemu. - -Signed-off-by: Eric Blake -(cherry picked from commit 54d4426394c372413f55f648d4ad1d21b3395e07) -Signed-off-by: Eric Blake ---- - interop/Makefile.am | 2 + - interop/strict-mode-auto-flag.sh | 138 +++++++++++++++++++++++++++++++ - 2 files changed, 140 insertions(+) - create mode 100755 interop/strict-mode-auto-flag.sh - -diff --git a/interop/Makefile.am b/interop/Makefile.am -index d6485adf..ac12d84a 100644 ---- a/interop/Makefile.am -+++ b/interop/Makefile.am -@@ -28,6 +28,7 @@ EXTRA_DIST = \ - structured-read.sh \ - opt-extended-headers.sh \ - block-status-payload.sh \ -+ strict-mode-auto-flag.sh \ - $(NULL) - - TESTS_ENVIRONMENT = \ -@@ -153,6 +154,7 @@ TESTS += \ - interop-qemu-block-size.sh \ - opt-extended-headers.sh \ - block-status-payload.sh \ -+ strict-mode-auto-flag.sh \ - $(NULL) - - interop_qemu_nbd_SOURCES = \ -diff --git a/interop/strict-mode-auto-flag.sh b/interop/strict-mode-auto-flag.sh -new file mode 100755 -index 00000000..8f73ea73 ---- /dev/null -+++ b/interop/strict-mode-auto-flag.sh -@@ -0,0 +1,138 @@ -+#!/usr/bin/env bash -+# nbd client library in userspace -+# Copyright Red Hat -+# -+# This library is free software; you can redistribute it and/or -+# modify it under the terms of the GNU Lesser General Public -+# License as published by the Free Software Foundation; either -+# version 2 of the License, or (at your option) any later version. -+# -+# This library is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY; without even the implied warranty of -+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+# Lesser General Public License for more details. -+# -+# You should have received a copy of the GNU Lesser General Public -+# License along with this library; if not, write to the Free Software -+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -+ -+# Test effect of AUTO_FLAG bit in set_strict_mode() -+ -+source ../tests/functions.sh -+set -e -+set -x -+ -+requires truncate --version -+requires qemu-nbd --version -+requires nbdsh --version -+ -+file="strict-mode-auto-flag.file" -+rm -f $file -+cleanup_fn rm -f $file -+ -+truncate -s 1M $file -+ -+# Unconditional part of test: behavior when extended headers are not in use -+$VG nbdsh -c ' -+import errno -+ -+h.set_request_extended_headers(False) -+args = ["qemu-nbd", "-f", "raw", "'"$file"'"] -+h.connect_systemd_socket_activation(args) -+assert h.get_extended_headers_negotiated() is False -+ -+# STRICT_AUTO_FLAG and STRICT_COMMANDS are on by default -+flags = h.get_strict_mode() -+assert flags & nbd.STRICT_AUTO_FLAG -+assert flags & nbd.STRICT_COMMANDS -+ -+# Under STRICT_AUTO_FLAG, using or omitting flag does not matter; client -+# side auto-corrects the flag before passing to server -+h.pwrite(b"1"*512, 0, 0) -+h.pwrite(b"2"*512, 0, nbd.CMD_FLAG_PAYLOAD_LEN) -+ -+# Without STRICT_AUTO_FLAG but still STRICT_COMMANDS, client side now sees -+# attempts to use the flag as invalid -+flags = flags & ~nbd.STRICT_AUTO_FLAG -+h.set_strict_mode(flags) -+h.pwrite(b"3"*512, 0, 0) -+stats = h.stats_bytes_sent() -+try: -+ h.pwrite(b"4"*512, 0, nbd.CMD_FLAG_PAYLOAD_LEN) -+ assert False -+except nbd.Error as e: -+ assert e.errnum == errno.EINVAL -+assert stats == h.stats_bytes_sent() -+ -+# Warning: fragile test ahead. Without STRICT_COMMANDS, we send unexpected -+# flag to qemu, and expect failure. For qemu <= 8.1, this is safe (those -+# versions did not know the flag, and correctly reject unknown flags with -+# NBD_EINVAL). For qemu 8.2, this also works (qemu knows the flag, but warns -+# that we were not supposed to send it without extended headers). But if -+# future qemu versions change to start silently ignoring the flag (after all, -+# a write command obviously has a payload even without extended headers, so -+# the flag is redundant for NBD_CMD_WRITE), then we may need to tweak this. -+flags = flags & ~nbd.STRICT_COMMANDS -+h.set_strict_mode(flags) -+h.pwrite(b"5"*512, 0, 0) -+stats = h.stats_bytes_sent() -+try: -+ h.pwrite(b"6"*512, 0, nbd.CMD_FLAG_PAYLOAD_LEN) -+ print("Did newer qemu change behavior?") -+ assert False -+except nbd.Error as e: -+ assert e.errnum == errno.EINVAL -+assert stats < h.stats_bytes_sent() -+ -+h.shutdown() -+' -+ -+# Conditional part of test: only run if qemu supports extended headers -+requires nbdinfo --has extended-headers -- [ qemu-nbd -r -f raw "$file" ] -+$VG nbdsh -c ' -+import errno -+ -+args = ["qemu-nbd", "-f", "raw", "'"$file"'"] -+h.connect_systemd_socket_activation(args) -+assert h.get_extended_headers_negotiated() is True -+ -+# STRICT_AUTO_FLAG and STRICT_COMMANDS are on by default -+flags = h.get_strict_mode() -+assert flags & nbd.STRICT_AUTO_FLAG -+assert flags & nbd.STRICT_COMMANDS -+ -+# Under STRICT_AUTO_FLAG, using or omitting flag does not matter; client -+# side auto-corrects the flag before passing to server -+h.pwrite(b"1"*512, 0, 0) -+h.pwrite(b"2"*512, 0, nbd.CMD_FLAG_PAYLOAD_LEN) -+ -+# Without STRICT_AUTO_FLAG but still STRICT_COMMANDS, client side now sees -+# attempts to omit the flag as invalid -+flags = flags & ~nbd.STRICT_AUTO_FLAG -+h.set_strict_mode(flags) -+h.pwrite(b"3"*512, 0, nbd.CMD_FLAG_PAYLOAD_LEN) -+stats = h.stats_bytes_sent() -+try: -+ h.pwrite(b"4"*512, 0, 0) -+ assert False -+except nbd.Error as e: -+ assert e.errnum == errno.EINVAL -+assert stats == h.stats_bytes_sent() -+ -+# Warning: fragile test ahead. Without STRICT_COMMANDS, omitting the flag -+# is a protocol violation. qemu 8.2 silently ignores the violation; but a -+# future qemu might start failing the command, at which point we would need -+# to tweak this part of the test. -+flags = flags & ~nbd.STRICT_COMMANDS -+h.set_strict_mode(flags) -+h.pwrite(b"5"*512, 0, nbd.CMD_FLAG_PAYLOAD_LEN) -+stats = h.stats_bytes_sent() -+try: -+ h.pwrite(b"6"*512, 0, 0) -+except nbd.Error: -+ print("Did newer qemu change behavior?") -+ assert False -+assert stats < h.stats_bytes_sent() -+ -+h.shutdown() -+' --- -2.41.0 - diff --git a/SOURCES/0004-generator-states-newstyle.c-Quote-untrusted-string-f.patch b/SOURCES/0004-generator-states-newstyle.c-Quote-untrusted-string-f.patch new file mode 100644 index 0000000..257230e --- /dev/null +++ b/SOURCES/0004-generator-states-newstyle.c-Quote-untrusted-string-f.patch @@ -0,0 +1,175 @@ +From 923357f49704d55cd74dc5334a12a7089825ff57 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 25 Jul 2024 13:25:34 +0100 +Subject: [PATCH] generator/states-newstyle.c: Quote untrusted string from the + server + +Updates: commit cf49a49adc8abc8c917437db7461ed9956583877 +(cherry picked from commit 5dbfc418cb6176102634acea2256b2335520159c) +--- + generator/states-newstyle.c | 124 ++++++++++++++++++++---------------- + 1 file changed, 68 insertions(+), 56 deletions(-) + +diff --git a/generator/states-newstyle.c b/generator/states-newstyle.c +index 6c7cc45c..8c483bd2 100644 +--- a/generator/states-newstyle.c ++++ b/generator/states-newstyle.c +@@ -18,6 +18,7 @@ + + #include + ++#include "ascii-ctype.h" + #include "internal.h" + + /* Common code for parsing a reply to NBD_OPT_*. */ +@@ -88,80 +89,91 @@ prepare_for_reply_payload (struct nbd_handle *h, uint32_t opt) + static int + handle_reply_error (struct nbd_handle *h) + { +- uint32_t len; + uint32_t reply; +- char *msg = NULL; ++ uint32_t replylen; ++ FILE *fp; ++ char *s = NULL; ++ size_t len = 0; ++ int err = 0; + +- len = be32toh (h->sbuf.or.option_reply.replylen); + reply = be32toh (h->sbuf.or.option_reply.reply); + if (!NBD_REP_IS_ERR (reply)) { + set_error (0, "handshake: unexpected option reply type %d", reply); + return -1; + } + ++ replylen = be32toh (h->sbuf.or.option_reply.replylen); + assert (NBD_MAX_STRING < sizeof h->sbuf.or.payload); +- if (len > NBD_MAX_STRING) { ++ if (replylen > NBD_MAX_STRING) { + set_error (0, "handshake: option error string too long"); + return -1; + } + +- /* Decode expected errors into a nicer string. +- * +- * XXX Note this string comes directly from the server, and most +- * libnbd users simply print the error using 'fprintf'. We really +- * ought to quote this string somehow, but we don't have a useful +- * function for that. +- */ +- if (len > 0) { +- if (asprintf (&msg, ": %.*s", +- (int)len, h->sbuf.or.payload.err_msg) == -1) { +- set_error (errno, "asprintf"); +- return -1; +- } ++ /* Decode expected errors into a nicer string. */ ++ fp = open_memstream (&s, &len); ++ if (fp == NULL) { ++ set_error (errno, "open_memstream"); ++ return -1; + } + + switch (reply) { + case NBD_REP_ERR_UNSUP: +- set_error (ENOTSUP, "the operation is not supported by the server%s", +- msg ? : ""); +- break; +- case NBD_REP_ERR_POLICY: +- set_error (0, "server policy prevents the operation%s", +- msg ? : ""); +- break; +- case NBD_REP_ERR_PLATFORM: +- set_error (0, "the operation is not supported by the server platform%s", +- msg ? : ""); +- break; +- case NBD_REP_ERR_INVALID: +- set_error (EINVAL, "the server rejected this operation as invalid%s", +- msg ? : ""); +- break; +- case NBD_REP_ERR_TOO_BIG: +- set_error (EINVAL, "the operation is too large to process%s", +- msg ? : ""); +- break; +- case NBD_REP_ERR_TLS_REQD: +- set_error (ENOTSUP, "the server requires TLS encryption first%s", +- msg ? : ""); +- break; +- case NBD_REP_ERR_UNKNOWN: +- set_error (ENOENT, "the server has no export named '%s'%s", +- h->export_name, msg ? : ""); +- break; +- case NBD_REP_ERR_SHUTDOWN: +- set_error (ESHUTDOWN, "the server is shutting down%s", +- msg ? : ""); +- break; +- case NBD_REP_ERR_BLOCK_SIZE_REQD: +- set_error (EINVAL, "the server requires specific block sizes%s", +- msg ? : ""); +- break; +- default: +- set_error (0, "handshake: unknown reply from the server: 0x%" PRIx32 "%s", +- reply, msg ? : ""); ++ err = ENOTSUP; ++ fprintf (fp, "the operation is not supported by the server"); ++ break; ++ case NBD_REP_ERR_POLICY: ++ fprintf (fp, "server policy prevents the operation"); ++ break; ++ case NBD_REP_ERR_PLATFORM: ++ fprintf (fp, "the operation is not supported by the server platform"); ++ break; ++ case NBD_REP_ERR_INVALID: ++ err = EINVAL; ++ fprintf (fp, "the server rejected this operation as invalid"); ++ break; ++ case NBD_REP_ERR_TOO_BIG: ++ err = EINVAL; ++ fprintf (fp, "the operation is too large to process"); ++ break; ++ case NBD_REP_ERR_TLS_REQD: ++ err = ENOTSUP; ++ fprintf (fp, "the server requires TLS encryption first"); ++ break; ++ case NBD_REP_ERR_UNKNOWN: ++ err = ENOENT; ++ fprintf (fp, "the server has no export named '%s'", h->export_name); ++ break; ++ case NBD_REP_ERR_SHUTDOWN: ++ err = ESHUTDOWN; ++ fprintf (fp, "the server is shutting down"); ++ break; ++ case NBD_REP_ERR_BLOCK_SIZE_REQD: ++ err = EINVAL; ++ fprintf (fp, "the server requires specific block sizes"); ++ break; ++ default: ++ fprintf (fp, "handshake: unknown reply from the server: 0x%" PRIx32, ++ reply); ++ } ++ ++ if (replylen > 0) { ++ /* Since this message comes from the server, take steps to quote it. */ ++ uint32_t i; ++ const char *msg = h->sbuf.or.payload.err_msg; ++ ++ fprintf (fp, ": "); ++ for (i = 0; i < replylen; ++i) { ++ if (ascii_isprint (msg[i])) ++ fputc (msg[i], fp); ++ else ++ fprintf (fp, "\\x%02x", msg[i]); + } +- free (msg); ++ } ++ ++ fclose (fp); ++ ++ set_error (err, "%s", s); ++ free (s); + + return 0; + } +-- +2.43.0 + diff --git a/SOURCES/0005-generator-states-newstyle.c-Don-t-sign-extend-escape.patch b/SOURCES/0005-generator-states-newstyle.c-Don-t-sign-extend-escape.patch new file mode 100644 index 0000000..2fdd384 --- /dev/null +++ b/SOURCES/0005-generator-states-newstyle.c-Don-t-sign-extend-escape.patch @@ -0,0 +1,27 @@ +From a8e554e53ce8150286e06f4afd578ab52e2f03d9 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 25 Jul 2024 15:48:46 +0100 +Subject: [PATCH] generator/states-newstyle.c: Don't sign extend escaped chars + +Fixes: commit 5dbfc418cb6176102634acea2256b2335520159c +(cherry picked from commit 0d6c6bbb3386de3b60ab6c4831045f2b1896051b) +--- + generator/states-newstyle.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/generator/states-newstyle.c b/generator/states-newstyle.c +index 8c483bd2..1e026a8a 100644 +--- a/generator/states-newstyle.c ++++ b/generator/states-newstyle.c +@@ -159,7 +159,7 @@ handle_reply_error (struct nbd_handle *h) + if (replylen > 0) { + /* Since this message comes from the server, take steps to quote it. */ + uint32_t i; +- const char *msg = h->sbuf.or.payload.err_msg; ++ const unsigned char *msg = (unsigned char *) h->sbuf.or.payload.err_msg; + + fprintf (fp, ": "); + for (i = 0; i < replylen; ++i) { +-- +2.43.0 + diff --git a/SOURCES/copy-patches.sh b/SOURCES/copy-patches.sh index 991798c..c045688 100755 --- a/SOURCES/copy-patches.sh +++ b/SOURCES/copy-patches.sh @@ -6,7 +6,7 @@ set -e # directory. Use it like this: # ./copy-patches.sh -rhel_version=9.4 +rhel_version=9.5 # Check we're in the right directory. if [ ! -f libnbd.spec ]; then diff --git a/SOURCES/libnbd-1.18.1.tar.gz.sig b/SOURCES/libnbd-1.18.1.tar.gz.sig deleted file mode 100644 index 7bd7214..0000000 --- a/SOURCES/libnbd-1.18.1.tar.gz.sig +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQJFBAABCAAvFiEE93dPsa0HSn6Mh2fqkXOPc+G3aKAFAmU2izoRHHJpY2hAYW5u -ZXhpYS5vcmcACgkQkXOPc+G3aKA+txAAkLeWdvH2ryibEyqMeyejvh9vMgQO5I46 -LaygI8jDi+XG+rGy7imiwIxxWvyCZI3y2U5MFudLZoFi+gCyVAC+LeBxjF41NBGz -fbgwFaQHrCbxyLlsj9OcR6M0+EPU8NXXPPGgXZNcnf7tHNZkTO0OGS9chml0wXHA -Zx9WheHl6wbLTVIAtLWOJqzRQj80RlcPC+De1wZL+WFMPMkfF8L8K5FRNsfeTIXn -l31d1R0g5QOMTTqBiKE2iopPmVmA5uC/adWCuqF3mzzjzCkHp+Ux/Ys99tkCETrU -jUuHgJ+1pYjn4Lmt/HUwXQZD3L+RkNAWWQziY/3ejK31tGxZqR/XTwq5RPrc6Qs1 -/zuoWvSWJZZo9yvX1Iq2RQAaZF5724V/svm+HgaCakaK8EJEj6sntM0OhAl2pRC4 -G45Kb2o7k016WgL8plNOlrbHNxaruBcPrkFYDMoyy3KLnWaw3OYMARTD5w/Pd4dC -CJa3tIXIKedhXw9xDtEWfxiKIHfO+LBHBMjpW9KzP/oxE2akmcJZJT+JNpsrpdzV -O6mbVDPedWd5LQQ1bNmwzxkCsMEC9HFhVbCaTuYuSXe/By04Norns4xyEJyDXlG/ -QqFgEUS8R+V9xwYHoAPg5RUmycXubSmm64iTAQNqc46QsxeP0Va7gpbrPLe5qVk/ -irUsxdBhGIM= -=pgmp ------END PGP SIGNATURE----- diff --git a/SOURCES/libnbd-1.20.2.tar.gz.sig b/SOURCES/libnbd-1.20.2.tar.gz.sig new file mode 100644 index 0000000..80a5dcf --- /dev/null +++ b/SOURCES/libnbd-1.20.2.tar.gz.sig @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJFBAABCAAvFiEE93dPsa0HSn6Mh2fqkXOPc+G3aKAFAmZ7KY8RHHJpY2hAYW5u +ZXhpYS5vcmcACgkQkXOPc+G3aKAP7A/9EzaUWFPP1GDCrpfvVods7F+8DN87O/aZ +mTzw59HNZVz42Yw0cM8MAEckgnpMdT7d64m6E1lx8fxIYGaO+c4r+VeZfYlsZIam +HFzTe4UYwdjZT1HcR9njSkdL/6th5ZMM6NK+RI3MBmQAc5TfFxPUsXbHSAfLgclb +v3yh2F0x6gnSBMxdVVYGha/eGegLZONGhZIxVYlVr0VlFrpy3Lo6+4mxIw5Sbery +cY+LxKwpyx1Gj01qZS+muub7kCXefwZggf15F1NqaQNz6tzriwXpW1yrZGayERmT +s11htXEvqSI3ViO3Rf/paq5vKwo5v7AMLHI7cv2dzrmNglLrEsY4GywlHdgbDYLh +7qY6/35WzQwCcCWlvXP3K4SIkauH5lVR0KMsdU+idxpodqtHSTQN8f0z1vSSf5We +wilBzw1LPiD55Dz7/9AIbjU/w/iUREsZceplbis1DFsVxf+cVZTtwx0LJn2enKTc +O61LVcV9FDB7EJIdTQfFr9WJTR+6gfSF5R7H4kMX8gZHucoUruoXA+M4fqV8aRHc +5lrzgnsVN/GatF5R63yt6dSByQGqnyhpLynoYWCht64PRglb7XekGuT9AR3m85VP +hTu1fZsLfmVl9iJjc47/c5CKq9mAhoicDiCVCgTidCq5O9SmI2HgmCPUH/nd2+Fc +uB2geCRdnZ0= +=dUDc +-----END PGP SIGNATURE----- diff --git a/SPECS/libnbd.spec b/SPECS/libnbd.spec index b523ea2..a937df8 100644 --- a/SPECS/libnbd.spec +++ b/SPECS/libnbd.spec @@ -1,15 +1,27 @@ +# i686 no longer has any kind of OCaml compiler, not even ocamlc. +%ifnarch %{ix86} +%global have_ocaml 1 +%endif + +# No ublk in RHEL 9. +%if !0%{?rhel} +%global have_ublk 1 +%endif + +# No nbd.ko in RHEL 9. +%if !0%{?rhel} +%global have_nbd_ko 1 +%endif + # If we should verify tarball signature with GPGv2. %global verify_tarball_signature 1 -# If there are patches which touch autotools files, set this to 1. -%global patches_touch_autotools 1 - # The source directory. -%global source_directory 1.18-stable +%global source_directory 1.20-stable Name: libnbd -Version: 1.18.1 -Release: 3%{?dist} +Version: 1.20.2 +Release: 2%{?dist} Summary: NBD client library in userspace License: LGPL-2.0-or-later AND BSD-3-Clause @@ -26,21 +38,21 @@ Source2: libguestfs.keyring Source3: copy-patches.sh # Patches are stored in the upstream repository: -# https://gitlab.com/nbdkit/libnbd/-/commits/rhel-9.4/ +# https://gitlab.com/nbdkit/libnbd/-/commits/rhel-9.5/ -# Patches. -Patch0001: 0001-generator-Fix-assertion-in-ext-mode-BLOCK_STATUS-CVE.patch -Patch0002: 0002-docs-Fix-incorrect-xref-in-libnbd-release-notes-for-.patch -Patch0003: 0003-tests-Check-behavior-of-nbd_set_strict_mode-STRICT_A.patch - -%if 0%{patches_touch_autotools} -BuildRequires: autoconf, automake, libtool -%endif +Patch0001: 0001-generator-Print-full-error-in-handle_reply_error.patch +Patch0002: 0002-lib-Don-t-overwrite-error-in-nbd_opt_-go-info.patch +Patch0003: 0003-generator-Restore-assignment-to-local-err.patch +Patch0004: 0004-generator-states-newstyle.c-Quote-untrusted-string-f.patch +Patch0005: 0005-generator-states-newstyle.c-Don-t-sign-extend-escape.patch %if 0%{verify_tarball_signature} BuildRequires: gnupg2 %endif +# For rebuilding autoconf cruft. +BuildRequires: autoconf, automake, libtool + # For the core library. BuildRequires: gcc BuildRequires: make @@ -51,7 +63,7 @@ BuildRequires: libxml2-devel # For nbdfuse. BuildRequires: fuse3, fuse3-devel -%if !0%{?rhel} +%if 0%{?have_ublk} # For nbdublk BuildRequires: liburing-devel >= 2.2 BuildRequires: ubdsrv-devel >= 1.0-3.rc6 @@ -60,7 +72,7 @@ BuildRequires: ubdsrv-devel >= 1.0-3.rc6 # For the Python 3 bindings. BuildRequires: python3-devel -%ifnarch %{ix86} +%if 0%{?have_ocaml} # For the OCaml bindings. BuildRequires: ocaml BuildRequires: ocaml-findlib-devel @@ -79,7 +91,7 @@ BuildRequires: gcc-c++ BuildRequires: gnutls-utils BuildRequires: iproute BuildRequires: jq -%if !0%{?rhel} +%if 0%{?have_nbd_ko} BuildRequires: nbd %endif BuildRequires: util-linux @@ -100,7 +112,7 @@ BuildRequires: nbdkit-sh-plugin BuildRequires: nbdkit-sparse-random-plugin %endif -%ifnarch %{ix86} +%if 0%{?have_ocaml} # The OCaml runtime system does not provide this symbol %global __ocaml_requires_opts -x Stdlib__Callback %endif @@ -136,7 +148,7 @@ Requires: %{name}%{?_isa} = %{version}-%{release} This package contains development headers for %{name}. -%ifnarch %{ix86} +%if 0%{?have_ocaml} %package -n ocaml-%{name} Summary: OCaml language bindings for %{name} Requires: %{name}%{?_isa} = %{version}-%{release} @@ -182,7 +194,7 @@ Recommends: fuse3 This package contains FUSE support for %{name}. -%if !0%{?rhel} +%if 0%{?have_ublk} %package -n nbdublk Summary: Userspace NBD block device Requires: %{name}%{?_isa} = %{version}-%{release} @@ -215,25 +227,30 @@ for %{name}. %{gpgverify} --keyring='%{SOURCE2}' --signature='%{SOURCE1}' --data='%{SOURCE0}' %endif %autosetup -p1 -%if 0%{patches_touch_autotools} autoreconf -i -%endif %build %configure \ --disable-static \ --with-tls-priority=@LIBNBD,SYSTEM \ + --with-bash-completions \ PYTHON=%{__python3} \ --enable-python \ -%ifnarch %{ix86} +%if 0%{?have_ocaml} --enable-ocaml \ %else --disable-ocaml \ %endif --enable-fuse \ --disable-golang \ - --disable-rust + --disable-rust \ +%if 0%{?have_ublk} + --enable-ublk \ +%else + --disable-ublk \ +%endif + %{nil} make %{?_smp_mflags} @@ -247,16 +264,11 @@ find $RPM_BUILD_ROOT -name '*.la' -delete # Delete the golang man page since we're not distributing the bindings. rm $RPM_BUILD_ROOT%{_mandir}/man3/libnbd-golang.3* -%ifarch %{ix86} +%if !0%{?have_ocaml} # Delete the OCaml man page on i686. rm $RPM_BUILD_ROOT%{_mandir}/man3/libnbd-ocaml.3* %endif -%if 0%{?rhel} -# Delete nbdublk on RHEL. -rm $RPM_BUILD_ROOT%{_datadir}/bash-completion/completions/nbdublk -%endif - %check function skip_test () @@ -268,12 +280,6 @@ function skip_test () done } -# interop/structured-read.sh fails with the old qemu-nbd in Fedora 29, -# so disable it there. -%if 0%{?fedora} <= 29 -skip_test interop/structured-read.sh -%endif - # interop/interop-qemu-storage-daemon.sh fails in RHEL 9 because of # this bug in qemu: # https://lists.nongnu.org/archive/html/qemu-devel/2021-03/threads.html#03544 @@ -324,7 +330,7 @@ make %{?_smp_mflags} check || { %{_mandir}/man3/nbd_*.3* -%ifnarch %{ix86} +%if 0%{?have_ocaml} %files -n ocaml-%{name} %dir %{_libdir}/ocaml/nbd %{_libdir}/ocaml/nbd/META @@ -363,7 +369,7 @@ make %{?_smp_mflags} check || { %{_mandir}/man1/nbdfuse.1* -%if !0%{?rhel} +%if 0%{?have_ublk} %files -n nbdublk %{_bindir}/nbdublk %{_mandir}/man1/nbdublk.1* @@ -383,6 +389,18 @@ make %{?_smp_mflags} check || { %changelog +* Fri Jul 26 2024 Richard W.M. Jones - 1.20.2-2 +- Rebase to libnbd 1.20.2 + resolves: RHEL-31883 +- Fix multiple flaws in TLS server certificate checking + resolves: RHEL-49801 +- Print full NBD error from server + resolves: RHEL-50665 + +* Tue Apr 09 2024 Miroslav Rezanina - 1.20.0-1 +- Rebase to 1.20.0 + resolves: RHEL-31883 + * Mon Nov 13 2023 Eric Blake - 1.18.1-3 - Backport unit test of recent libnbd API addition resolves: RHEL-16292