[PATCH 1/1] ubifs: support authentication, for ro mount, when no key is given
Torben Hohn
torben.hohn at linutronix.de
Thu Jun 25 11:59:27 EDT 2020
Ubifs authentication requires a hmac key, even when a
filesystem is mounted read-only.
Split c->authenticated into _rw and _ro.
Also implement ubifs_init_authentication_read_only(),
which only allocates the structures needed for validating
the hashes. That is called, when no auth_key_name is specified,
only auth_hash_name. It sets c->authenticated_ro to true.
Then implement ubifs_authenticated_read() and
ubifs_authenticated_write(). And change all occurences of
ubifs_authenticated() to the respective read or write version.
ubifs_authenticated_write() verifies, that it is not called
when only c->authenticated_ro is active. (The WARN_ON should
probably be BUG_ON(), though)
Signed-off-by: Torben Hohn <torben.hohn at linutronix.de>
---
fs/ubifs/auth.c | 69 ++++++++++++++++++++++++++++++++++++++++++----
fs/ubifs/gc.c | 2 +-
fs/ubifs/journal.c | 12 ++++----
fs/ubifs/lpt.c | 4 +--
fs/ubifs/master.c | 2 +-
fs/ubifs/replay.c | 2 +-
fs/ubifs/sb.c | 16 +++++++----
fs/ubifs/super.c | 21 ++++++++++----
fs/ubifs/ubifs.h | 48 +++++++++++++++++++++-----------
9 files changed, 133 insertions(+), 43 deletions(-)
diff --git a/fs/ubifs/auth.c b/fs/ubifs/auth.c
index cc5c0abfd536..5429a05f537e 100644
--- a/fs/ubifs/auth.c
+++ b/fs/ubifs/auth.c
@@ -94,7 +94,7 @@ static struct shash_desc *ubifs_get_desc(const struct ubifs_info *c,
struct shash_desc *desc;
int err;
- if (!ubifs_authenticated(c))
+ if (!ubifs_authenticated_read(c))
return NULL;
desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);
@@ -248,6 +248,61 @@ int ubifs_sb_verify_signature(struct ubifs_info *c,
return err;
}
+/**
+ * ubifs_init_authentication_read_only - init only the read_only parts
+ *
+ * @c: UBIFS file-system description object
+ *
+ * This function returns 0 for success or a negative error code otherwise.
+ */
+
+int ubifs_init_authentication_read_only(struct ubifs_info *c)
+{
+ int err;
+
+ if (!c->auth_hash_name) {
+ ubifs_err(c, "authentication hash name needed with authentication");
+ return -EINVAL;
+ }
+
+ c->auth_hash_algo = match_string(hash_algo_name, HASH_ALGO__LAST,
+ c->auth_hash_name);
+ if ((int)c->auth_hash_algo < 0) {
+ ubifs_err(c, "Unknown hash algo %s specified",
+ c->auth_hash_name);
+ return -EINVAL;
+ }
+
+ c->hash_tfm = crypto_alloc_shash(c->auth_hash_name, 0, 0);
+ if (IS_ERR(c->hash_tfm)) {
+ err = PTR_ERR(c->hash_tfm);
+ ubifs_err(c, "Can not allocate %s: %d",
+ c->auth_hash_name, err);
+ goto out;
+ }
+
+ c->hash_len = crypto_shash_digestsize(c->hash_tfm);
+ if (c->hash_len > UBIFS_HASH_ARR_SZ) {
+ ubifs_err(c, "hash %s is bigger than maximum allowed hash size (%d > %d)",
+ c->auth_hash_name, c->hash_len, UBIFS_HASH_ARR_SZ);
+ err = -EINVAL;
+ goto out_free_hash;
+ }
+
+ c->authenticated_ro = true;
+
+ c->log_hash = ubifs_hash_get_desc(c);
+ if (IS_ERR(c->log_hash))
+ goto out_free_hash;
+
+ err = 0;
+out_free_hash:
+ if (err)
+ crypto_free_shash(c->hash_tfm);
+out:
+ return err;
+}
+
/**
* ubifs_init_authentication - initialize UBIFS authentication support
* @c: UBIFS file-system description object
@@ -335,7 +390,7 @@ int ubifs_init_authentication(struct ubifs_info *c)
if (err)
goto out_free_hmac;
- c->authenticated = true;
+ c->authenticated_rw = true;
c->log_hash = ubifs_hash_get_desc(c);
if (IS_ERR(c->log_hash))
@@ -364,11 +419,15 @@ int ubifs_init_authentication(struct ubifs_info *c)
*/
void __ubifs_exit_authentication(struct ubifs_info *c)
{
- if (!ubifs_authenticated(c))
+ if (!ubifs_authenticated_read(c))
return;
- crypto_free_shash(c->hmac_tfm);
crypto_free_shash(c->hash_tfm);
+
+ if (!c->authenticated_rw)
+ return;
+
+ crypto_free_shash(c->hmac_tfm);
kfree(c->log_hash);
}
@@ -511,7 +570,7 @@ int ubifs_hmac_wkm(struct ubifs_info *c, u8 *hmac)
int err;
const char well_known_message[] = "UBIFS";
- if (!ubifs_authenticated(c))
+ if (!ubifs_authenticated_write(c))
return 0;
shash->tfm = c->hmac_tfm;
diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c
index 62cb3db44e6e..8616e8ba97d6 100644
--- a/fs/ubifs/gc.c
+++ b/fs/ubifs/gc.c
@@ -410,7 +410,7 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb)
moved = 1;
}
- if (ubifs_authenticated(c) && moved) {
+ if (ubifs_authenticated_write(c) && moved) {
struct ubifs_auth_node *auth;
auth = kmalloc(ubifs_auth_node_sz(c), GFP_NOFS);
diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
index e5ec1afe1c66..da13e5eb93e1 100644
--- a/fs/ubifs/journal.c
+++ b/fs/ubifs/journal.c
@@ -80,7 +80,7 @@ static inline void zero_trun_node_unused(struct ubifs_trun_node *trun)
static void ubifs_add_auth_dirt(struct ubifs_info *c, int lnum)
{
- if (ubifs_authenticated(c))
+ if (ubifs_authenticated_write(c))
ubifs_add_dirt(c, lnum, ubifs_auth_node_sz(c));
}
@@ -278,7 +278,7 @@ static int write_head(struct ubifs_info *c, int jhead, void *buf, int len,
dbg_jnl("jhead %s, LEB %d:%d, len %d",
dbg_jhead(jhead), *lnum, *offs, len);
- if (ubifs_authenticated(c)) {
+ if (ubifs_authenticated_write(c)) {
err = ubifs_hash_nodes(c, buf, len, c->jheads[jhead].log_hash);
if (err)
return err;
@@ -572,7 +572,7 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
len = aligned_dlen + aligned_ilen + UBIFS_INO_NODE_SZ;
/* Make sure to also account for extended attributes */
- if (ubifs_authenticated(c))
+ if (ubifs_authenticated_write(c))
len += ALIGN(host_ui->data_len, 8) + ubifs_auth_node_sz(c);
else
len += host_ui->data_len;
@@ -778,7 +778,7 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
}
dlen = UBIFS_DATA_NODE_SZ + out_len;
- if (ubifs_authenticated(c))
+ if (ubifs_authenticated_write(c))
write_len = ALIGN(dlen, 8) + auth_len;
else
write_len = dlen;
@@ -860,7 +860,7 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
write_len += UBIFS_INO_NODE_SZ * ui->xattr_cnt;
}
- if (ubifs_authenticated(c))
+ if (ubifs_authenticated_write(c))
write_len += ALIGN(ilen, 8) + ubifs_auth_node_sz(c);
else
write_len += ilen;
@@ -1572,7 +1572,7 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
/* Must make reservation before allocating sequence numbers */
len = UBIFS_TRUN_NODE_SZ + UBIFS_INO_NODE_SZ;
- if (ubifs_authenticated(c))
+ if (ubifs_authenticated_write(c))
len += ALIGN(dlen, 8) + ubifs_auth_node_sz(c);
else
len += dlen;
diff --git a/fs/ubifs/lpt.c b/fs/ubifs/lpt.c
index e21abf250951..f3a7136518d1 100644
--- a/fs/ubifs/lpt.c
+++ b/fs/ubifs/lpt.c
@@ -1660,7 +1660,7 @@ int ubifs_lpt_calc_hash(struct ubifs_info *c, u8 *hash)
void *buf;
int err;
- if (!ubifs_authenticated(c))
+ if (!ubifs_authenticated_read(c))
return 0;
if (!c->nroot) {
@@ -1750,7 +1750,7 @@ static int lpt_check_hash(struct ubifs_info *c)
int err;
u8 hash[UBIFS_HASH_ARR_SZ];
- if (!ubifs_authenticated(c))
+ if (!ubifs_authenticated_read(c))
return 0;
err = ubifs_lpt_calc_hash(c, hash);
diff --git a/fs/ubifs/master.c b/fs/ubifs/master.c
index 911d0555b9f2..57304e3f7016 100644
--- a/fs/ubifs/master.c
+++ b/fs/ubifs/master.c
@@ -129,7 +129,7 @@ static int scan_for_master(struct ubifs_info *c)
c->mst_offs = offs;
ubifs_scan_destroy(sleb);
- if (!ubifs_authenticated(c))
+ if (!ubifs_authenticated_read(c))
return 0;
if (ubifs_hmac_zero(c, c->mst_node->hmac)) {
diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c
index b69ffac7e415..6c1b8739359c 100644
--- a/fs/ubifs/replay.c
+++ b/fs/ubifs/replay.c
@@ -595,7 +595,7 @@ static int authenticate_sleb(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
u8 hash[UBIFS_HASH_ARR_SZ];
u8 hmac[UBIFS_HMAC_ARR_SZ];
- if (!ubifs_authenticated(c))
+ if (!ubifs_authenticated_write(c))
return sleb->nodes_cnt;
list_for_each_entry(snod, &sleb->nodes, list) {
diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c
index 4b4b65b48c57..52396a92f8af 100644
--- a/fs/ubifs/sb.c
+++ b/fs/ubifs/sb.c
@@ -176,7 +176,7 @@ static int create_default_filesystem(struct ubifs_info *c)
sup_flags |= UBIFS_FLG_BIGLPT;
sup_flags |= UBIFS_FLG_DOUBLE_HASH;
- if (ubifs_authenticated(c)) {
+ if (ubifs_authenticated_write(c)) {
sup_flags |= UBIFS_FLG_AUTHENTICATION;
sup->hash_algo = cpu_to_le16(c->auth_hash_algo);
err = ubifs_hmac_wkm(c, sup->hmac_wkm);
@@ -542,20 +542,20 @@ static int authenticate_sb_node(struct ubifs_info *c,
int hash_algo;
int err;
- if (c->authenticated && !authenticated) {
+ if (ubifs_authenticated_read(c) && !authenticated) {
ubifs_err(c, "authenticated FS forced, but found FS without authentication");
return -EINVAL;
}
- if (!c->authenticated && authenticated) {
- ubifs_err(c, "authenticated FS found, but no key given");
+ if (!ubifs_authenticated_read(c) && authenticated) {
+ ubifs_err(c, "authenticated FS found, but no key nor hash name given");
return -EINVAL;
}
ubifs_msg(c, "Mounting in %sauthenticated mode",
- c->authenticated ? "" : "un");
+ c->authenticated_rw ? "RW " : (c->authenticated_ro ? "RO " :"un"));
- if (!c->authenticated)
+ if (!ubifs_authenticated_read(c))
return 0;
if (!IS_ENABLED(CONFIG_UBIFS_FS_AUTHENTICATION))
@@ -583,6 +583,10 @@ static int authenticate_sb_node(struct ubifs_info *c,
if (ubifs_hmac_zero(c, sup->hmac)) {
err = ubifs_sb_verify_signature(c, sup);
} else {
+ if (!ubifs_authenticated_write(c)) {
+ ubifs_err(c, "HMAC authenticated FS found, but no key given");
+ return -EINVAL;
+ }
err = ubifs_hmac_wkm(c, hmac_wkm);
if (err)
return err;
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 7fc2f3f07c16..ec95f1f50e5e 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -1291,6 +1291,17 @@ static int mount_ubifs(struct ubifs_info *c)
err = -EINVAL;
goto out_free;
}
+ } else if (c->auth_hash_name) {
+ if (IS_ENABLED(CONFIG_UBIFS_FS_AUTHENTICATION)) {
+ err = ubifs_init_authentication_read_only(c);
+ if (err)
+ goto out_free;
+ } else {
+ ubifs_err(c, "auth_hash_name, but UBIFS is built without"
+ " authentication support");
+ err = -EINVAL;
+ goto out_free;
+ }
}
err = ubifs_read_superblock(c);
@@ -1383,7 +1394,7 @@ static int mount_ubifs(struct ubifs_info *c)
* in the superblock, we can update the offline signed
* superblock with a HMAC version,
*/
- if (ubifs_authenticated(c) && ubifs_hmac_zero(c, c->sup_node->hmac)) {
+ if (c->authenticated_rw && ubifs_hmac_zero(c, c->sup_node->hmac)) {
err = ubifs_hmac_wkm(c, c->sup_node->hmac_wkm);
if (err)
goto out_lpt;
@@ -1430,7 +1441,7 @@ static int mount_ubifs(struct ubifs_info *c)
}
if (c->need_recovery) {
- if (!ubifs_authenticated(c)) {
+ if (!ubifs_authenticated_write(c)) {
err = ubifs_recover_size(c, true);
if (err)
goto out_orphans;
@@ -1440,7 +1451,7 @@ static int mount_ubifs(struct ubifs_info *c)
if (err)
goto out_orphans;
- if (ubifs_authenticated(c)) {
+ if (ubifs_authenticated_write(c)) {
err = ubifs_recover_size(c, false);
if (err)
goto out_orphans;
@@ -1686,7 +1697,7 @@ static int ubifs_remount_rw(struct ubifs_info *c)
err = ubifs_write_rcvrd_mst_node(c);
if (err)
goto out;
- if (!ubifs_authenticated(c)) {
+ if (!ubifs_authenticated_write(c)) {
err = ubifs_recover_size(c, true);
if (err)
goto out;
@@ -1771,7 +1782,7 @@ static int ubifs_remount_rw(struct ubifs_info *c)
if (err)
goto out;
- if (ubifs_authenticated(c)) {
+ if (ubifs_authenticated_write(c)) {
err = ubifs_recover_size(c, false);
if (err)
goto out;
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index bff682309fbe..0789c859a148 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1040,7 +1040,8 @@ struct ubifs_debug_info;
* @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc)
* @rw_incompat: the media is not R/W compatible
* @assert_action: action to take when a ubifs_assert() fails
- * @authenticated: flag indigating the FS is mounted in authenticated mode
+ * @authenticated_rw: flag indigating the FS is mounted in authenticated mode
+ * @authenticated_ro: flag indigating the FS is mounted in read-only authenticated mode (without HMAC)
*
* @tnc_mutex: protects the Tree Node Cache (TNC), @zroot, @cnext, @enext, and
* @calc_idx_sz
@@ -1293,7 +1294,8 @@ struct ubifs_info {
unsigned int default_compr:2;
unsigned int rw_incompat:1;
unsigned int assert_action:2;
- unsigned int authenticated:1;
+ unsigned int authenticated_rw:1;
+ unsigned int authenticated_ro:1;
unsigned int superblock_need_write:1;
struct mutex tnc_mutex;
@@ -1506,21 +1508,34 @@ extern const struct inode_operations ubifs_symlink_inode_operations;
extern struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT];
/* auth.c */
-static inline int ubifs_authenticated(const struct ubifs_info *c)
+static inline int ubifs_authenticated_read(const struct ubifs_info *c)
{
- return (IS_ENABLED(CONFIG_UBIFS_FS_AUTHENTICATION)) && c->authenticated;
+ return (IS_ENABLED(CONFIG_UBIFS_FS_AUTHENTICATION)) && (c->authenticated_rw || c->authenticated_ro);
+}
+
+static inline int ubifs_authenticated_write(const struct ubifs_info *c)
+{
+ /*
+ * Lets make sure, that ubifs does not try to write
+ * when authenticate_ro is active.
+ *
+ * Because this will certeinly be an error.
+ */
+ WARN_ON(c->authenticated_ro && !c->authenticated_rw);
+
+ return (IS_ENABLED(CONFIG_UBIFS_FS_AUTHENTICATION)) && c->authenticated_rw;
}
struct shash_desc *__ubifs_hash_get_desc(const struct ubifs_info *c);
static inline struct shash_desc *ubifs_hash_get_desc(const struct ubifs_info *c)
{
- return ubifs_authenticated(c) ? __ubifs_hash_get_desc(c) : NULL;
+ return ubifs_authenticated_read(c) ? __ubifs_hash_get_desc(c) : NULL;
}
static inline int ubifs_shash_init(const struct ubifs_info *c,
struct shash_desc *desc)
{
- if (ubifs_authenticated(c))
+ if (ubifs_authenticated_read(c))
return crypto_shash_init(desc);
else
return 0;
@@ -1532,7 +1547,7 @@ static inline int ubifs_shash_update(const struct ubifs_info *c,
{
int err = 0;
- if (ubifs_authenticated(c)) {
+ if (ubifs_authenticated_read(c)) {
err = crypto_shash_update(desc, buf, len);
if (err < 0)
return err;
@@ -1544,7 +1559,7 @@ static inline int ubifs_shash_update(const struct ubifs_info *c,
static inline int ubifs_shash_final(const struct ubifs_info *c,
struct shash_desc *desc, u8 *out)
{
- return ubifs_authenticated(c) ? crypto_shash_final(desc, out) : 0;
+ return ubifs_authenticated_read(c) ? crypto_shash_final(desc, out) : 0;
}
int __ubifs_node_calc_hash(const struct ubifs_info *c, const void *buf,
@@ -1552,7 +1567,7 @@ int __ubifs_node_calc_hash(const struct ubifs_info *c, const void *buf,
static inline int ubifs_node_calc_hash(const struct ubifs_info *c,
const void *buf, u8 *hash)
{
- if (ubifs_authenticated(c))
+ if (ubifs_authenticated_read(c))
return __ubifs_node_calc_hash(c, buf, hash);
else
return 0;
@@ -1599,17 +1614,18 @@ int __ubifs_node_check_hash(const struct ubifs_info *c, const void *buf,
static inline int ubifs_node_check_hash(const struct ubifs_info *c,
const void *buf, const u8 *expected)
{
- if (ubifs_authenticated(c))
+ if (ubifs_authenticated_read(c))
return __ubifs_node_check_hash(c, buf, expected);
else
return 0;
}
+int ubifs_init_authentication_read_only(struct ubifs_info *c);
int ubifs_init_authentication(struct ubifs_info *c);
void __ubifs_exit_authentication(struct ubifs_info *c);
static inline void ubifs_exit_authentication(struct ubifs_info *c)
{
- if (ubifs_authenticated(c))
+ if (ubifs_authenticated_read(c))
__ubifs_exit_authentication(c);
}
@@ -1638,7 +1654,7 @@ static inline u8 *ubifs_branch_hash(struct ubifs_info *c,
static inline void ubifs_copy_hash(const struct ubifs_info *c, const u8 *from,
u8 *to)
{
- if (ubifs_authenticated(c))
+ if (ubifs_authenticated_read(c))
memcpy(to, from, c->hash_len);
}
@@ -1647,7 +1663,7 @@ int __ubifs_node_insert_hmac(const struct ubifs_info *c, void *buf,
static inline int ubifs_node_insert_hmac(const struct ubifs_info *c, void *buf,
int len, int ofs_hmac)
{
- if (ubifs_authenticated(c))
+ if (ubifs_authenticated_write(c))
return __ubifs_node_insert_hmac(c, buf, len, ofs_hmac);
else
return 0;
@@ -1658,7 +1674,7 @@ int __ubifs_node_verify_hmac(const struct ubifs_info *c, const void *buf,
static inline int ubifs_node_verify_hmac(const struct ubifs_info *c,
const void *buf, int len, int ofs_hmac)
{
- if (ubifs_authenticated(c))
+ if (ubifs_authenticated_write(c))
return __ubifs_node_verify_hmac(c, buf, len, ofs_hmac);
else
return 0;
@@ -1674,7 +1690,7 @@ static inline int ubifs_node_verify_hmac(const struct ubifs_info *c,
*/
static inline int ubifs_auth_node_sz(const struct ubifs_info *c)
{
- if (ubifs_authenticated(c))
+ if (ubifs_authenticated_write(c))
return sizeof(struct ubifs_auth_node) + c->hmac_desc_len;
else
return 0;
@@ -1691,7 +1707,7 @@ static inline int ubifs_shash_copy_state(const struct ubifs_info *c,
struct shash_desc *src,
struct shash_desc *target)
{
- if (ubifs_authenticated(c))
+ if (ubifs_authenticated_write(c))
return __ubifs_shash_copy_state(c, src, target);
else
return 0;
--
2.20.1
More information about the linux-mtd
mailing list