Skip to content
1 change: 1 addition & 0 deletions common/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ COMMON_SRC_NOGEN := \
common/utxo.c \
common/version.c \
common/wallet.c \
common/whitelisted_peer.c \
common/wireaddr.c \
common/wire_error.c

Expand Down
68 changes: 68 additions & 0 deletions common/whitelisted_peer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#include "config.h"
#include <common/whitelisted_peer.h>
#include <connectd/connectd.h>
#include <wire/wire.h>

static void destroy_whitelisted_peer(struct whitelisted_peer *peer)
{
if (peer->my_alt_addrs)
tal_free(peer->my_alt_addrs);
tal_free(peer);
}

void populate_whitelist_table(struct daemon *daemon,
struct whitelisted_peer *wp)
{
if (wp) {
size_t num_whitelisted = tal_count(wp);
for (size_t i = 0; i < num_whitelisted; i++) {
if (whitelisted_peer_htable_get(daemon->whitelisted_peer_htable,
&wp[i].id)) {
tal_free(&wp[i]);
continue;
}

struct whitelisted_peer *new_peer = tal_steal(daemon,
&wp[i]);

whitelisted_peer_htable_add(daemon->whitelisted_peer_htable,
new_peer);

tal_add_destructor(new_peer, destroy_whitelisted_peer);
}
}
}

void towire_whitelisted_peer(uint8_t **p, const struct whitelisted_peer *wp)
{
/* Serialize the node_id */
towire_node_id(p, &wp->id);

/* Serialize the num of addrs */
uint16_t num_alt_addrs = tal_count(wp->my_alt_addrs);
towire_u16(p, num_alt_addrs);

/* Serialize each alternate address */
for (size_t i = 0; i < num_alt_addrs; i++)
towire_wireaddr_internal(p, &wp->my_alt_addrs[i]);
}

bool fromwire_whitelisted_peer(const uint8_t **cursor,
size_t *plen,
struct whitelisted_peer *wp)
{
/* Deserialize the node_id */
fromwire_node_id(cursor, plen, &wp->id);

/* Deserialize the number of alternate addresses */
uint16_t num_alt_addrs = fromwire_u16(cursor, plen);

wp->my_alt_addrs = tal_arr(wp, struct wireaddr_internal, num_alt_addrs);

/* Deserialize each alternate address */
for (size_t i = 0; i < num_alt_addrs; i++)
if (!fromwire_wireaddr_internal(cursor, plen, &wp->my_alt_addrs[i]))
return false;

return *cursor != NULL;
}
29 changes: 29 additions & 0 deletions common/whitelisted_peer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef LIGHTNING_COMMON_WHITELISTED_PEER_H
#define LIGHTNING_COMMON_WHITELISTED_PEER_H
#include "config.h"
#include <ccan/htable/htable_type.h>
#include <ccan/tal/tal.h>
#include <common/node_id.h>
#include <common/wireaddr.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

struct daemon;

struct whitelisted_peer {
struct node_id id;
struct wireaddr_internal *my_alt_addrs;
};

/* Function for populating the hashtable */
void populate_whitelist_table(struct daemon *daemon,
struct whitelisted_peer *peers);

void towire_whitelisted_peer(uint8_t **p,
const struct whitelisted_peer *whitelisted_peer);

bool fromwire_whitelisted_peer(const uint8_t **cursor, size_t *plen,
struct whitelisted_peer *whitelisted_peer);

#endif /* LIGHTNING_COMMON_WHITELISTED_PEER_H */
1 change: 1 addition & 0 deletions connectd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ CONNECTD_COMMON_OBJS := \
common/utils.o \
common/utxo.o \
common/version.o \
common/whitelisted_peer.o \
common/wireaddr.o \
common/wire_error.o \
gossipd/gossipd_wiregen.o \
Expand Down
100 changes: 99 additions & 1 deletion connectd/connectd.c
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,77 @@ struct io_plan *peer_connected(struct io_conn *conn,
return multiplex_peer_setup(conn, peer);
}

static bool verify_alt_addr(struct io_conn *conn,
struct daemon *daemon,
const struct node_id *id)
{
struct sockaddr_in local_addr;
socklen_t addr_len = sizeof(local_addr);

/* Get local address and port */
if (getsockname(io_conn_fd(conn), (struct sockaddr *)&local_addr,
&addr_len) == -1) {
status_broken("verify_alt_addr: getsockname failed");
return false;
}

char listening_addr[INET_ADDRSTRLEN];
if (!inet_ntop(AF_INET, &local_addr.sin_addr, listening_addr,
sizeof(listening_addr))) {
status_broken("verify_alt_addr: inet_ntop failed");
return false;
}
int listening_port = ntohs(local_addr.sin_port);

char full_listening_addr[INET_ADDRSTRLEN + 6];
snprintf(full_listening_addr, sizeof(full_listening_addr), "%s:%d",
listening_addr, listening_port);

struct wireaddr_internal search_addr;
if (parse_wireaddr_internal(tmpctx, full_listening_addr, 0,
false, &search_addr) != NULL) {
status_broken("verify_alt_addr: parse_wireaddr_internal failed");
return false;
}

struct whitelisted_peer *wp = whitelisted_peer_htable_get(daemon->whitelisted_peer_htable,
id);
bool is_whitelisted = false;

if (wp) {
size_t num_addrs = tal_count(wp->my_alt_addrs);
for (size_t i = 0; i < num_addrs; ++i) {
char *whitelist_addr_str = fmt_wireaddr_internal(tmpctx,
&wp->my_alt_addrs[i]);
if (strcmp(full_listening_addr, whitelist_addr_str) == 0) {
is_whitelisted = true;
status_debug("Peer's address %s is in the whitelist. Accepting connection.",
full_listening_addr);
goto check_alt_bind_addr;
}
}
}

check_alt_bind_addr:
/* Check against alt_bind_addr only if the connection is not whitelisted */
if (!is_whitelisted) {
char *alt_bind_addrs = tal_strdup(tmpctx,
(const char *)daemon->alt_bind_addr);
for (char *alt_bind_token = strtok(alt_bind_addrs, ",");
alt_bind_token;
alt_bind_token = strtok(NULL, ",")) {
if (strcmp(full_listening_addr, alt_bind_token) == 0) {
status_unusual("Connection attempt from address %s which is not in the whitelist. Closing connection.",
full_listening_addr);
tal_free(alt_bind_addrs);
return false;
}
}
tal_free(alt_bind_addrs);
}
return true;
}

/*~ handshake.c's handles setting up the crypto state once we get a connection
* in; we hand it straight to peer_exchange_initmsg() to send and receive INIT
* and call peer_connected(). */
Expand All @@ -327,6 +398,12 @@ static struct io_plan *handshake_in_success(struct io_conn *conn,
struct node_id id;
node_id_from_pubkey(&id, id_key);
status_peer_debug(&id, "Connect IN");

/* Confirm that peer connects to the alt-bind-addr you sent */
if (daemon->alt_bind_addr)
if (!verify_alt_addr(conn, daemon, &id))
return (io_close(conn));

return peer_exchange_initmsg(conn, daemon, daemon->our_features,
cs, &id, addr, timeout, is_websocket, true);
}
Expand Down Expand Up @@ -1438,7 +1515,8 @@ static void connect_init(struct daemon *daemon, const u8 *msg)
&dev_disconnect,
&daemon->dev_no_ping_timer,
&daemon->dev_handshake_no_reply,
&dev_throttle_gossip)) {
&dev_throttle_gossip,
&daemon->alt_bind_addr)) {
/* This is a helper which prints the type expected and the actual
* message, then exits (it should never be called!). */
master_badmsg(WIRE_CONNECTD_INIT, msg);
Expand Down Expand Up @@ -1925,6 +2003,7 @@ static void dev_connect_memleak(struct daemon *daemon, const u8 *msg)
memleak_scan_obj(memtable, daemon);
memleak_scan_htable(memtable, &daemon->peers->raw);
memleak_scan_htable(memtable, &daemon->scid_htable->raw);
memleak_scan_htable(memtable, &daemon->whitelisted_peer_htable->raw);

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

static void handle_alt_addr_whitelist(struct daemon *daemon, const u8 *msg)
{
struct whitelisted_peer *received_peers;

if (!fromwire_connectd_alt_addr_whitelist(daemon, msg, &received_peers)) {
master_badmsg(WIRE_CONNECTD_ALT_ADDR_WHITELIST, msg);
return;
}

populate_whitelist_table(daemon, received_peers);
}

static struct io_plan *recv_peer_connect_subd(struct io_conn *conn,
const u8 *msg,
int fd,
Expand Down Expand Up @@ -2262,6 +2353,10 @@ static struct io_plan *recv_req(struct io_conn *conn,
goto out;
}
/* Fall thru */
case WIRE_CONNECTD_ALT_ADDR_WHITELIST:
handle_alt_addr_whitelist(daemon, msg);
goto out;
/* Fall thru */
/* We send these, we don't receive them */
case WIRE_CONNECTD_INIT_REPLY:
case WIRE_CONNECTD_ACTIVATE_REPLY:
Expand Down Expand Up @@ -2363,6 +2458,9 @@ int main(int argc, char *argv[])
daemon->gossip_stream_limit = 1000000;
daemon->scid_htable = tal(daemon, struct scid_htable);
scid_htable_init(daemon->scid_htable);
daemon->whitelisted_peer_htable = tal(daemon,
struct whitelisted_peer_htable);
whitelisted_peer_htable_init(daemon->whitelisted_peer_htable);

/* stdin == control */
daemon->master = daemon_conn_new(daemon, STDIN_FILENO, recv_req, NULL,
Expand Down
24 changes: 24 additions & 0 deletions connectd/connectd.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <common/crypto_state.h>
#include <common/node_id.h>
#include <common/pseudorand.h>
#include <common/whitelisted_peer.h>
#include <common/wireaddr.h>
#include <connectd/handshake.h>

Expand Down Expand Up @@ -226,6 +227,24 @@ HTABLE_DEFINE_TYPE(struct scid_to_node_id,
scid_to_node_id_eq_scid,
scid_htable);

static const struct node_id *whitelisted_peer_keyof(const struct whitelisted_peer *wp)
{
return &wp->id;
}

static bool whitelisted_peer_eq(const struct whitelisted_peer *wp, const struct node_id *id)
{
return node_id_eq(&wp->id, id);
}

/*~ This defines 'struct witelisted_peer_htable' which contains
* 'struct whitelisted_peer' pointers. */
HTABLE_DEFINE_TYPE(struct whitelisted_peer,
whitelisted_peer_keyof,
node_id_hash,
whitelisted_peer_eq,
whitelisted_peer_htable);

/*~ This is the global state, like `struct lightningd *ld` in lightningd. */
struct daemon {
/* Who am I? */
Expand Down Expand Up @@ -289,6 +308,11 @@ struct daemon {
/* Our features, as lightningd told us */
struct feature_set *our_features;

/* check whitelist before accepting incoming alt addr */
struct whitelisted_peer_htable *whitelisted_peer_htable;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can simply be a set of node ids (i.e a htable of node_ids).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the previous answer does explain why we need more then only the node_id

/* Bind for 'alt-addr' rpc cmd, or '--alt-annouce-addr' listening, but do not announce */
u8 *alt_bind_addr;

/* Subdaemon to proxy websocket requests. */
char *websocket_helper;

Expand Down
8 changes: 8 additions & 0 deletions connectd/connectd_wire.csv
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <common/cryptomsg.h>
#include <common/features.h>
#include <common/node_id.h>
#include <common/whitelisted_peer.h>
#include <common/wireaddr.h>
#include <wire/onion_wire.h>

Expand All @@ -28,6 +29,9 @@ msgdata,connectd_init,dev_no_ping_timer,bool,
# Allow incoming connections, but don't talk.
msgdata,connectd_init,dev_noreply,bool,
msgdata,connectd_init,dev_throttle_gossip,bool,
# Whitelist for incoming alternative address connections
msgdata,connectd_init,alt_bind_addr_len,u8,
msgdata,connectd_init,alt_bind_addr,byte,alt_bind_addr_len,

# Connectd->master, here are the addresses I bound, can announce.
msgtype,connectd_init_reply,2100
Expand Down Expand Up @@ -194,3 +198,7 @@ msgtype,connectd_peer_alt_addr,2037
msgdata,connectd_peer_alt_addr,id,node_id,
msgdata,connectd_peer_alt_addr,addr_len,u8,
msgdata,connectd_peer_alt_addr,addr,byte,addr_len,

# master -> connectd: alternative connection whitelist
msgtype,connectd_alt_addr_whitelist,2138
msgdata,connectd_alt_addr_whitelist,whitelisted_peer,?whitelisted_peer,
1 change: 1 addition & 0 deletions lightningd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ LIGHTNINGD_COMMON_OBJS := \
common/utxo.o \
common/version.o \
common/wallet.o \
common/whitelisted_peer.o \
common/wire_error.o \
common/wireaddr.o \
db/bindings.o \
Expand Down
Loading