Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ima: support fs-verity file digest based version 3 signatures

IMA may verify a file's integrity against a "good" value stored in the
'security.ima' xattr or as an appended signature, based on policy. When
the "good value" is stored in the xattr, the xattr may contain a file
hash or signature. In either case, the "good" value is preceded by a
header. The first byte of the xattr header indicates the type of data
- hash, signature - stored in the xattr. To support storing fs-verity
signatures in the 'security.ima' xattr requires further differentiating
the fs-verity signature from the existing IMA signature.

In addition the signatures stored in 'security.ima' xattr, need to be
disambiguated. Instead of directly signing the fs-verity digest, a new
signature format version 3 is defined as the hash of the ima_file_id
structure, which identifies the type of signature and the digest.

The IMA policy defines "which" files are to be measured, verified, and/or
audited. For those files being verified, the policy rules indicate "how"
the file should be verified. For example to require a file be signed,
the appraise policy rule must include the 'appraise_type' option.

appraise_type:= [imasig] | [imasig|modsig] | [sigv3]
where 'imasig' is the original or signature format v2 (default),
where 'modsig' is an appended signature,
where 'sigv3' is the signature format v3.

The policy rule must also indicate the type of digest, if not the IMA
default, by first specifying the digest type:

digest_type:= [verity]

The following policy rule requires fsverity signatures. The rule may be
constrained, for example based on a fsuuid or LSM label.

appraise func=BPRM_CHECK digest_type=verity appraise_type=sigv3

Acked-by: Stefan Berger <stefanb@linux.ibm.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>

+209 -19
+30 -1
Documentation/ABI/testing/ima_policy
··· 48 48 fgroup:= decimal value 49 49 lsm: are LSM specific 50 50 option: 51 - appraise_type:= [imasig] [imasig|modsig] 51 + appraise_type:= [imasig] | [imasig|modsig] | [sigv3] 52 + where 'imasig' is the original or the signature 53 + format v2. 54 + where 'modsig' is an appended signature, 55 + where 'sigv3' is the signature format v3. (Currently 56 + limited to fsverity digest based signatures 57 + stored in security.ima xattr. Requires 58 + specifying "digest_type=verity" first.) 59 + 52 60 appraise_flag:= [check_blacklist] 53 61 Currently, blacklist check is only for files signed with appended 54 62 signature. ··· 167 159 168 160 measure func=FILE_CHECK digest_type=verity \ 169 161 template=ima-ngv2 162 + 163 + Example of 'measure' and 'appraise' rules requiring fs-verity 164 + signatures (format version 3) stored in security.ima xattr. 165 + 166 + The 'measure' rule specifies the 'ima-sigv3' template option, 167 + which includes the indication of type of digest and the file 168 + signature in the measurement list. 169 + 170 + measure func=BPRM_CHECK digest_type=verity \ 171 + template=ima-sigv3 172 + 173 + 174 + The 'appraise' rule specifies the type and signature format 175 + version (sigv3) required. 176 + 177 + appraise func=BPRM_CHECK digest_type=verity \ 178 + appraise_type=sigv3 179 + 180 + All of these policy rules could, for example, be constrained 181 + either based on a filesystem's UUID (fsuuid) or based on LSM 182 + labels.
+2 -2
Documentation/security/IMA-templates.rst
··· 71 71 (field format: <digest type>:<hash algo>:digest); 72 72 - 'd-modsig': the digest of the event without the appended modsig; 73 73 - 'n-ng': the name of the event, without size limitations; 74 - - 'sig': the file signature, or the EVM portable signature if the file 75 - signature is not found; 74 + - 'sig': the file signature, based on either the file's/fsverity's digest[1], 75 + or the EVM portable signature, if 'security.ima' contains a file hash. 76 76 - 'modsig' the appended file signature; 77 77 - 'buf': the buffer data that was used to generate the hash without size limitations; 78 78 - 'evmsig': the EVM portable signature;
+2 -1
security/integrity/digsig.c
··· 75 75 /* v1 API expect signature without xattr type */ 76 76 return digsig_verify(keyring, sig + 1, siglen - 1, digest, 77 77 digestlen); 78 - case 2: 78 + case 2: /* regular file data hash based signature */ 79 + case 3: /* struct ima_file_id data based signature */ 79 80 return asymmetric_verify(keyring, sig, siglen, digest, 80 81 digestlen); 81 82 }
+110 -4
security/integrity/ima/ima_appraise.c
··· 13 13 #include <linux/magic.h> 14 14 #include <linux/ima.h> 15 15 #include <linux/evm.h> 16 + #include <linux/fsverity.h> 16 17 #include <keys/system_keyring.h> 18 + #include <uapi/linux/fsverity.h> 17 19 18 20 #include "ima.h" 19 21 ··· 185 183 return ima_hash_algo; 186 184 187 185 switch (xattr_value->type) { 186 + case IMA_VERITY_DIGSIG: 187 + sig = (typeof(sig))xattr_value; 188 + if (sig->version != 3 || xattr_len <= sizeof(*sig) || 189 + sig->hash_algo >= HASH_ALGO__LAST) 190 + return ima_hash_algo; 191 + return sig->hash_algo; 188 192 case EVM_IMA_XATTR_DIGSIG: 189 193 sig = (typeof(sig))xattr_value; 190 194 if (sig->version != 2 || xattr_len <= sizeof(*sig) 191 195 || sig->hash_algo >= HASH_ALGO__LAST) 192 196 return ima_hash_algo; 193 197 return sig->hash_algo; 194 - break; 195 198 case IMA_XATTR_DIGEST_NG: 196 199 /* first byte contains algorithm id */ 197 200 ret = xattr_value->data[0]; ··· 233 226 } 234 227 235 228 /* 229 + * calc_file_id_hash - calculate the hash of the ima_file_id struct data 230 + * @type: xattr type [enum evm_ima_xattr_type] 231 + * @algo: hash algorithm [enum hash_algo] 232 + * @digest: pointer to the digest to be hashed 233 + * @hash: (out) pointer to the hash 234 + * 235 + * IMA signature version 3 disambiguates the data that is signed by 236 + * indirectly signing the hash of the ima_file_id structure data. 237 + * 238 + * Signing the ima_file_id struct is currently only supported for 239 + * IMA_VERITY_DIGSIG type xattrs. 240 + * 241 + * Return 0 on success, error code otherwise. 242 + */ 243 + static int calc_file_id_hash(enum evm_ima_xattr_type type, 244 + enum hash_algo algo, const u8 *digest, 245 + struct ima_digest_data *hash) 246 + { 247 + struct ima_file_id file_id = { 248 + .hash_type = IMA_VERITY_DIGSIG, .hash_algorithm = algo}; 249 + unsigned int unused = HASH_MAX_DIGESTSIZE - hash_digest_size[algo]; 250 + 251 + if (type != IMA_VERITY_DIGSIG) 252 + return -EINVAL; 253 + 254 + memcpy(file_id.hash, digest, hash_digest_size[algo]); 255 + 256 + hash->algo = algo; 257 + hash->length = hash_digest_size[algo]; 258 + 259 + return ima_calc_buffer_hash(&file_id, sizeof(file_id) - unused, hash); 260 + } 261 + 262 + /* 236 263 * xattr_verify - verify xattr digest or signature 237 264 * 238 265 * Verify whether the hash or signature matches the file contents. ··· 277 236 struct evm_ima_xattr_data *xattr_value, int xattr_len, 278 237 enum integrity_status *status, const char **cause) 279 238 { 239 + struct ima_max_digest_data hash; 240 + struct signature_v2_hdr *sig; 280 241 int rc = -EINVAL, hash_start = 0; 242 + int mask; 281 243 282 244 switch (xattr_value->type) { 283 245 case IMA_XATTR_DIGEST_NG: ··· 290 246 case IMA_XATTR_DIGEST: 291 247 if (*status != INTEGRITY_PASS_IMMUTABLE) { 292 248 if (iint->flags & IMA_DIGSIG_REQUIRED) { 293 - *cause = "IMA-signature-required"; 249 + if (iint->flags & IMA_VERITY_REQUIRED) 250 + *cause = "verity-signature-required"; 251 + else 252 + *cause = "IMA-signature-required"; 294 253 *status = INTEGRITY_FAIL; 295 254 break; 296 255 } ··· 321 274 break; 322 275 case EVM_IMA_XATTR_DIGSIG: 323 276 set_bit(IMA_DIGSIG, &iint->atomic_flags); 277 + 278 + mask = IMA_DIGSIG_REQUIRED | IMA_VERITY_REQUIRED; 279 + if ((iint->flags & mask) == mask) { 280 + *cause = "verity-signature-required"; 281 + *status = INTEGRITY_FAIL; 282 + break; 283 + } 284 + 285 + sig = (typeof(sig))xattr_value; 286 + if (sig->version >= 3) { 287 + *cause = "invalid-signature-version"; 288 + *status = INTEGRITY_FAIL; 289 + break; 290 + } 324 291 rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA, 325 292 (const char *)xattr_value, 326 293 xattr_len, ··· 357 296 } else { 358 297 *status = INTEGRITY_PASS; 359 298 } 299 + break; 300 + case IMA_VERITY_DIGSIG: 301 + set_bit(IMA_DIGSIG, &iint->atomic_flags); 302 + 303 + if (iint->flags & IMA_DIGSIG_REQUIRED) { 304 + if (!(iint->flags & IMA_VERITY_REQUIRED)) { 305 + *cause = "IMA-signature-required"; 306 + *status = INTEGRITY_FAIL; 307 + break; 308 + } 309 + } 310 + 311 + sig = (typeof(sig))xattr_value; 312 + if (sig->version != 3) { 313 + *cause = "invalid-signature-version"; 314 + *status = INTEGRITY_FAIL; 315 + break; 316 + } 317 + 318 + rc = calc_file_id_hash(IMA_VERITY_DIGSIG, iint->ima_hash->algo, 319 + iint->ima_hash->digest, &hash.hdr); 320 + if (rc) { 321 + *cause = "sigv3-hashing-error"; 322 + *status = INTEGRITY_FAIL; 323 + break; 324 + } 325 + 326 + rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA, 327 + (const char *)xattr_value, 328 + xattr_len, hash.digest, 329 + hash.hdr.length); 330 + if (rc) { 331 + *cause = "invalid-verity-signature"; 332 + *status = INTEGRITY_FAIL; 333 + } else { 334 + *status = INTEGRITY_PASS; 335 + } 336 + 360 337 break; 361 338 default: 362 339 *status = INTEGRITY_UNKNOWN; ··· 495 396 if (rc && rc != -ENODATA) 496 397 goto out; 497 398 498 - cause = iint->flags & IMA_DIGSIG_REQUIRED ? 499 - "IMA-signature-required" : "missing-hash"; 399 + if (iint->flags & IMA_DIGSIG_REQUIRED) { 400 + if (iint->flags & IMA_VERITY_REQUIRED) 401 + cause = "verity-signature-required"; 402 + else 403 + cause = "IMA-signature-required"; 404 + } else { 405 + cause = "missing-hash"; 406 + } 407 + 500 408 status = INTEGRITY_NOLABEL; 501 409 if (file->f_mode & FMODE_CREATED) 502 410 iint->flags |= IMA_NEW_FILE;
+38 -8
security/integrity/ima/ima_policy.c
··· 1310 1310 !(entry->flags & IMA_MODSIG_ALLOWED)) 1311 1311 return false; 1312 1312 1313 + /* 1314 + * Unlike for regular IMA 'appraise' policy rules where security.ima 1315 + * xattr may contain either a file hash or signature, the security.ima 1316 + * xattr for fsverity must contain a file signature (sigv3). Ensure 1317 + * that 'appraise' rules for fsverity require file signatures by 1318 + * checking the IMA_DIGSIG_REQUIRED flag is set. 1319 + */ 1320 + if (entry->action == APPRAISE && 1321 + (entry->flags & IMA_VERITY_REQUIRED) && 1322 + !(entry->flags & IMA_DIGSIG_REQUIRED)) 1323 + return false; 1324 + 1313 1325 return true; 1314 1326 } 1315 1327 ··· 1739 1727 break; 1740 1728 case Opt_digest_type: 1741 1729 ima_log_string(ab, "digest_type", args[0].from); 1742 - if ((strcmp(args[0].from, "verity")) == 0) 1730 + if (entry->flags & IMA_DIGSIG_REQUIRED) 1731 + result = -EINVAL; 1732 + else if ((strcmp(args[0].from, "verity")) == 0) 1743 1733 entry->flags |= IMA_VERITY_REQUIRED; 1744 1734 else 1745 1735 result = -EINVAL; 1746 1736 break; 1747 1737 case Opt_appraise_type: 1748 1738 ima_log_string(ab, "appraise_type", args[0].from); 1749 - if ((strcmp(args[0].from, "imasig")) == 0) 1750 - entry->flags |= IMA_DIGSIG_REQUIRED; 1751 - else if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) && 1752 - strcmp(args[0].from, "imasig|modsig") == 0) 1753 - entry->flags |= IMA_DIGSIG_REQUIRED | 1739 + 1740 + if ((strcmp(args[0].from, "imasig")) == 0) { 1741 + if (entry->flags & IMA_VERITY_REQUIRED) 1742 + result = -EINVAL; 1743 + else 1744 + entry->flags |= IMA_DIGSIG_REQUIRED; 1745 + } else if (strcmp(args[0].from, "sigv3") == 0) { 1746 + /* Only fsverity supports sigv3 for now */ 1747 + if (entry->flags & IMA_VERITY_REQUIRED) 1748 + entry->flags |= IMA_DIGSIG_REQUIRED; 1749 + else 1750 + result = -EINVAL; 1751 + } else if (IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG) && 1752 + strcmp(args[0].from, "imasig|modsig") == 0) { 1753 + if (entry->flags & IMA_VERITY_REQUIRED) 1754 + result = -EINVAL; 1755 + else 1756 + entry->flags |= IMA_DIGSIG_REQUIRED | 1754 1757 IMA_MODSIG_ALLOWED; 1755 - else 1758 + } else { 1756 1759 result = -EINVAL; 1760 + } 1757 1761 break; 1758 1762 case Opt_appraise_flag: 1759 1763 ima_log_string(ab, "appraise_flag", args[0].from); ··· 2211 2183 if (entry->template) 2212 2184 seq_printf(m, "template=%s ", entry->template->name); 2213 2185 if (entry->flags & IMA_DIGSIG_REQUIRED) { 2214 - if (entry->flags & IMA_MODSIG_ALLOWED) 2186 + if (entry->flags & IMA_VERITY_REQUIRED) 2187 + seq_puts(m, "appraise_type=sigv3 "); 2188 + else if (entry->flags & IMA_MODSIG_ALLOWED) 2215 2189 seq_puts(m, "appraise_type=imasig|modsig "); 2216 2190 else 2217 2191 seq_puts(m, "appraise_type=imasig ");
+3 -1
security/integrity/ima/ima_template_lib.c
··· 535 535 { 536 536 struct evm_ima_xattr_data *xattr_value = event_data->xattr_value; 537 537 538 - if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG)) 538 + if (!xattr_value || 539 + (xattr_value->type != EVM_IMA_XATTR_DIGSIG && 540 + xattr_value->type != IMA_VERITY_DIGSIG)) 539 541 return ima_eventevmsig_init(event_data, field_data); 540 542 541 543 return ima_write_template_field_data(xattr_value, event_data->xattr_len,
+24 -2
security/integrity/integrity.h
··· 79 79 EVM_IMA_XATTR_DIGSIG, 80 80 IMA_XATTR_DIGEST_NG, 81 81 EVM_XATTR_PORTABLE_DIGSIG, 82 + IMA_VERITY_DIGSIG, 82 83 IMA_XATTR_LAST 83 84 }; 84 85 ··· 94 93 u8 digest[SHA1_DIGEST_SIZE]; 95 94 } __packed; 96 95 97 - #define IMA_MAX_DIGEST_SIZE 64 96 + #define IMA_MAX_DIGEST_SIZE HASH_MAX_DIGESTSIZE 98 97 99 98 struct ima_digest_data { 100 99 u8 algo; ··· 123 122 } __packed; 124 123 125 124 /* 126 - * signature format v2 - for using with asymmetric keys 125 + * signature header format v2 - for using with asymmetric keys 126 + * 127 + * The signature_v2_hdr struct includes a signature format version 128 + * to simplify defining new signature formats. 129 + * 130 + * signature format: 131 + * version 2: regular file data hash based signature 132 + * version 3: struct ima_file_id data based signature 127 133 */ 128 134 struct signature_v2_hdr { 129 135 uint8_t type; /* xattr type */ ··· 139 131 __be32 keyid; /* IMA key identifier - not X509/PGP specific */ 140 132 __be16 sig_size; /* signature size */ 141 133 uint8_t sig[]; /* signature payload */ 134 + } __packed; 135 + 136 + /* 137 + * IMA signature version 3 disambiguates the data that is signed, by 138 + * indirectly signing the hash of the ima_file_id structure data, 139 + * containing either the fsverity_descriptor struct digest or, in the 140 + * future, the regular IMA file hash. 141 + * 142 + * (The hash of the ima_file_id structure is only of the portion used.) 143 + */ 144 + struct ima_file_id { 145 + __u8 hash_type; /* xattr type [enum evm_ima_xattr_type] */ 146 + __u8 hash_algorithm; /* Digest algorithm [enum hash_algo] */ 147 + __u8 hash[HASH_MAX_DIGESTSIZE]; 142 148 } __packed; 143 149 144 150 /* integrity data associated with an inode */