Skip to content

Commit 1be20b5

Browse files
committed
connectd: validate connections to alt-addr against a whitelist
1 parent e43ab91 commit 1be20b5

File tree

16 files changed

+383
-4
lines changed

16 files changed

+383
-4
lines changed

common/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ COMMON_SRC_NOGEN := \
103103
common/utxo.c \
104104
common/version.c \
105105
common/wallet.c \
106+
common/whitelisted_peer.c \
106107
common/wireaddr.c \
107108
common/wire_error.c
108109

common/whitelisted_peer.c

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#include "config.h"
2+
#include <common/whitelisted_peer.h>
3+
#include <connectd/connectd.h>
4+
#include <wire/wire.h>
5+
6+
static void destroy_whitelisted_peer(struct whitelisted_peer *peer)
7+
{
8+
if (peer->my_alt_addrs)
9+
tal_free(peer->my_alt_addrs);
10+
tal_free(peer);
11+
}
12+
13+
void populate_whitelist_table(struct daemon *daemon,
14+
struct whitelisted_peer *wp)
15+
{
16+
if (wp) {
17+
size_t num_whitelisted = tal_count(wp);
18+
for (size_t i = 0; i < num_whitelisted; i++) {
19+
if (whitelisted_peer_htable_get(daemon->whitelisted_peer_htable,
20+
&wp[i].id)) {
21+
tal_free(&wp[i]);
22+
continue;
23+
}
24+
25+
struct whitelisted_peer *new_peer = tal_steal(daemon,
26+
&wp[i]);
27+
28+
whitelisted_peer_htable_add(daemon->whitelisted_peer_htable,
29+
new_peer);
30+
31+
tal_add_destructor(new_peer, destroy_whitelisted_peer);
32+
}
33+
}
34+
}
35+
36+
void towire_whitelisted_peer(uint8_t **p, const struct whitelisted_peer *wp)
37+
{
38+
/* Serialize the node_id */
39+
towire_node_id(p, &wp->id);
40+
41+
/* Serialize the num of addrs */
42+
uint16_t num_alt_addrs = tal_count(wp->my_alt_addrs);
43+
towire_u16(p, num_alt_addrs);
44+
45+
/* Serialize each alternate address */
46+
for (size_t i = 0; i < num_alt_addrs; i++)
47+
towire_wireaddr_internal(p, &wp->my_alt_addrs[i]);
48+
}
49+
50+
bool fromwire_whitelisted_peer(const uint8_t **cursor,
51+
size_t *plen,
52+
struct whitelisted_peer *wp)
53+
{
54+
/* Deserialize the node_id */
55+
fromwire_node_id(cursor, plen, &wp->id);
56+
57+
/* Deserialize the number of alternate addresses */
58+
uint16_t num_alt_addrs = fromwire_u16(cursor, plen);
59+
60+
wp->my_alt_addrs = tal_arr(wp, struct wireaddr_internal, num_alt_addrs);
61+
62+
/* Deserialize each alternate address */
63+
for (size_t i = 0; i < num_alt_addrs; i++)
64+
if (!fromwire_wireaddr_internal(cursor, plen, &wp->my_alt_addrs[i]))
65+
return false;
66+
67+
return *cursor != NULL;
68+
}

common/whitelisted_peer.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#ifndef LIGHTNING_COMMON_WHITELISTED_PEER_H
2+
#define LIGHTNING_COMMON_WHITELISTED_PEER_H
3+
#include "config.h"
4+
#include <ccan/htable/htable_type.h>
5+
#include <ccan/tal/tal.h>
6+
#include <common/node_id.h>
7+
#include <common/wireaddr.h>
8+
#include <stdbool.h>
9+
#include <stddef.h>
10+
#include <stdint.h>
11+
12+
struct daemon;
13+
14+
struct whitelisted_peer {
15+
struct node_id id;
16+
struct wireaddr_internal *my_alt_addrs;
17+
};
18+
19+
/* Function for populating the hashtable */
20+
void populate_whitelist_table(struct daemon *daemon,
21+
struct whitelisted_peer *peers);
22+
23+
void towire_whitelisted_peer(uint8_t **p,
24+
const struct whitelisted_peer *whitelisted_peer);
25+
26+
bool fromwire_whitelisted_peer(const uint8_t **cursor, size_t *plen,
27+
struct whitelisted_peer *whitelisted_peer);
28+
29+
#endif /* LIGHTNING_COMMON_WHITELISTED_PEER_H */

connectd/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ CONNECTD_COMMON_OBJS := \
8282
common/utils.o \
8383
common/utxo.o \
8484
common/version.o \
85+
common/whitelisted_peer.o \
8586
common/wireaddr.o \
8687
common/wire_error.o \
8788
gossipd/gossipd_wiregen.o \

connectd/connectd.c

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,77 @@ struct io_plan *peer_connected(struct io_conn *conn,
313313
return multiplex_peer_setup(conn, peer);
314314
}
315315

316+
static bool verify_alt_addr(struct io_conn *conn,
317+
struct daemon *daemon,
318+
const struct node_id *id)
319+
{
320+
struct sockaddr_in local_addr;
321+
socklen_t addr_len = sizeof(local_addr);
322+
323+
/* Get local address and port */
324+
if (getsockname(io_conn_fd(conn), (struct sockaddr *)&local_addr,
325+
&addr_len) == -1) {
326+
status_broken("verify_alt_addr: getsockname failed");
327+
return false;
328+
}
329+
330+
char listening_addr[INET_ADDRSTRLEN];
331+
if (!inet_ntop(AF_INET, &local_addr.sin_addr, listening_addr,
332+
sizeof(listening_addr))) {
333+
status_broken("verify_alt_addr: inet_ntop failed");
334+
return false;
335+
}
336+
int listening_port = ntohs(local_addr.sin_port);
337+
338+
char full_listening_addr[INET_ADDRSTRLEN + 6];
339+
snprintf(full_listening_addr, sizeof(full_listening_addr), "%s:%d",
340+
listening_addr, listening_port);
341+
342+
struct wireaddr_internal search_addr;
343+
if (parse_wireaddr_internal(tmpctx, full_listening_addr, 0,
344+
false, &search_addr) != NULL) {
345+
status_broken("verify_alt_addr: parse_wireaddr_internal failed");
346+
return false;
347+
}
348+
349+
struct whitelisted_peer *wp = whitelisted_peer_htable_get(daemon->whitelisted_peer_htable,
350+
id);
351+
bool is_whitelisted = false;
352+
353+
if (wp) {
354+
size_t num_addrs = tal_count(wp->my_alt_addrs);
355+
for (size_t i = 0; i < num_addrs; ++i) {
356+
char *whitelist_addr_str = fmt_wireaddr_internal(tmpctx,
357+
&wp->my_alt_addrs[i]);
358+
if (strcmp(full_listening_addr, whitelist_addr_str) == 0) {
359+
is_whitelisted = true;
360+
status_debug("Peer's address %s is in the whitelist. Accepting connection.",
361+
full_listening_addr);
362+
goto check_alt_bind_addr;
363+
}
364+
}
365+
}
366+
367+
check_alt_bind_addr:
368+
/* Check against alt_bind_addr only if the connection is not whitelisted */
369+
if (!is_whitelisted) {
370+
char *alt_bind_addrs = tal_strdup(tmpctx,
371+
(const char *)daemon->alt_bind_addr);
372+
for (char *alt_bind_token = strtok(alt_bind_addrs, ",");
373+
alt_bind_token;
374+
alt_bind_token = strtok(NULL, ",")) {
375+
if (strcmp(full_listening_addr, alt_bind_token) == 0) {
376+
status_unusual("Connection attempt from address %s which is not in the whitelist. Closing connection.",
377+
full_listening_addr);
378+
tal_free(alt_bind_addrs);
379+
return false;
380+
}
381+
}
382+
tal_free(alt_bind_addrs);
383+
}
384+
return true;
385+
}
386+
316387
/*~ handshake.c's handles setting up the crypto state once we get a connection
317388
* in; we hand it straight to peer_exchange_initmsg() to send and receive INIT
318389
* and call peer_connected(). */
@@ -327,6 +398,12 @@ static struct io_plan *handshake_in_success(struct io_conn *conn,
327398
struct node_id id;
328399
node_id_from_pubkey(&id, id_key);
329400
status_peer_debug(&id, "Connect IN");
401+
402+
/* Confirm that peer connects to the alt-bind-addr you sent */
403+
if (daemon->alt_bind_addr)
404+
if (!verify_alt_addr(conn, daemon, &id))
405+
return (io_close(conn));
406+
330407
return peer_exchange_initmsg(conn, daemon, daemon->our_features,
331408
cs, &id, addr, timeout, is_websocket, true);
332409
}
@@ -1438,7 +1515,8 @@ static void connect_init(struct daemon *daemon, const u8 *msg)
14381515
&dev_disconnect,
14391516
&daemon->dev_no_ping_timer,
14401517
&daemon->dev_handshake_no_reply,
1441-
&dev_throttle_gossip)) {
1518+
&dev_throttle_gossip,
1519+
&daemon->alt_bind_addr)) {
14421520
/* This is a helper which prints the type expected and the actual
14431521
* message, then exits (it should never be called!). */
14441522
master_badmsg(WIRE_CONNECTD_INIT, msg);
@@ -1925,6 +2003,7 @@ static void dev_connect_memleak(struct daemon *daemon, const u8 *msg)
19252003
memleak_scan_obj(memtable, daemon);
19262004
memleak_scan_htable(memtable, &daemon->peers->raw);
19272005
memleak_scan_htable(memtable, &daemon->scid_htable->raw);
2006+
memleak_scan_htable(memtable, &daemon->whitelisted_peer_htable->raw);
19282007

19292008
found_leak = dump_memleak(memtable, memleak_status_broken, NULL);
19302009
daemon_conn_send(daemon->master,
@@ -2161,6 +2240,18 @@ static void dev_exhaust_fds(struct daemon *daemon, const u8 *msg)
21612240
daemon->dev_exhausted_fds = true;
21622241
}
21632242

2243+
static void handle_alt_addr_whitelist(struct daemon *daemon, const u8 *msg)
2244+
{
2245+
struct whitelisted_peer *received_peers;
2246+
2247+
if (!fromwire_connectd_alt_addr_whitelist(daemon, msg, &received_peers)) {
2248+
master_badmsg(WIRE_CONNECTD_ALT_ADDR_WHITELIST, msg);
2249+
return;
2250+
}
2251+
2252+
populate_whitelist_table(daemon, received_peers);
2253+
}
2254+
21642255
static struct io_plan *recv_peer_connect_subd(struct io_conn *conn,
21652256
const u8 *msg,
21662257
int fd,
@@ -2262,6 +2353,10 @@ static struct io_plan *recv_req(struct io_conn *conn,
22622353
goto out;
22632354
}
22642355
/* Fall thru */
2356+
case WIRE_CONNECTD_ALT_ADDR_WHITELIST:
2357+
handle_alt_addr_whitelist(daemon, msg);
2358+
goto out;
2359+
/* Fall thru */
22652360
/* We send these, we don't receive them */
22662361
case WIRE_CONNECTD_INIT_REPLY:
22672362
case WIRE_CONNECTD_ACTIVATE_REPLY:
@@ -2363,6 +2458,9 @@ int main(int argc, char *argv[])
23632458
daemon->gossip_stream_limit = 1000000;
23642459
daemon->scid_htable = tal(daemon, struct scid_htable);
23652460
scid_htable_init(daemon->scid_htable);
2461+
daemon->whitelisted_peer_htable = tal(daemon,
2462+
struct whitelisted_peer_htable);
2463+
whitelisted_peer_htable_init(daemon->whitelisted_peer_htable);
23662464

23672465
/* stdin == control */
23682466
daemon->master = daemon_conn_new(daemon, STDIN_FILENO, recv_req, NULL,

connectd/connectd.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <common/crypto_state.h>
1212
#include <common/node_id.h>
1313
#include <common/pseudorand.h>
14+
#include <common/whitelisted_peer.h>
1415
#include <common/wireaddr.h>
1516
#include <connectd/handshake.h>
1617

@@ -226,6 +227,24 @@ HTABLE_DEFINE_TYPE(struct scid_to_node_id,
226227
scid_to_node_id_eq_scid,
227228
scid_htable);
228229

230+
static const struct node_id *whitelisted_peer_keyof(const struct whitelisted_peer *wp)
231+
{
232+
return &wp->id;
233+
}
234+
235+
static bool whitelisted_peer_eq(const struct whitelisted_peer *wp, const struct node_id *id)
236+
{
237+
return node_id_eq(&wp->id, id);
238+
}
239+
240+
/*~ This defines 'struct witelisted_peer_htable' which contains
241+
* 'struct whitelisted_peer' pointers. */
242+
HTABLE_DEFINE_TYPE(struct whitelisted_peer,
243+
whitelisted_peer_keyof,
244+
node_id_hash,
245+
whitelisted_peer_eq,
246+
whitelisted_peer_htable);
247+
229248
/*~ This is the global state, like `struct lightningd *ld` in lightningd. */
230249
struct daemon {
231250
/* Who am I? */
@@ -289,6 +308,11 @@ struct daemon {
289308
/* Our features, as lightningd told us */
290309
struct feature_set *our_features;
291310

311+
/* check whitelist before accepting incoming alt addr */
312+
struct whitelisted_peer_htable *whitelisted_peer_htable;
313+
/* Bind for 'alt-addr' rpc cmd, or '--alt-annouce-addr' listening, but do not announce */
314+
u8 *alt_bind_addr;
315+
292316
/* Subdaemon to proxy websocket requests. */
293317
char *websocket_helper;
294318

connectd/connectd_wire.csv

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <common/cryptomsg.h>
44
#include <common/features.h>
55
#include <common/node_id.h>
6+
#include <common/whitelisted_peer.h>
67
#include <common/wireaddr.h>
78
#include <wire/onion_wire.h>
89

@@ -28,6 +29,9 @@ msgdata,connectd_init,dev_no_ping_timer,bool,
2829
# Allow incoming connections, but don't talk.
2930
msgdata,connectd_init,dev_noreply,bool,
3031
msgdata,connectd_init,dev_throttle_gossip,bool,
32+
# Whitelist for incoming alternative address connections
33+
msgdata,connectd_init,alt_bind_addr_len,u8,
34+
msgdata,connectd_init,alt_bind_addr,byte,alt_bind_addr_len,
3135

3236
# Connectd->master, here are the addresses I bound, can announce.
3337
msgtype,connectd_init_reply,2100
@@ -194,3 +198,7 @@ msgtype,connectd_peer_alt_addr,2037
194198
msgdata,connectd_peer_alt_addr,id,node_id,
195199
msgdata,connectd_peer_alt_addr,addr_len,u8,
196200
msgdata,connectd_peer_alt_addr,addr,byte,addr_len,
201+
202+
# master -> connectd: alternative connection whitelist
203+
msgtype,connectd_alt_addr_whitelist,2138
204+
msgdata,connectd_alt_addr_whitelist,whitelisted_peer,?whitelisted_peer,

lightningd/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ LIGHTNINGD_COMMON_OBJS := \
151151
common/utxo.o \
152152
common/version.o \
153153
common/wallet.o \
154+
common/whitelisted_peer.o \
154155
common/wire_error.o \
155156
common/wireaddr.o \
156157
db/bindings.o \

0 commit comments

Comments
 (0)