diff mbox series

[v5,07/12] evm: Allow xattr/attr operations for portable signatures

Message ID 20210407105252.30721-8-roberto.sassu@huawei.com (mailing list archive)
State New, archived
Headers show
Series evm: Improve usability of portable signatures | expand

Commit Message

Roberto Sassu April 7, 2021, 10:52 a.m. UTC
If files with portable signatures are copied from one location to another
or are extracted from an archive, verification can temporarily fail until
all xattrs/attrs are set in the destination. Only portable signatures may
be moved or copied from one file to another, as they don't depend on
system-specific information such as the inode generation. Instead portable
signatures must include security.ima.

Unlike other security.evm types, EVM portable signatures are also
immutable. Thus, it wouldn't be a problem to allow xattr/attr operations
when verification fails, as portable signatures will never be replaced with
the HMAC on possibly corrupted xattrs/attrs.

This patch first introduces a new integrity status called
INTEGRITY_FAIL_IMMUTABLE, that allows callers of
evm_verify_current_integrity() to detect that a portable signature didn't
pass verification and then adds an exception in evm_protect_xattr() and
evm_inode_setattr() for this status and returns 0 instead of -EPERM.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
---
 include/linux/integrity.h             |  1 +
 security/integrity/evm/evm_main.c     | 31 +++++++++++++++++++++------
 security/integrity/ima/ima_appraise.c |  2 ++
 3 files changed, 28 insertions(+), 6 deletions(-)

Comments

Mimi Zohar May 3, 2021, 12:12 a.m. UTC | #1
Hi Roberto,

> diff --git a/include/linux/integrity.h b/include/linux/integrity.h
> index 2271939c5c31..2ea0f2f65ab6 100644
> --- a/include/linux/integrity.h
> +++ b/include/linux/integrity.h
> 
> @@ -238,9 +241,12 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
>  		break;
>  	}
>  
> -	if (rc)
> -		evm_status = (rc == -ENODATA) ?
> -				INTEGRITY_NOXATTRS : INTEGRITY_FAIL;
> +	if (rc) {
> +		evm_status = INTEGRITY_NOXATTRS;
> +		if (rc != -ENODATA)
> +			evm_status = evm_immutable ?
> +				     INTEGRITY_FAIL_IMMUTABLE : INTEGRITY_FAIL;

The original code made an exception for the -ENODATA case.   Using a
ternary operator made sense in that case.   Inverting the test makes
the code less readable.  Please use the standard "if" statement
instead.

thanks,

Mimi
Roberto Sassu May 4, 2021, 2:28 p.m. UTC | #2
> From: Mimi Zohar [mailto:zohar@linux.ibm.com]
> Sent: Monday, May 3, 2021 2:13 AM
> Hi Roberto,
> 
> > diff --git a/include/linux/integrity.h b/include/linux/integrity.h
> > index 2271939c5c31..2ea0f2f65ab6 100644
> > --- a/include/linux/integrity.h
> > +++ b/include/linux/integrity.h
> >
> > @@ -238,9 +241,12 @@ static enum integrity_status
> evm_verify_hmac(struct dentry *dentry,
> >  		break;
> >  	}
> >
> > -	if (rc)
> > -		evm_status = (rc == -ENODATA) ?
> > -				INTEGRITY_NOXATTRS : INTEGRITY_FAIL;
> > +	if (rc) {
> > +		evm_status = INTEGRITY_NOXATTRS;
> > +		if (rc != -ENODATA)
> > +			evm_status = evm_immutable ?
> > +				     INTEGRITY_FAIL_IMMUTABLE :
> INTEGRITY_FAIL;
> 
> The original code made an exception for the -ENODATA case.   Using a
> ternary operator made sense in that case.   Inverting the test makes
> the code less readable.  Please use the standard "if" statement
> instead.

Did I understand correctly that the code should be:

                evm_status = INTEGRITY_NOXATTRS;
                if (rc != -ENODATA) {
                        evm_status = INTEGRITY_FAIL;
                        if (evm_immutable)
                                evm_status = INTEGRITY_FAIL_IMMUTABLE;
                }

Thanks

Roberto

HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063
Managing Director: Li Peng, Li Jian, Shi Yanli

> thanks,
> 
> Mimi
Mimi Zohar May 4, 2021, 2:49 p.m. UTC | #3
On Tue, 2021-05-04 at 14:28 +0000, Roberto Sassu wrote:
> > From: Mimi Zohar [mailto:zohar@linux.ibm.com]
> > Sent: Monday, May 3, 2021 2:13 AM
> > Hi Roberto,
> > 
> > > diff --git a/include/linux/integrity.h b/include/linux/integrity.h
> > > index 2271939c5c31..2ea0f2f65ab6 100644
> > > --- a/include/linux/integrity.h
> > > +++ b/include/linux/integrity.h
> > >
> > > @@ -238,9 +241,12 @@ static enum integrity_status
> > evm_verify_hmac(struct dentry *dentry,
> > >  		break;
> > >  	}
> > >
> > > -	if (rc)
> > > -		evm_status = (rc == -ENODATA) ?
> > > -				INTEGRITY_NOXATTRS : INTEGRITY_FAIL;
> > > +	if (rc) {
> > > +		evm_status = INTEGRITY_NOXATTRS;
> > > +		if (rc != -ENODATA)
> > > +			evm_status = evm_immutable ?
> > > +				     INTEGRITY_FAIL_IMMUTABLE :
> > INTEGRITY_FAIL;
> > 
> > The original code made an exception for the -ENODATA case.   Using a
> > ternary operator made sense in that case.   Inverting the test makes
> > the code less readable.  Please use the standard "if" statement
> > instead.
> 
> Did I understand correctly that the code should be:
> 
>                 evm_status = INTEGRITY_NOXATTRS;
>                 if (rc != -ENODA
>                         evm_status = INTEGRITY_FAIL;
>                         if (evm_immutable)
>                                 evm_status = INTEGRITY_FAIL_IMMUTABLE;
>                 }
> 
 
                if (rc == -ENODATA)
                        evm_status = INTEGRITY_NOXATTRS;
                else if (evm_status == evm_immutable)
                        evm_status = INTEGRITY_FAIL_IMMUTABLE;
                else
                        evm_status = INTEGRITY_FAIL;

I think keeping it simple makes it really clear that ENODATA is an
exception.

thanks,

Mimi
diff mbox series

Patch

diff --git a/include/linux/integrity.h b/include/linux/integrity.h
index 2271939c5c31..2ea0f2f65ab6 100644
--- a/include/linux/integrity.h
+++ b/include/linux/integrity.h
@@ -13,6 +13,7 @@  enum integrity_status {
 	INTEGRITY_PASS = 0,
 	INTEGRITY_PASS_IMMUTABLE,
 	INTEGRITY_FAIL,
+	INTEGRITY_FAIL_IMMUTABLE,
 	INTEGRITY_NOLABEL,
 	INTEGRITY_NOXATTRS,
 	INTEGRITY_UNKNOWN,
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 6556e8c22da9..eab536fa260f 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -27,7 +27,8 @@ 
 int evm_initialized;
 
 static const char * const integrity_status_msg[] = {
-	"pass", "pass_immutable", "fail", "no_label", "no_xattrs", "unknown"
+	"pass", "pass_immutable", "fail", "fail_immutable", "no_label",
+	"no_xattrs", "unknown"
 };
 int evm_hmac_attrs;
 
@@ -155,7 +156,7 @@  static enum integrity_status evm_verify_hmac(struct dentry *dentry,
 	enum integrity_status evm_status = INTEGRITY_PASS;
 	struct evm_digest digest;
 	struct inode *inode;
-	int rc, xattr_len;
+	int rc, xattr_len, evm_immutable = 0;
 
 	if (iint && (iint->evm_status == INTEGRITY_PASS ||
 		     iint->evm_status == INTEGRITY_PASS_IMMUTABLE))
@@ -200,8 +201,10 @@  static enum integrity_status evm_verify_hmac(struct dentry *dentry,
 		if (rc)
 			rc = -EINVAL;
 		break;
-	case EVM_IMA_XATTR_DIGSIG:
 	case EVM_XATTR_PORTABLE_DIGSIG:
+		evm_immutable = 1;
+		fallthrough;
+	case EVM_IMA_XATTR_DIGSIG:
 		/* accept xattr with non-empty signature field */
 		if (xattr_len <= sizeof(struct signature_v2_hdr)) {
 			evm_status = INTEGRITY_FAIL;
@@ -238,9 +241,12 @@  static enum integrity_status evm_verify_hmac(struct dentry *dentry,
 		break;
 	}
 
-	if (rc)
-		evm_status = (rc == -ENODATA) ?
-				INTEGRITY_NOXATTRS : INTEGRITY_FAIL;
+	if (rc) {
+		evm_status = INTEGRITY_NOXATTRS;
+		if (rc != -ENODATA)
+			evm_status = evm_immutable ?
+				     INTEGRITY_FAIL_IMMUTABLE : INTEGRITY_FAIL;
+	}
 out:
 	if (iint)
 		iint->evm_status = evm_status;
@@ -374,6 +380,14 @@  static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
 out:
 	if (evm_ignore_error_safe(evm_status))
 		return 0;
+
+	/*
+	 * Writing other xattrs is safe for portable signatures, as portable
+	 * signatures are immutable and can never be updated.
+	 */
+	if (evm_status == INTEGRITY_FAIL_IMMUTABLE)
+		return 0;
+
 	if (evm_status != INTEGRITY_PASS)
 		integrity_audit_msg(AUDIT_INTEGRITY_METADATA, d_backing_inode(dentry),
 				    dentry->d_name.name, "appraise_metadata",
@@ -534,8 +548,13 @@  int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
 	if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)))
 		return 0;
 	evm_status = evm_verify_current_integrity(dentry);
+	/*
+	 * Writing attrs is safe for portable signatures, as portable signatures
+	 * are immutable and can never be updated.
+	 */
 	if ((evm_status == INTEGRITY_PASS) ||
 	    (evm_status == INTEGRITY_NOXATTRS) ||
+	    (evm_status == INTEGRITY_FAIL_IMMUTABLE) ||
 	    (evm_ignore_error_safe(evm_status)))
 		return 0;
 	integrity_audit_msg(AUDIT_INTEGRITY_METADATA, d_backing_inode(dentry),
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index d4b8db1acadd..24d59893aab0 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -416,6 +416,8 @@  int ima_appraise_measurement(enum ima_hooks func,
 	case INTEGRITY_NOLABEL:		/* No security.evm xattr. */
 		cause = "missing-HMAC";
 		goto out;
+	case INTEGRITY_FAIL_IMMUTABLE:
+		fallthrough;
 	case INTEGRITY_FAIL:		/* Invalid HMAC/signature. */
 		cause = "invalid-HMAC";
 		goto out;