.. | .. |
---|
| 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: Adrian Hunter |
---|
20 | 8 | * Artem Bityutskiy (Битюцкий Артём) |
---|
.. | .. |
---|
34 | 22 | |
---|
35 | 23 | #include "ubifs.h" |
---|
36 | 24 | #include <linux/list_sort.h> |
---|
| 25 | +#include <crypto/hash.h> |
---|
| 26 | +#include <crypto/algapi.h> |
---|
37 | 27 | |
---|
38 | 28 | /** |
---|
39 | 29 | * struct replay_entry - replay list entry. |
---|
.. | .. |
---|
56 | 46 | int lnum; |
---|
57 | 47 | int offs; |
---|
58 | 48 | int len; |
---|
| 49 | + u8 hash[UBIFS_HASH_ARR_SZ]; |
---|
59 | 50 | unsigned int deletion:1; |
---|
60 | 51 | unsigned long long sqnum; |
---|
61 | 52 | struct list_head list; |
---|
.. | .. |
---|
261 | 252 | err = ubifs_tnc_remove_nm(c, &r->key, &r->nm); |
---|
262 | 253 | else |
---|
263 | 254 | err = ubifs_tnc_add_nm(c, &r->key, r->lnum, r->offs, |
---|
264 | | - r->len, &r->nm); |
---|
| 255 | + r->len, r->hash, &r->nm); |
---|
265 | 256 | } else { |
---|
266 | 257 | if (r->deletion) |
---|
267 | 258 | switch (key_type(c, &r->key)) { |
---|
.. | .. |
---|
286 | 277 | } |
---|
287 | 278 | else |
---|
288 | 279 | err = ubifs_tnc_add(c, &r->key, r->lnum, r->offs, |
---|
289 | | - r->len); |
---|
| 280 | + r->len, r->hash); |
---|
290 | 281 | if (err) |
---|
291 | 282 | return err; |
---|
292 | 283 | |
---|
.. | .. |
---|
390 | 381 | * in case of success and a negative error code in case of failure. |
---|
391 | 382 | */ |
---|
392 | 383 | static int insert_node(struct ubifs_info *c, int lnum, int offs, int len, |
---|
393 | | - union ubifs_key *key, unsigned long long sqnum, |
---|
394 | | - int deletion, int *used, loff_t old_size, |
---|
395 | | - loff_t new_size) |
---|
| 384 | + const u8 *hash, union ubifs_key *key, |
---|
| 385 | + unsigned long long sqnum, int deletion, int *used, |
---|
| 386 | + loff_t old_size, loff_t new_size) |
---|
396 | 387 | { |
---|
397 | 388 | struct replay_entry *r; |
---|
398 | 389 | |
---|
.. | .. |
---|
410 | 401 | r->lnum = lnum; |
---|
411 | 402 | r->offs = offs; |
---|
412 | 403 | r->len = len; |
---|
| 404 | + ubifs_copy_hash(c, hash, r->hash); |
---|
413 | 405 | r->deletion = !!deletion; |
---|
414 | 406 | r->sqnum = sqnum; |
---|
415 | 407 | key_copy(c, key, &r->key); |
---|
.. | .. |
---|
438 | 430 | * negative error code in case of failure. |
---|
439 | 431 | */ |
---|
440 | 432 | static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len, |
---|
441 | | - union ubifs_key *key, const char *name, int nlen, |
---|
442 | | - unsigned long long sqnum, int deletion, int *used) |
---|
| 433 | + const u8 *hash, union ubifs_key *key, |
---|
| 434 | + const char *name, int nlen, unsigned long long sqnum, |
---|
| 435 | + int deletion, int *used) |
---|
443 | 436 | { |
---|
444 | 437 | struct replay_entry *r; |
---|
445 | 438 | char *nbuf; |
---|
.. | .. |
---|
463 | 456 | r->lnum = lnum; |
---|
464 | 457 | r->offs = offs; |
---|
465 | 458 | r->len = len; |
---|
| 459 | + ubifs_copy_hash(c, hash, r->hash); |
---|
466 | 460 | r->deletion = !!deletion; |
---|
467 | 461 | r->sqnum = sqnum; |
---|
468 | 462 | key_copy(c, key, &r->key); |
---|
.. | .. |
---|
565 | 559 | return data == 0xFFFFFFFF; |
---|
566 | 560 | } |
---|
567 | 561 | |
---|
| 562 | +/* authenticate_sleb_hash is split out for stack usage */ |
---|
| 563 | +static int noinline_for_stack |
---|
| 564 | +authenticate_sleb_hash(struct ubifs_info *c, |
---|
| 565 | + struct shash_desc *log_hash, u8 *hash) |
---|
| 566 | +{ |
---|
| 567 | + SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm); |
---|
| 568 | + |
---|
| 569 | + hash_desc->tfm = c->hash_tfm; |
---|
| 570 | + |
---|
| 571 | + ubifs_shash_copy_state(c, log_hash, hash_desc); |
---|
| 572 | + return crypto_shash_final(hash_desc, hash); |
---|
| 573 | +} |
---|
| 574 | + |
---|
| 575 | +/** |
---|
| 576 | + * authenticate_sleb - authenticate one scan LEB |
---|
| 577 | + * @c: UBIFS file-system description object |
---|
| 578 | + * @sleb: the scan LEB to authenticate |
---|
| 579 | + * @log_hash: |
---|
| 580 | + * @is_last: if true, this is is the last LEB |
---|
| 581 | + * |
---|
| 582 | + * This function iterates over the buds of a single LEB authenticating all buds |
---|
| 583 | + * with the authentication nodes on this LEB. Authentication nodes are written |
---|
| 584 | + * after some buds and contain a HMAC covering the authentication node itself |
---|
| 585 | + * and the buds between the last authentication node and the current |
---|
| 586 | + * authentication node. It can happen that the last buds cannot be authenticated |
---|
| 587 | + * because a powercut happened when some nodes were written but not the |
---|
| 588 | + * corresponding authentication node. This function returns the number of nodes |
---|
| 589 | + * that could be authenticated or a negative error code. |
---|
| 590 | + */ |
---|
| 591 | +static int authenticate_sleb(struct ubifs_info *c, struct ubifs_scan_leb *sleb, |
---|
| 592 | + struct shash_desc *log_hash, int is_last) |
---|
| 593 | +{ |
---|
| 594 | + int n_not_auth = 0; |
---|
| 595 | + struct ubifs_scan_node *snod; |
---|
| 596 | + int n_nodes = 0; |
---|
| 597 | + int err; |
---|
| 598 | + u8 hash[UBIFS_HASH_ARR_SZ]; |
---|
| 599 | + u8 hmac[UBIFS_HMAC_ARR_SZ]; |
---|
| 600 | + |
---|
| 601 | + if (!ubifs_authenticated(c)) |
---|
| 602 | + return sleb->nodes_cnt; |
---|
| 603 | + |
---|
| 604 | + list_for_each_entry(snod, &sleb->nodes, list) { |
---|
| 605 | + |
---|
| 606 | + n_nodes++; |
---|
| 607 | + |
---|
| 608 | + if (snod->type == UBIFS_AUTH_NODE) { |
---|
| 609 | + struct ubifs_auth_node *auth = snod->node; |
---|
| 610 | + |
---|
| 611 | + err = authenticate_sleb_hash(c, log_hash, hash); |
---|
| 612 | + if (err) |
---|
| 613 | + goto out; |
---|
| 614 | + |
---|
| 615 | + err = crypto_shash_tfm_digest(c->hmac_tfm, hash, |
---|
| 616 | + c->hash_len, hmac); |
---|
| 617 | + if (err) |
---|
| 618 | + goto out; |
---|
| 619 | + |
---|
| 620 | + err = ubifs_check_hmac(c, auth->hmac, hmac); |
---|
| 621 | + if (err) { |
---|
| 622 | + err = -EPERM; |
---|
| 623 | + goto out; |
---|
| 624 | + } |
---|
| 625 | + n_not_auth = 0; |
---|
| 626 | + } else { |
---|
| 627 | + err = crypto_shash_update(log_hash, snod->node, |
---|
| 628 | + snod->len); |
---|
| 629 | + if (err) |
---|
| 630 | + goto out; |
---|
| 631 | + n_not_auth++; |
---|
| 632 | + } |
---|
| 633 | + } |
---|
| 634 | + |
---|
| 635 | + /* |
---|
| 636 | + * A powercut can happen when some nodes were written, but not yet |
---|
| 637 | + * the corresponding authentication node. This may only happen on |
---|
| 638 | + * the last bud though. |
---|
| 639 | + */ |
---|
| 640 | + if (n_not_auth) { |
---|
| 641 | + if (is_last) { |
---|
| 642 | + dbg_mnt("%d unauthenticated nodes found on LEB %d, Ignoring them", |
---|
| 643 | + n_not_auth, sleb->lnum); |
---|
| 644 | + err = 0; |
---|
| 645 | + } else { |
---|
| 646 | + dbg_mnt("%d unauthenticated nodes found on non-last LEB %d", |
---|
| 647 | + n_not_auth, sleb->lnum); |
---|
| 648 | + err = -EPERM; |
---|
| 649 | + } |
---|
| 650 | + } else { |
---|
| 651 | + err = 0; |
---|
| 652 | + } |
---|
| 653 | +out: |
---|
| 654 | + return err ? err : n_nodes - n_not_auth; |
---|
| 655 | +} |
---|
| 656 | + |
---|
568 | 657 | /** |
---|
569 | 658 | * replay_bud - replay a bud logical eraseblock. |
---|
570 | 659 | * @c: UBIFS file-system description object |
---|
.. | .. |
---|
578 | 667 | { |
---|
579 | 668 | int is_last = is_last_bud(c, b->bud); |
---|
580 | 669 | int err = 0, used = 0, lnum = b->bud->lnum, offs = b->bud->start; |
---|
| 670 | + int n_nodes, n = 0; |
---|
581 | 671 | struct ubifs_scan_leb *sleb; |
---|
582 | 672 | struct ubifs_scan_node *snod; |
---|
583 | 673 | |
---|
.. | .. |
---|
596 | 686 | sleb = ubifs_scan(c, lnum, offs, c->sbuf, 0); |
---|
597 | 687 | if (IS_ERR(sleb)) |
---|
598 | 688 | return PTR_ERR(sleb); |
---|
| 689 | + |
---|
| 690 | + n_nodes = authenticate_sleb(c, sleb, b->bud->log_hash, is_last); |
---|
| 691 | + if (n_nodes < 0) { |
---|
| 692 | + err = n_nodes; |
---|
| 693 | + goto out; |
---|
| 694 | + } |
---|
| 695 | + |
---|
| 696 | + ubifs_shash_copy_state(c, b->bud->log_hash, |
---|
| 697 | + c->jheads[b->bud->jhead].log_hash); |
---|
599 | 698 | |
---|
600 | 699 | /* |
---|
601 | 700 | * The bud does not have to start from offset zero - the beginning of |
---|
.. | .. |
---|
620 | 719 | */ |
---|
621 | 720 | |
---|
622 | 721 | list_for_each_entry(snod, &sleb->nodes, list) { |
---|
| 722 | + u8 hash[UBIFS_HASH_ARR_SZ]; |
---|
623 | 723 | int deletion = 0; |
---|
624 | 724 | |
---|
625 | 725 | cond_resched(); |
---|
.. | .. |
---|
628 | 728 | ubifs_err(c, "file system's life ended"); |
---|
629 | 729 | goto out_dump; |
---|
630 | 730 | } |
---|
| 731 | + |
---|
| 732 | + ubifs_node_calc_hash(c, snod->node, hash); |
---|
631 | 733 | |
---|
632 | 734 | if (snod->sqnum > c->max_sqnum) |
---|
633 | 735 | c->max_sqnum = snod->sqnum; |
---|
.. | .. |
---|
640 | 742 | |
---|
641 | 743 | if (le32_to_cpu(ino->nlink) == 0) |
---|
642 | 744 | deletion = 1; |
---|
643 | | - err = insert_node(c, lnum, snod->offs, snod->len, |
---|
| 745 | + err = insert_node(c, lnum, snod->offs, snod->len, hash, |
---|
644 | 746 | &snod->key, snod->sqnum, deletion, |
---|
645 | 747 | &used, 0, new_size); |
---|
646 | 748 | break; |
---|
.. | .. |
---|
652 | 754 | key_block(c, &snod->key) * |
---|
653 | 755 | UBIFS_BLOCK_SIZE; |
---|
654 | 756 | |
---|
655 | | - err = insert_node(c, lnum, snod->offs, snod->len, |
---|
| 757 | + err = insert_node(c, lnum, snod->offs, snod->len, hash, |
---|
656 | 758 | &snod->key, snod->sqnum, deletion, |
---|
657 | 759 | &used, 0, new_size); |
---|
658 | 760 | break; |
---|
.. | .. |
---|
666 | 768 | if (err) |
---|
667 | 769 | goto out_dump; |
---|
668 | 770 | |
---|
669 | | - err = insert_dent(c, lnum, snod->offs, snod->len, |
---|
| 771 | + err = insert_dent(c, lnum, snod->offs, snod->len, hash, |
---|
670 | 772 | &snod->key, dent->name, |
---|
671 | 773 | le16_to_cpu(dent->nlen), snod->sqnum, |
---|
672 | 774 | !le64_to_cpu(dent->inum), &used); |
---|
.. | .. |
---|
692 | 794 | * functions which expect nodes to have keys. |
---|
693 | 795 | */ |
---|
694 | 796 | trun_key_init(c, &key, le32_to_cpu(trun->inum)); |
---|
695 | | - err = insert_node(c, lnum, snod->offs, snod->len, |
---|
| 797 | + err = insert_node(c, lnum, snod->offs, snod->len, hash, |
---|
696 | 798 | &key, snod->sqnum, 1, &used, |
---|
697 | 799 | old_size, new_size); |
---|
698 | 800 | break; |
---|
699 | 801 | } |
---|
| 802 | + case UBIFS_AUTH_NODE: |
---|
| 803 | + break; |
---|
700 | 804 | default: |
---|
701 | 805 | ubifs_err(c, "unexpected node type %d in bud LEB %d:%d", |
---|
702 | 806 | snod->type, lnum, snod->offs); |
---|
.. | .. |
---|
705 | 809 | } |
---|
706 | 810 | if (err) |
---|
707 | 811 | goto out; |
---|
| 812 | + |
---|
| 813 | + n++; |
---|
| 814 | + if (n == n_nodes) |
---|
| 815 | + break; |
---|
708 | 816 | } |
---|
709 | 817 | |
---|
710 | 818 | ubifs_assert(c, ubifs_search_bud(c, lnum)); |
---|
.. | .. |
---|
783 | 891 | { |
---|
784 | 892 | struct ubifs_bud *bud; |
---|
785 | 893 | struct bud_entry *b; |
---|
| 894 | + int err; |
---|
786 | 895 | |
---|
787 | 896 | dbg_mnt("add replay bud LEB %d:%d, head %d", lnum, offs, jhead); |
---|
788 | 897 | |
---|
.. | .. |
---|
792 | 901 | |
---|
793 | 902 | b = kmalloc(sizeof(struct bud_entry), GFP_KERNEL); |
---|
794 | 903 | if (!b) { |
---|
795 | | - kfree(bud); |
---|
796 | | - return -ENOMEM; |
---|
| 904 | + err = -ENOMEM; |
---|
| 905 | + goto out; |
---|
797 | 906 | } |
---|
798 | 907 | |
---|
799 | 908 | bud->lnum = lnum; |
---|
800 | 909 | bud->start = offs; |
---|
801 | 910 | bud->jhead = jhead; |
---|
| 911 | + bud->log_hash = ubifs_hash_get_desc(c); |
---|
| 912 | + if (IS_ERR(bud->log_hash)) { |
---|
| 913 | + err = PTR_ERR(bud->log_hash); |
---|
| 914 | + goto out; |
---|
| 915 | + } |
---|
| 916 | + |
---|
| 917 | + ubifs_shash_copy_state(c, c->log_hash, bud->log_hash); |
---|
| 918 | + |
---|
802 | 919 | ubifs_add_bud(c, bud); |
---|
803 | 920 | |
---|
804 | 921 | b->bud = bud; |
---|
.. | .. |
---|
806 | 923 | list_add_tail(&b->list, &c->replay_buds); |
---|
807 | 924 | |
---|
808 | 925 | return 0; |
---|
| 926 | +out: |
---|
| 927 | + kfree(bud); |
---|
| 928 | + kfree(b); |
---|
| 929 | + |
---|
| 930 | + return err; |
---|
809 | 931 | } |
---|
810 | 932 | |
---|
811 | 933 | /** |
---|
812 | 934 | * validate_ref - validate a reference node. |
---|
813 | 935 | * @c: UBIFS file-system description object |
---|
814 | 936 | * @ref: the reference node to validate |
---|
815 | | - * @ref_lnum: LEB number of the reference node |
---|
816 | | - * @ref_offs: reference node offset |
---|
817 | 937 | * |
---|
818 | 938 | * This function returns %1 if a bud reference already exists for the LEB. %0 is |
---|
819 | 939 | * returned if the reference node is new, otherwise %-EINVAL is returned if |
---|
.. | .. |
---|
911 | 1031 | |
---|
912 | 1032 | c->cs_sqnum = le64_to_cpu(node->ch.sqnum); |
---|
913 | 1033 | dbg_mnt("commit start sqnum %llu", c->cs_sqnum); |
---|
| 1034 | + |
---|
| 1035 | + err = ubifs_shash_init(c, c->log_hash); |
---|
| 1036 | + if (err) |
---|
| 1037 | + goto out; |
---|
| 1038 | + |
---|
| 1039 | + err = ubifs_shash_update(c, c->log_hash, node, UBIFS_CS_NODE_SZ); |
---|
| 1040 | + if (err < 0) |
---|
| 1041 | + goto out; |
---|
914 | 1042 | } |
---|
915 | 1043 | |
---|
916 | 1044 | if (snod->sqnum < c->cs_sqnum) { |
---|
.. | .. |
---|
958 | 1086 | if (err) |
---|
959 | 1087 | goto out_dump; |
---|
960 | 1088 | |
---|
| 1089 | + err = ubifs_shash_update(c, c->log_hash, ref, |
---|
| 1090 | + UBIFS_REF_NODE_SZ); |
---|
| 1091 | + if (err) |
---|
| 1092 | + goto out; |
---|
| 1093 | + |
---|
961 | 1094 | err = add_replay_bud(c, le32_to_cpu(ref->lnum), |
---|
962 | 1095 | le32_to_cpu(ref->offs), |
---|
963 | 1096 | le32_to_cpu(ref->jhead), |
---|