From 32000b666032f2ee9ffc01ebdfb29fc7cac8f67a Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 4 Dec 2020 11:24:14 +0100 Subject: [PATCH] json: Add two param functions to parse string arrs and outpoint arrs In a couple of places we accept arrays of strings and don't validate them. If we forward them, e.g., call a JSON-RPC method from the plugin, we end up embedding the unverified string in the JSON-RPC call without escaping, which then leads to invalid JSON being passed on. This at least partially causes #4238 --- bitcoin/tx.h | 6 ++++++ common/json_helpers.c | 25 +++++++++++++++++++++++++ common/json_helpers.h | 4 ++++ common/json_tok.c | 29 +++++++++++++++++++++++++++++ common/json_tok.h | 10 ++++++++++ common/test/run-param.c | 4 ++++ 6 files changed, 78 insertions(+) diff --git a/bitcoin/tx.h b/bitcoin/tx.h index c2926e273..c5b92fc13 100644 --- a/bitcoin/tx.h +++ b/bitcoin/tx.h @@ -21,6 +21,12 @@ struct wally_psbt; struct bitcoin_txid { struct sha256_double shad; }; + +struct bitcoin_outpoint { + struct bitcoin_txid txid; + u16 n; +}; + /* Define bitcoin_txid_eq */ STRUCTEQ_DEF(bitcoin_txid, 0, shad.sha.u); diff --git a/common/json_helpers.c b/common/json_helpers.c index c1bf03cc9..63bdc1543 100644 --- a/common/json_helpers.c +++ b/common/json_helpers.c @@ -93,6 +93,31 @@ bool json_to_txid(const char *buffer, const jsmntok_t *tok, tok->end - tok->start, txid); } +bool json_to_outpoint(const char *buffer, const jsmntok_t *tok, + struct bitcoin_outpoint *op) +{ + size_t len = tok->end - tok->start; + char str[len + 1]; + + if (len < 66) + return NULL; + + memcpy(str, buffer+tok->start, len); + str[len] = 0x00; + + if (str[64] != ':') + return NULL; + + if (!bitcoin_txid_from_hex(str, 64, &op->txid)) + return false; + + op->n = atoi(str + 65); + if (op->n < 0) + return false; + + return true; +} + bool json_to_channel_id(const char *buffer, const jsmntok_t *tok, struct channel_id *cid) { diff --git a/common/json_helpers.h b/common/json_helpers.h index ffb5f7f4e..13a2044ab 100644 --- a/common/json_helpers.h +++ b/common/json_helpers.h @@ -62,6 +62,10 @@ bool json_to_msat(const char *buffer, const jsmntok_t *tok, bool json_to_txid(const char *buffer, const jsmntok_t *tok, struct bitcoin_txid *txid); +/* Extract a bitcoin outpoint from this */ +bool json_to_outpoint(const char *buffer, const jsmntok_t *tok, + struct bitcoin_outpoint *op); + /* Extract a channel id from this */ bool json_to_channel_id(const char *buffer, const jsmntok_t *tok, struct channel_id *cid); diff --git a/common/json_tok.c b/common/json_tok.c index 2a5e5dbd4..7f3f13230 100644 --- a/common/json_tok.c +++ b/common/json_tok.c @@ -501,3 +501,32 @@ struct command_result *param_psbt(struct command *cmd, return command_fail_badparam(cmd, name, buffer, tok, "Expected a PSBT"); } + +struct command_result *param_outpoint_arr(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct bitcoin_outpoint **outpoints) +{ + size_t i; + const jsmntok_t *curr; + if (tok->type != JSMN_ARRAY) { + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Could not decode the outpoint array for %s: " + "\"%s\" is not a valid outpoint array.", + name, json_strdup(tmpctx, buffer, tok)); + } + + *outpoints = tal_arr(cmd, struct bitcoin_outpoint, tok->size); + + json_for_each_arr(i, curr, tok) { + struct bitcoin_outpoint op; + if (!json_to_outpoint(buffer, curr, &op)) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Could not decode outpoint \"%.*s\", " + "expected format: txid:output", + json_tok_full_len(curr), json_tok_full(buffer, curr)); + (*outpoints)[i] = op; + } + return NULL; +} diff --git a/common/json_tok.h b/common/json_tok.h index 1b918ccd1..2fe336913 100644 --- a/common/json_tok.h +++ b/common/json_tok.h @@ -11,6 +11,7 @@ struct amount_msat; struct amount_sat; struct bitcoin_txid; +struct bitcoin_outpoint; struct channel_id; struct command; struct command_result; @@ -172,4 +173,13 @@ struct command_result *param_psbt(struct command *cmd, const char *buffer, const jsmntok_t *tok, struct wally_psbt **psbt); + +/** + * Parse a list of `txid:output` outpoints. + */ +struct command_result *param_outpoint_arr(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct bitcoin_outpoint **outpoints); #endif /* LIGHTNING_COMMON_JSON_TOK_H */ diff --git a/common/test/run-param.c b/common/test/run-param.c index 38399e13e..b3dfc79f4 100644 --- a/common/test/run-param.c +++ b/common/test/run-param.c @@ -52,6 +52,10 @@ bool json_to_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEED bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "json_to_node_id called!\n"); abort(); } +/* Generated stub for json_to_outpoint */ +bool json_to_outpoint(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct bitcoin_outpoint *op UNNEEDED) +{ fprintf(stderr, "json_to_outpoint called!\n"); abort(); } /* Generated stub for json_to_pubkey */ bool json_to_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct pubkey *pubkey UNNEEDED)