| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * This file is part of UBIFS. |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2006-2008 Nokia Corporation. |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 7 | | - * under the terms of the GNU General Public License version 2 as published by |
|---|
| 8 | | - * the Free Software Foundation. |
|---|
| 9 | | - * |
|---|
| 10 | | - * This program is distributed in the hope that it will be useful, but WITHOUT |
|---|
| 11 | | - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|---|
| 12 | | - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
|---|
| 13 | | - * more details. |
|---|
| 14 | | - * |
|---|
| 15 | | - * You should have received a copy of the GNU General Public License along with |
|---|
| 16 | | - * this program; if not, write to the Free Software Foundation, Inc., 51 |
|---|
| 17 | | - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|---|
| 18 | 6 | * |
|---|
| 19 | 7 | * Authors: Artem Bityutskiy (Битюцкий Артём) |
|---|
| 20 | 8 | * Adrian Hunter |
|---|
| .. | .. |
|---|
| 23 | 11 | /* This file implements reading and writing the master node */ |
|---|
| 24 | 12 | |
|---|
| 25 | 13 | #include "ubifs.h" |
|---|
| 14 | + |
|---|
| 15 | +/** |
|---|
| 16 | + * ubifs_compare_master_node - compare two UBIFS master nodes |
|---|
| 17 | + * @c: UBIFS file-system description object |
|---|
| 18 | + * @m1: the first node |
|---|
| 19 | + * @m2: the second node |
|---|
| 20 | + * |
|---|
| 21 | + * This function compares two UBIFS master nodes. Returns 0 if they are equal |
|---|
| 22 | + * and nonzero if not. |
|---|
| 23 | + */ |
|---|
| 24 | +int ubifs_compare_master_node(struct ubifs_info *c, void *m1, void *m2) |
|---|
| 25 | +{ |
|---|
| 26 | + int ret; |
|---|
| 27 | + int behind; |
|---|
| 28 | + int hmac_offs = offsetof(struct ubifs_mst_node, hmac); |
|---|
| 29 | + |
|---|
| 30 | + /* |
|---|
| 31 | + * Do not compare the common node header since the sequence number and |
|---|
| 32 | + * hence the CRC are different. |
|---|
| 33 | + */ |
|---|
| 34 | + ret = memcmp(m1 + UBIFS_CH_SZ, m2 + UBIFS_CH_SZ, |
|---|
| 35 | + hmac_offs - UBIFS_CH_SZ); |
|---|
| 36 | + if (ret) |
|---|
| 37 | + return ret; |
|---|
| 38 | + |
|---|
| 39 | + /* |
|---|
| 40 | + * Do not compare the embedded HMAC aswell which also must be different |
|---|
| 41 | + * due to the different common node header. |
|---|
| 42 | + */ |
|---|
| 43 | + behind = hmac_offs + UBIFS_MAX_HMAC_LEN; |
|---|
| 44 | + |
|---|
| 45 | + if (UBIFS_MST_NODE_SZ > behind) |
|---|
| 46 | + return memcmp(m1 + behind, m2 + behind, UBIFS_MST_NODE_SZ - behind); |
|---|
| 47 | + |
|---|
| 48 | + return 0; |
|---|
| 49 | +} |
|---|
| 50 | + |
|---|
| 51 | +/* mst_node_check_hash - Check hash of a master node |
|---|
| 52 | + * @c: UBIFS file-system description object |
|---|
| 53 | + * @mst: The master node |
|---|
| 54 | + * @expected: The expected hash of the master node |
|---|
| 55 | + * |
|---|
| 56 | + * This checks the hash of a master node against a given expected hash. |
|---|
| 57 | + * Note that we have two master nodes on a UBIFS image which have different |
|---|
| 58 | + * sequence numbers and consequently different CRCs. To be able to match |
|---|
| 59 | + * both master nodes we exclude the common node header containing the sequence |
|---|
| 60 | + * number and CRC from the hash. |
|---|
| 61 | + * |
|---|
| 62 | + * Returns 0 if the hashes are equal, a negative error code otherwise. |
|---|
| 63 | + */ |
|---|
| 64 | +static int mst_node_check_hash(const struct ubifs_info *c, |
|---|
| 65 | + const struct ubifs_mst_node *mst, |
|---|
| 66 | + const u8 *expected) |
|---|
| 67 | +{ |
|---|
| 68 | + u8 calc[UBIFS_MAX_HASH_LEN]; |
|---|
| 69 | + const void *node = mst; |
|---|
| 70 | + |
|---|
| 71 | + crypto_shash_tfm_digest(c->hash_tfm, node + sizeof(struct ubifs_ch), |
|---|
| 72 | + UBIFS_MST_NODE_SZ - sizeof(struct ubifs_ch), |
|---|
| 73 | + calc); |
|---|
| 74 | + |
|---|
| 75 | + if (ubifs_check_hash(c, expected, calc)) |
|---|
| 76 | + return -EPERM; |
|---|
| 77 | + |
|---|
| 78 | + return 0; |
|---|
| 79 | +} |
|---|
| 26 | 80 | |
|---|
| 27 | 81 | /** |
|---|
| 28 | 82 | * scan_for_master - search the valid master node. |
|---|
| .. | .. |
|---|
| 37 | 91 | { |
|---|
| 38 | 92 | struct ubifs_scan_leb *sleb; |
|---|
| 39 | 93 | struct ubifs_scan_node *snod; |
|---|
| 40 | | - int lnum, offs = 0, nodes_cnt; |
|---|
| 94 | + int lnum, offs = 0, nodes_cnt, err; |
|---|
| 41 | 95 | |
|---|
| 42 | 96 | lnum = UBIFS_MST_LNUM; |
|---|
| 43 | 97 | |
|---|
| .. | .. |
|---|
| 69 | 123 | goto out_dump; |
|---|
| 70 | 124 | if (snod->offs != offs) |
|---|
| 71 | 125 | goto out; |
|---|
| 72 | | - if (memcmp((void *)c->mst_node + UBIFS_CH_SZ, |
|---|
| 73 | | - (void *)snod->node + UBIFS_CH_SZ, |
|---|
| 74 | | - UBIFS_MST_NODE_SZ - UBIFS_CH_SZ)) |
|---|
| 126 | + if (ubifs_compare_master_node(c, c->mst_node, snod->node)) |
|---|
| 75 | 127 | goto out; |
|---|
| 128 | + |
|---|
| 76 | 129 | c->mst_offs = offs; |
|---|
| 77 | 130 | ubifs_scan_destroy(sleb); |
|---|
| 131 | + |
|---|
| 132 | + if (!ubifs_authenticated(c)) |
|---|
| 133 | + return 0; |
|---|
| 134 | + |
|---|
| 135 | + if (ubifs_hmac_zero(c, c->mst_node->hmac)) { |
|---|
| 136 | + err = mst_node_check_hash(c, c->mst_node, |
|---|
| 137 | + c->sup_node->hash_mst); |
|---|
| 138 | + if (err) |
|---|
| 139 | + ubifs_err(c, "Failed to verify master node hash"); |
|---|
| 140 | + } else { |
|---|
| 141 | + err = ubifs_node_verify_hmac(c, c->mst_node, |
|---|
| 142 | + sizeof(struct ubifs_mst_node), |
|---|
| 143 | + offsetof(struct ubifs_mst_node, hmac)); |
|---|
| 144 | + if (err) |
|---|
| 145 | + ubifs_err(c, "Failed to verify master node HMAC"); |
|---|
| 146 | + } |
|---|
| 147 | + |
|---|
| 148 | + if (err) |
|---|
| 149 | + return -EPERM; |
|---|
| 150 | + |
|---|
| 78 | 151 | return 0; |
|---|
| 79 | 152 | |
|---|
| 80 | 153 | out: |
|---|
| .. | .. |
|---|
| 305 | 378 | c->lst.total_dead = le64_to_cpu(c->mst_node->total_dead); |
|---|
| 306 | 379 | c->lst.total_dark = le64_to_cpu(c->mst_node->total_dark); |
|---|
| 307 | 380 | |
|---|
| 381 | + ubifs_copy_hash(c, c->mst_node->hash_root_idx, c->zroot.hash); |
|---|
| 382 | + |
|---|
| 308 | 383 | c->calc_idx_sz = c->bi.old_idx_sz; |
|---|
| 309 | 384 | |
|---|
| 310 | 385 | if (c->mst_node->flags & cpu_to_le32(UBIFS_MST_NO_ORPHS)) |
|---|
| .. | .. |
|---|
| 378 | 453 | c->mst_offs = offs; |
|---|
| 379 | 454 | c->mst_node->highest_inum = cpu_to_le64(c->highest_inum); |
|---|
| 380 | 455 | |
|---|
| 381 | | - err = ubifs_write_node(c, c->mst_node, len, lnum, offs); |
|---|
| 456 | + ubifs_copy_hash(c, c->zroot.hash, c->mst_node->hash_root_idx); |
|---|
| 457 | + err = ubifs_write_node_hmac(c, c->mst_node, len, lnum, offs, |
|---|
| 458 | + offsetof(struct ubifs_mst_node, hmac)); |
|---|
| 382 | 459 | if (err) |
|---|
| 383 | 460 | return err; |
|---|
| 384 | 461 | |
|---|
| .. | .. |
|---|
| 389 | 466 | if (err) |
|---|
| 390 | 467 | return err; |
|---|
| 391 | 468 | } |
|---|
| 392 | | - err = ubifs_write_node(c, c->mst_node, len, lnum, offs); |
|---|
| 469 | + err = ubifs_write_node_hmac(c, c->mst_node, len, lnum, offs, |
|---|
| 470 | + offsetof(struct ubifs_mst_node, hmac)); |
|---|
| 393 | 471 | |
|---|
| 394 | 472 | return err; |
|---|
| 395 | 473 | } |
|---|