| .. | .. | 
|---|
 | 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), | 
|---|