Received: by 2002:ac0:a5a6:0:0:0:0:0 with SMTP id m35-v6csp1777675imm; Fri, 7 Sep 2018 06:06:35 -0700 (PDT) X-Google-Smtp-Source: ANB0VdYDxbUxHdAyFb2h3SvLP3mAoMtr/L3LI+8cEe8Bs/BDNQZsjWffGL/wQ7v/YQkm8LYX37g8 X-Received: by 2002:a17:902:7145:: with SMTP id u5-v6mr7899725plm.259.1536325595394; Fri, 07 Sep 2018 06:06:35 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1536325595; cv=none; d=google.com; s=arc-20160816; b=zMueg73YD5sNMld6bckQEKoq9n7WmKUDG+LGLNI6IWlwH1hzpZXCKewS2L5LMA+kzQ KpwGXCJrOBP03PZ+HpOFadauGZl5l1ryxTPH7XOuCqkpRXqY29glHdoBwLHeyFgmwjPS BTcRTSdkmGdQ7w5o3Um32cVDFUZeNHyDGrVkDt4NMVspT8ly6B6z9d0DWAiArbsrGfEv 4iG0V9PhIJMxeoKhRM9+DxZkcvRL10R8X7JHMk9oI2Gg6+5yXwYnC0/7v4mHtJSOj6iG 0+6od5xgT3JzdQ2CovegeDcB0QnrxRslvWX+Fed6bNDvzktZOiv4eYn84uwGF5/V5edO qo1g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from; bh=XRD2wD3ZdsXPL58gLyf33xgoWPRNpafxfGRNuEciWfk=; b=drugcCvCD9AjUQwpFErHBBXqYHyx4D07+QzcLftS4rWq+RF0G8ziQJhRsiwo/JUwuW 2TsyU9kJEtyXSEu+tHFAZDTNOyFtBuEmZ38gO7gvwavjpz8sHMqTWv2PiuFkc5ABRBqV WqpM/K5VAZ3CAsA8S1sMnBZWsDgZGqti4a9H3W/faOwdqFWjwUOZRgRPPKAug1jwmnYp Kz7CMkVJhiRmcYbIziAC8IeKEwpvticsew7tcqq9KCHNzrfJhLGF44JLgafIIjZpsDXb BWQFqEwA38exWfwM46CK/KAqrf5wUH7hxK7W3pZ2ToK55eS0IPmZiD8+Ab0EoC13YeQw iG2g== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id f10-v6si8013799pfn.85.2018.09.07.06.06.20; Fri, 07 Sep 2018 06:06:35 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729288AbeIGRTd (ORCPT + 99 others); Fri, 7 Sep 2018 13:19:33 -0400 Received: from metis.ext.pengutronix.de ([85.220.165.71]:52905 "EHLO metis.ext.pengutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729252AbeIGRTc (ORCPT ); Fri, 7 Sep 2018 13:19:32 -0400 Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7]) by metis.ext.pengutronix.de with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.89) (envelope-from ) id 1fyG27-0007c2-PQ; Fri, 07 Sep 2018 14:38:43 +0200 Received: from sha by dude.hi.pengutronix.de with local (Exim 4.91) (envelope-from ) id 1fyG26-00062R-I1; Fri, 07 Sep 2018 14:38:42 +0200 From: Sascha Hauer To: linux-mtd@lists.infradead.org Cc: David Gstir , Richard Weinberger , kernel@pengutronix.de, linux-kernel@vger.kernel.org, Sascha Hauer Subject: [PATCH 17/25] ubifs: authenticate replayed journal Date: Fri, 7 Sep 2018 14:36:38 +0200 Message-Id: <20180907123646.12688-18-s.hauer@pengutronix.de> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20180907123646.12688-1-s.hauer@pengutronix.de> References: <20180907123646.12688-1-s.hauer@pengutronix.de> X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: sha@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-kernel@vger.kernel.org Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Make sure that during replay all buds can be authenticated. To do this we calculate the hash chain until we find an authentication node and check the HMAC in that node against the current status of the hash chain. After a power cut it can happen that some nodes have been written, but not yet the authentication node for them. These nodes have to be discarded during replay. Signed-off-by: Sascha Hauer --- fs/ubifs/replay.c | 146 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 2 deletions(-) diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c index db489d93439c..75f961c4c044 100644 --- a/fs/ubifs/replay.c +++ b/fs/ubifs/replay.c @@ -34,6 +34,8 @@ #include "ubifs.h" #include +#include +#include /** * struct replay_entry - replay list entry. @@ -531,6 +533,105 @@ static int is_last_bud(struct ubifs_info *c, struct ubifs_bud *bud) return data == 0xFFFFFFFF; } +/** + * authenticate_sleb - authenticate one scan LEB + * @c: UBIFS file-system description object + * @sleb: the scan LEB to authenticate + * @log_hash: + * @is_last: if true, this is is the last LEB + * + * This function iterates over the buds of a single LEB authenticating all buds + * with the authentication nodes on this LEB. Authentication nodes are written + * after some buds and contain a HMAC covering the authentication node itself + * and the buds between the last authentication node and the current + * authentication node. It can happen that the last buds cannot be authenticated + * because a powercut happened when some nodes were written but not the + * corresponding authentication node. This function returns the number of nodes + * that could be authenticated or a negative error code. + */ +static int authenticate_sleb(struct ubifs_info *c, struct ubifs_scan_leb *sleb, + struct shash_desc *log_hash, int is_last) +{ + int n_not_auth = 0; + struct ubifs_scan_node *snod; + int n_nodes = 0; + int err; + u8 *hash, *hmac; + + if (!ubifs_authenticated(c)) + return sleb->nodes_cnt; + + hash = kmalloc(crypto_shash_descsize(c->hash_tfm), GFP_NOFS); + hmac = kmalloc(c->hmac_desc_len, GFP_NOFS); + if (!hash || !hmac) { + err = -ENOMEM; + goto out; + } + + list_for_each_entry(snod, &sleb->nodes, list) { + + n_nodes++; + + if (snod->type == UBIFS_AUTH_NODE) { + struct ubifs_auth_node *auth = snod->node; + SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm); + SHASH_DESC_ON_STACK(hmac_desc, c->hmac_tfm); + + hash_desc->tfm = c->hash_tfm; + hash_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + + ubifs_shash_copy_state(c, log_hash, hash_desc); + err = crypto_shash_final(hash_desc, hash); + if (err) + goto out; + + hmac_desc->tfm = c->hmac_tfm; + hmac_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + err = crypto_shash_digest(hmac_desc, hash, c->hash_len, + hmac); + if (err) + goto out; + + err = ubifs_check_hmac(c, auth->hmac, hmac); + if (err) { + err = -EPERM; + goto out; + } + n_not_auth = 0; + } else { + err = crypto_shash_update(log_hash, snod->node, + snod->len); + if (err) + goto out; + n_not_auth++; + } + } + + /* + * A powercut can happen when some nodes were written, but not yet + * the corresponding authentication node. This may only happen on + * the last bud though. + */ + if (n_not_auth) { + if (is_last) { + dbg_mnt("%d unauthenticated nodes found on LEB %d, Ignoring them", + n_not_auth, sleb->lnum); + err = 0; + } else { + dbg_mnt("%d unauthenticated nodes found on non-last LEB %d", + n_not_auth, sleb->lnum); + err = -EPERM; + } + } else { + err = 0; + } +out: + kfree(hash); + kfree(hmac); + + return err ? err : n_nodes - n_not_auth; +} + /** * replay_bud - replay a bud logical eraseblock. * @c: UBIFS file-system description object @@ -544,6 +645,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b) { int is_last = is_last_bud(c, b->bud); int err = 0, used = 0, lnum = b->bud->lnum, offs = b->bud->start; + int n_nodes, n = 0; struct ubifs_scan_leb *sleb; struct ubifs_scan_node *snod; @@ -563,6 +665,15 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b) if (IS_ERR(sleb)) return PTR_ERR(sleb); + n_nodes = authenticate_sleb(c, sleb, b->bud->log_hash, is_last); + if (n_nodes < 0) { + err = n_nodes; + goto out; + } + + ubifs_shash_copy_state(c, b->bud->log_hash, + c->jheads[b->bud->jhead].log_hash); + /* * The bud does not have to start from offset zero - the beginning of * the 'lnum' LEB may contain previously committed data. One of the @@ -676,6 +787,10 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b) } if (err) goto out; + + n++; + if (n == n_nodes) + break; } ubifs_assert(c, ubifs_search_bud(c, lnum)); @@ -754,6 +869,7 @@ static int add_replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead, { struct ubifs_bud *bud; struct bud_entry *b; + int err; dbg_mnt("add replay bud LEB %d:%d, head %d", lnum, offs, jhead); @@ -763,13 +879,21 @@ static int add_replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead, b = kmalloc(sizeof(struct bud_entry), GFP_KERNEL); if (!b) { - kfree(bud); - return -ENOMEM; + err = -ENOMEM; + goto out; } bud->lnum = lnum; bud->start = offs; bud->jhead = jhead; + bud->log_hash = ubifs_hash_get_desc(c); + if (IS_ERR(bud->log_hash)) { + err = PTR_ERR(bud->log_hash); + goto out; + } + + ubifs_shash_copy_state(c, c->log_hash, bud->log_hash); + ubifs_add_bud(c, bud); b->bud = bud; @@ -777,6 +901,11 @@ static int add_replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead, list_add_tail(&b->list, &c->replay_buds); return 0; +out: + kfree(bud); + kfree(b); + + return err; } /** @@ -882,6 +1011,14 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf) c->cs_sqnum = le64_to_cpu(node->ch.sqnum); dbg_mnt("commit start sqnum %llu", c->cs_sqnum); + + err = ubifs_shash_init(c, c->log_hash); + if (err) + goto out; + + err = ubifs_shash_update(c, c->log_hash, node, UBIFS_CS_NODE_SZ); + if (err < 0) + goto out; } if (snod->sqnum < c->cs_sqnum) { @@ -929,6 +1066,11 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf) if (err) goto out_dump; + err = ubifs_shash_update(c, c->log_hash, ref, + UBIFS_REF_NODE_SZ); + if (err) + goto out; + err = add_replay_bud(c, le32_to_cpu(ref->lnum), le32_to_cpu(ref->offs), le32_to_cpu(ref->jhead), -- 2.18.0