parent
0bd77eef71
commit
029aadbd59
@ -1,2 +1,2 @@
|
|||||||
SOURCES/libguestfs.keyring
|
SOURCES/libguestfs.keyring
|
||||||
SOURCES/libnbd-1.18.1.tar.gz
|
SOURCES/libnbd-1.20.2.tar.gz
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
cc1b37b9cfafa515aab3eefd345ecc59aac2ce7b SOURCES/libguestfs.keyring
|
cc1b37b9cfafa515aab3eefd345ecc59aac2ce7b SOURCES/libguestfs.keyring
|
||||||
4f99e6f21edffe62b394aa9c7fb68149e6d4d5e4 SOURCES/libnbd-1.18.1.tar.gz
|
d3abc6e794dd4b99640328bfd395a8913288e859 SOURCES/libnbd-1.20.2.tar.gz
|
||||||
|
@ -1,88 +0,0 @@
|
|||||||
From 4451e5b61ca07771ceef3e012223779e7a0c7701 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Eric Blake <eblake@redhat.com>
|
|
||||||
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 <rjones@redhat.com>
|
|
||||||
Fixes: 20dadb0e10 ("generator: Prepare for extent64 callback", v1.17.4)
|
|
||||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
|
||||||
(cherry picked from commit 177308adb17e81fce7c0f2b2fcf655c5c0b6a4d6)
|
|
||||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
|
||||||
---
|
|
||||||
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
|
|
||||||
|
|
@ -0,0 +1,191 @@
|
|||||||
|
From 89f0e33f355681b7c5b1835a09c5fac93c08e71e Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Richard W.M. Jones" <rjones@redhat.com>
|
||||||
|
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
|
||||||
|
|
@ -1,34 +0,0 @@
|
|||||||
From c39e31b7a20c7dc8aa12c5fa3f1742824e1e0c76 Mon Sep 17 00:00:00 2001
|
|
||||||
From: "Richard W.M. Jones" <rjones@redhat.com>
|
|
||||||
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 <eblake@redhat.com>
|
|
||||||
---
|
|
||||||
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<nbd_shutdown(3)> now works correctly when in opt mode (Eric Blake).
|
|
||||||
|
|
||||||
-L<nbd_set_string(3)> adds C<LIBNBD_STRICT_AUTO_FLAG> which allows the
|
|
||||||
-client to test how servers behave when the payload length flag is
|
|
||||||
+L<nbd_set_strict_mode(3)> adds C<LIBNBD_STRICT_AUTO_FLAG> which allows
|
|
||||||
+the client to test how servers behave when the payload length flag is
|
|
||||||
adjusted (Eric Blake).
|
|
||||||
|
|
||||||
=head2 Protocol
|
|
||||||
--
|
|
||||||
2.41.0
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
|||||||
|
From f31e31b32e662d0aadb77bc639308c8a65968886 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Richard W.M. Jones" <rjones@redhat.com>
|
||||||
|
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
|
||||||
|
|
@ -0,0 +1,43 @@
|
|||||||
|
From ff5ee9308e66fe9fd19ea578623c4540f44942d6 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Richard W.M. Jones" <rjones@redhat.com>
|
||||||
|
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
|
||||||
|
|
@ -1,206 +0,0 @@
|
|||||||
From 32cb9ab9f1701b1a1a826b48f2083cb75adf1e87 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Eric Blake <eblake@redhat.com>
|
|
||||||
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 <eblake@redhat.com>
|
|
||||||
(cherry picked from commit 54d4426394c372413f55f648d4ad1d21b3395e07)
|
|
||||||
Signed-off-by: Eric Blake <eblake@redhat.com>
|
|
||||||
---
|
|
||||||
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
|
|
||||||
|
|
@ -0,0 +1,175 @@
|
|||||||
|
From 923357f49704d55cd74dc5334a12a7089825ff57 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Richard W.M. Jones" <rjones@redhat.com>
|
||||||
|
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 <assert.h>
|
||||||
|
|
||||||
|
+#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
|
||||||
|
|
@ -0,0 +1,27 @@
|
|||||||
|
From a8e554e53ce8150286e06f4afd578ab52e2f03d9 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Richard W.M. Jones" <rjones@redhat.com>
|
||||||
|
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
|
||||||
|
|
@ -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-----
|
|
@ -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-----
|
Loading…
Reference in new issue