Skip to content

Commit da8ef65

Browse files
saschahauerrichardweinberger
authored andcommitted
ubifs: Authenticate replayed journal
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 <s.hauer@pengutronix.de> Signed-off-by: Richard Weinberger <richard@nod.at>
1 parent 6f06d96 commit da8ef65

File tree

1 file changed

+144
-2
lines changed

1 file changed

+144
-2
lines changed

fs/ubifs/replay.c

Lines changed: 144 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434

3535
#include "ubifs.h"
3636
#include <linux/list_sort.h>
37+
#include <crypto/hash.h>
38+
#include <crypto/algapi.h>
3739

3840
/**
3941
* struct replay_entry - replay list entry.
@@ -531,6 +533,105 @@ static int is_last_bud(struct ubifs_info *c, struct ubifs_bud *bud)
531533
return data == 0xFFFFFFFF;
532534
}
533535

536+
/**
537+
* authenticate_sleb - authenticate one scan LEB
538+
* @c: UBIFS file-system description object
539+
* @sleb: the scan LEB to authenticate
540+
* @log_hash:
541+
* @is_last: if true, this is is the last LEB
542+
*
543+
* This function iterates over the buds of a single LEB authenticating all buds
544+
* with the authentication nodes on this LEB. Authentication nodes are written
545+
* after some buds and contain a HMAC covering the authentication node itself
546+
* and the buds between the last authentication node and the current
547+
* authentication node. It can happen that the last buds cannot be authenticated
548+
* because a powercut happened when some nodes were written but not the
549+
* corresponding authentication node. This function returns the number of nodes
550+
* that could be authenticated or a negative error code.
551+
*/
552+
static int authenticate_sleb(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
553+
struct shash_desc *log_hash, int is_last)
554+
{
555+
int n_not_auth = 0;
556+
struct ubifs_scan_node *snod;
557+
int n_nodes = 0;
558+
int err;
559+
u8 *hash, *hmac;
560+
561+
if (!ubifs_authenticated(c))
562+
return sleb->nodes_cnt;
563+
564+
hash = kmalloc(crypto_shash_descsize(c->hash_tfm), GFP_NOFS);
565+
hmac = kmalloc(c->hmac_desc_len, GFP_NOFS);
566+
if (!hash || !hmac) {
567+
err = -ENOMEM;
568+
goto out;
569+
}
570+
571+
list_for_each_entry(snod, &sleb->nodes, list) {
572+
573+
n_nodes++;
574+
575+
if (snod->type == UBIFS_AUTH_NODE) {
576+
struct ubifs_auth_node *auth = snod->node;
577+
SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm);
578+
SHASH_DESC_ON_STACK(hmac_desc, c->hmac_tfm);
579+
580+
hash_desc->tfm = c->hash_tfm;
581+
hash_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
582+
583+
ubifs_shash_copy_state(c, log_hash, hash_desc);
584+
err = crypto_shash_final(hash_desc, hash);
585+
if (err)
586+
goto out;
587+
588+
hmac_desc->tfm = c->hmac_tfm;
589+
hmac_desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
590+
err = crypto_shash_digest(hmac_desc, hash, c->hash_len,
591+
hmac);
592+
if (err)
593+
goto out;
594+
595+
err = ubifs_check_hmac(c, auth->hmac, hmac);
596+
if (err) {
597+
err = -EPERM;
598+
goto out;
599+
}
600+
n_not_auth = 0;
601+
} else {
602+
err = crypto_shash_update(log_hash, snod->node,
603+
snod->len);
604+
if (err)
605+
goto out;
606+
n_not_auth++;
607+
}
608+
}
609+
610+
/*
611+
* A powercut can happen when some nodes were written, but not yet
612+
* the corresponding authentication node. This may only happen on
613+
* the last bud though.
614+
*/
615+
if (n_not_auth) {
616+
if (is_last) {
617+
dbg_mnt("%d unauthenticated nodes found on LEB %d, Ignoring them",
618+
n_not_auth, sleb->lnum);
619+
err = 0;
620+
} else {
621+
dbg_mnt("%d unauthenticated nodes found on non-last LEB %d",
622+
n_not_auth, sleb->lnum);
623+
err = -EPERM;
624+
}
625+
} else {
626+
err = 0;
627+
}
628+
out:
629+
kfree(hash);
630+
kfree(hmac);
631+
632+
return err ? err : n_nodes - n_not_auth;
633+
}
634+
534635
/**
535636
* replay_bud - replay a bud logical eraseblock.
536637
* @c: UBIFS file-system description object
@@ -544,6 +645,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
544645
{
545646
int is_last = is_last_bud(c, b->bud);
546647
int err = 0, used = 0, lnum = b->bud->lnum, offs = b->bud->start;
648+
int n_nodes, n = 0;
547649
struct ubifs_scan_leb *sleb;
548650
struct ubifs_scan_node *snod;
549651

@@ -563,6 +665,15 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b)
563665
if (IS_ERR(sleb))
564666
return PTR_ERR(sleb);
565667

668+
n_nodes = authenticate_sleb(c, sleb, b->bud->log_hash, is_last);
669+
if (n_nodes < 0) {
670+
err = n_nodes;
671+
goto out;
672+
}
673+
674+
ubifs_shash_copy_state(c, b->bud->log_hash,
675+
c->jheads[b->bud->jhead].log_hash);
676+
566677
/*
567678
* The bud does not have to start from offset zero - the beginning of
568679
* 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)
676787
}
677788
if (err)
678789
goto out;
790+
791+
n++;
792+
if (n == n_nodes)
793+
break;
679794
}
680795

681796
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,
754869
{
755870
struct ubifs_bud *bud;
756871
struct bud_entry *b;
872+
int err;
757873

758874
dbg_mnt("add replay bud LEB %d:%d, head %d", lnum, offs, jhead);
759875

@@ -763,20 +879,33 @@ static int add_replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead,
763879

764880
b = kmalloc(sizeof(struct bud_entry), GFP_KERNEL);
765881
if (!b) {
766-
kfree(bud);
767-
return -ENOMEM;
882+
err = -ENOMEM;
883+
goto out;
768884
}
769885

770886
bud->lnum = lnum;
771887
bud->start = offs;
772888
bud->jhead = jhead;
889+
bud->log_hash = ubifs_hash_get_desc(c);
890+
if (IS_ERR(bud->log_hash)) {
891+
err = PTR_ERR(bud->log_hash);
892+
goto out;
893+
}
894+
895+
ubifs_shash_copy_state(c, c->log_hash, bud->log_hash);
896+
773897
ubifs_add_bud(c, bud);
774898

775899
b->bud = bud;
776900
b->sqnum = sqnum;
777901
list_add_tail(&b->list, &c->replay_buds);
778902

779903
return 0;
904+
out:
905+
kfree(bud);
906+
kfree(b);
907+
908+
return err;
780909
}
781910

782911
/**
@@ -882,6 +1011,14 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf)
8821011

8831012
c->cs_sqnum = le64_to_cpu(node->ch.sqnum);
8841013
dbg_mnt("commit start sqnum %llu", c->cs_sqnum);
1014+
1015+
err = ubifs_shash_init(c, c->log_hash);
1016+
if (err)
1017+
goto out;
1018+
1019+
err = ubifs_shash_update(c, c->log_hash, node, UBIFS_CS_NODE_SZ);
1020+
if (err < 0)
1021+
goto out;
8851022
}
8861023

8871024
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)
9291066
if (err)
9301067
goto out_dump;
9311068

1069+
err = ubifs_shash_update(c, c->log_hash, ref,
1070+
UBIFS_REF_NODE_SZ);
1071+
if (err)
1072+
goto out;
1073+
9321074
err = add_replay_bud(c, le32_to_cpu(ref->lnum),
9331075
le32_to_cpu(ref->offs),
9341076
le32_to_cpu(ref->jhead),

0 commit comments

Comments
 (0)