Skip to content

Commit 611f931

Browse files
committed
server: Wait until handshake complete before calling .open callback
Currently we call the plugin .open callback as soon as we receive a TCP connection: $ nbdkit -fv --tls=require --tls-certificates=tests/pki null \ --run "telnet localhost 10809" [...] Trying ::1... Connected to localhost. Escape character is '^]'. nbdkit: debug: accepted connection nbdkit: debug: null: open readonly=0 ◀ NOTE nbdkit: null[1]: debug: newstyle negotiation: flags: global 0x3 NBDMAGICIHAVEOPT In plugins such as curl, guestfs, ssh, vddk and others we do a considerable amount of work in the .open callback (such as making a remote connection or launching an appliance). Therefore we are providing an easy Denial of Service / Amplification Attack for unauthorized clients to cause a lot of work to be done for only the cost of a simple TCP 3 way handshake. This commit moves the call to the .open callback after the NBD handshake has mostly completed. In particular TLS authentication must be complete before we will call into the plugin. It is unlikely that there are plugins which really depend on the current behaviour of .open (which I found surprising even though I guess I must have written it). If there are then we could add a new .connect callback or similar to allow plugins to get control at the earlier point in the connection. After this change you can see that the .open callback is not called from just a simple TCP connection: $ ./nbdkit -fv --tls=require --tls-certificates=tests/pki null \ --run "telnet localhost 10809" [...] Trying ::1... Connected to localhost. Escape character is '^]'. nbdkit: debug: accepted connection nbdkit: null[1]: debug: newstyle negotiation: flags: global 0x3 NBDMAGICIHAVEOPT xx nbdkit: null[1]: debug: newstyle negotiation: client flags: 0xd0a7878 nbdkit: null[1]: error: client requested unknown flags 0xd0a7878 Connection closed by foreign host. nbdkit: debug: null: unload plugin Signed-off-by: Richard W.M. Jones <rjones@redhat.com> (cherry picked from commit c05686f) (cherry picked from commit e06cde0)
1 parent 8538ef6 commit 611f931

File tree

1 file changed

+40
-43
lines changed

1 file changed

+40
-43
lines changed

src/connections.c

Lines changed: 40 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -246,12 +246,6 @@ _handle_single_connection (int sockin, int sockout)
246246
if (!conn)
247247
goto done;
248248

249-
lock_request (conn);
250-
r = backend->open (backend, conn, readonly);
251-
unlock_request (conn);
252-
if (r == -1)
253-
goto done;
254-
255249
/* NB: because of an asynchronous exit backend can be set to NULL at
256250
* just about any time.
257251
*/
@@ -261,17 +255,11 @@ _handle_single_connection (int sockin, int sockout)
261255
plugin_name = "(unknown)";
262256
threadlocal_set_name (plugin_name);
263257

264-
/* Prepare (for filters), called just after open. */
265-
lock_request (conn);
266-
if (backend)
267-
r = backend->prepare (backend, conn);
268-
else
269-
r = 0;
270-
unlock_request (conn);
271-
if (r == -1)
272-
goto done;
273-
274-
/* Handshake. */
258+
/* NBD handshake.
259+
*
260+
* Note that this calls the backend .open callback when it is safe
261+
* to do so (eg. after TLS authentication).
262+
*/
275263
if (negotiate_handshake (conn) == -1)
276264
goto done;
277265

@@ -408,12 +396,43 @@ free_connection (struct connection *conn)
408396
free (conn);
409397
}
410398

399+
/* Common code used by oldstyle and newstyle protocols to:
400+
*
401+
* - call the backend .open method
402+
*
403+
* - get the export size
404+
*
405+
* - compute the eflags (same between oldstyle and newstyle
406+
* protocols)
407+
*
408+
* The protocols must defer this as late as possible so that
409+
* unauthorized clients can't cause unnecessary work in .open by
410+
* simply opening a TCP connection.
411+
*/
411412
static int
412-
compute_eflags (struct connection *conn, uint16_t *flags)
413+
protocol_common_open (struct connection *conn,
414+
uint64_t *exportsize, uint16_t *flags)
413415
{
416+
int64_t size;
414417
uint16_t eflags = NBD_FLAG_HAS_FLAGS;
415418
int fl;
416419

420+
if (backend->open (backend, conn, readonly) == -1)
421+
return -1;
422+
423+
/* Prepare (for filters), called just after open. */
424+
if (backend->prepare (backend, conn) == -1)
425+
return -1;
426+
427+
size = backend->get_size (backend, conn);
428+
if (size == -1)
429+
return -1;
430+
if (size < 0) {
431+
nbdkit_error (".get_size function returned invalid value "
432+
"(%" PRIi64 ")", size);
433+
return -1;
434+
}
435+
417436
fl = backend->can_write (backend, conn);
418437
if (fl == -1)
419438
return -1;
@@ -463,6 +482,7 @@ compute_eflags (struct connection *conn, uint16_t *flags)
463482
conn->is_rotational = 1;
464483
}
465484

485+
*exportsize = size;
466486
*flags = eflags;
467487
return 0;
468488
}
@@ -471,7 +491,6 @@ static int
471491
_negotiate_handshake_oldstyle (struct connection *conn)
472492
{
473493
struct old_handshake handshake;
474-
int64_t r;
475494
uint64_t exportsize;
476495
uint16_t gflags, eflags;
477496

@@ -483,21 +502,11 @@ _negotiate_handshake_oldstyle (struct connection *conn)
483502
return -1;
484503
}
485504

486-
r = backend->get_size (backend, conn);
487-
if (r == -1)
488-
return -1;
489-
if (r < 0) {
490-
nbdkit_error (".get_size function returned invalid value "
491-
"(%" PRIi64 ")", r);
505+
if (protocol_common_open (conn, &exportsize, &eflags) == -1)
492506
return -1;
493-
}
494-
exportsize = (uint64_t) r;
495507
conn->exportsize = exportsize;
496508

497509
gflags = 0;
498-
if (compute_eflags (conn, &eflags) < 0)
499-
return -1;
500-
501510
debug ("oldstyle negotiation: flags: global 0x%x export 0x%x",
502511
gflags, eflags);
503512

@@ -607,19 +616,7 @@ send_newstyle_option_reply_info_export (struct connection *conn,
607616
static int
608617
finish_newstyle_options (struct connection *conn)
609618
{
610-
int64_t r;
611-
612-
r = backend->get_size (backend, conn);
613-
if (r == -1)
614-
return -1;
615-
if (r < 0) {
616-
nbdkit_error (".get_size function returned invalid value "
617-
"(%" PRIi64 ")", r);
618-
return -1;
619-
}
620-
conn->exportsize = (uint64_t) r;
621-
622-
if (compute_eflags (conn, &conn->eflags) < 0)
619+
if (protocol_common_open (conn, &conn->exportsize, &conn->eflags) == -1)
623620
return -1;
624621

625622
debug ("newstyle negotiation: flags: export 0x%x", conn->eflags);

0 commit comments

Comments
 (0)