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

ipe: add errno field to IPE policy load auditing

Users of IPE require a way to identify when and why an operation fails,
allowing them to both respond to violations of policy and be notified
of potentially malicious actions on their systems with respect to IPE.

This patch introduces a new error field to the AUDIT_IPE_POLICY_LOAD event
to log policy loading failures. Currently, IPE only logs successful policy
loads, but not failures. Tracking failures is crucial to detect malicious
attempts and ensure a complete audit trail for security events.

The new error field will capture the following error codes:

* -ENOKEY: Key used to sign the IPE policy not found in the keyring
* -ESTALE: Attempting to update an IPE policy with an older version
* -EKEYREJECTED: IPE signature verification failed
* -ENOENT: Policy was deleted while updating
* -EEXIST: Same name policy already deployed
* -ERANGE: Policy version number overflow
* -EINVAL: Policy version parsing error
* -EPERM: Insufficient permission
* -ENOMEM: Out of memory (OOM)
* -EBADMSG: Policy is invalid

Here are some examples of the updated audit record types:

AUDIT_IPE_POLICY_LOAD(1422):
audit: AUDIT1422 policy_name="Test_Policy" policy_version=0.0.1
policy_digest=sha256:84EFBA8FA71E62AE0A537FAB962F8A2BD1053964C4299DCA
92BFFF4DB82E86D3 auid=1000 ses=3 lsm=ipe res=1 errno=0

The above record shows a new policy has been successfully loaded into
the kernel with the policy name, version, and hash with the errno=0.

AUDIT_IPE_POLICY_LOAD(1422) with error:

audit: AUDIT1422 policy_name=? policy_version=? policy_digest=?
auid=1000 ses=3 lsm=ipe res=0 errno=-74

The above record shows a policy load failure due to an invalid policy
(-EBADMSG).

By adding this error field, we ensure that all policy load attempts,
whether successful or failed, are logged, providing a comprehensive
audit trail for IPE policy management.

Signed-off-by: Jasjiv Singh <jasjivsingh@linux.microsoft.com>
Signed-off-by: Fan Wu <wufan@kernel.org>

authored by

Jasjiv Singh and committed by
Fan Wu
1d887d6f 0ff41df1

+112 -44
+49 -18
Documentation/admin-guide/LSM/ipe.rst
··· 423 423 424 424 Event Example:: 425 425 426 - type=1422 audit(1653425529.927:53): policy_name="boot_verified" policy_version=0.0.0 policy_digest=sha256:820EEA5B40CA42B51F68962354BA083122A20BB846F26765076DD8EED7B8F4DB auid=4294967295 ses=4294967295 lsm=ipe res=1 426 + type=1422 audit(1653425529.927:53): policy_name="boot_verified" policy_version=0.0.0 policy_digest=sha256:820EEA5B40CA42B51F68962354BA083122A20BB846F26765076DD8EED7B8F4DB auid=4294967295 ses=4294967295 lsm=ipe res=1 errno=0 427 427 type=1300 audit(1653425529.927:53): arch=c000003e syscall=1 success=yes exit=2567 a0=3 a1=5596fcae1fb0 a2=a07 a3=2 items=0 ppid=184 pid=229 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=4294967295 comm="python3" exe="/usr/bin/python3.10" key=(null) 428 428 type=1327 audit(1653425529.927:53): PROCTITLE proctitle=707974686F6E3300746573742F6D61696E2E7079002D66002E2E 429 429 ··· 433 433 434 434 Field descriptions: 435 435 436 - +----------------+------------+-----------+---------------------------------------------------+ 437 - | Field | Value Type | Optional? | Description of Value | 438 - +================+============+===========+===================================================+ 439 - | policy_name | string | No | The policy_name | 440 - +----------------+------------+-----------+---------------------------------------------------+ 441 - | policy_version | string | No | The policy_version | 442 - +----------------+------------+-----------+---------------------------------------------------+ 443 - | policy_digest | string | No | The policy hash | 444 - +----------------+------------+-----------+---------------------------------------------------+ 445 - | auid | integer | No | The login user ID | 446 - +----------------+------------+-----------+---------------------------------------------------+ 447 - | ses | integer | No | The login session ID | 448 - +----------------+------------+-----------+---------------------------------------------------+ 449 - | lsm | string | No | The lsm name associated with the event | 450 - +----------------+------------+-----------+---------------------------------------------------+ 451 - | res | integer | No | The result of the audited operation(success/fail) | 452 - +----------------+------------+-----------+---------------------------------------------------+ 436 + +----------------+------------+-----------+-------------------------------------------------------------+ 437 + | Field | Value Type | Optional? | Description of Value | 438 + +================+============+===========+=============================================================+ 439 + | policy_name | string | Yes | The policy_name | 440 + +----------------+------------+-----------+-------------------------------------------------------------+ 441 + | policy_version | string | Yes | The policy_version | 442 + +----------------+------------+-----------+-------------------------------------------------------------+ 443 + | policy_digest | string | Yes | The policy hash | 444 + +----------------+------------+-----------+-------------------------------------------------------------+ 445 + | auid | integer | No | The login user ID | 446 + +----------------+------------+-----------+-------------------------------------------------------------+ 447 + | ses | integer | No | The login session ID | 448 + +----------------+------------+-----------+-------------------------------------------------------------+ 449 + | lsm | string | No | The lsm name associated with the event | 450 + +----------------+------------+-----------+-------------------------------------------------------------+ 451 + | res | integer | No | The result of the audited operation(success/fail) | 452 + +----------------+------------+-----------+-------------------------------------------------------------+ 453 + | errno | integer | No | Error code from policy loading operations (see table below) | 454 + +----------------+------------+-----------+-------------------------------------------------------------+ 453 455 456 + Policy error codes (errno): 457 + 458 + The following table lists the error codes that may appear in the errno field while loading or updating the policy: 459 + 460 + +----------------+--------------------------------------------------------+ 461 + | Error Code | Description | 462 + +================+========================================================+ 463 + | 0 | Success | 464 + +----------------+--------------------------------------------------------+ 465 + | -EPERM | Insufficient permission | 466 + +----------------+--------------------------------------------------------+ 467 + | -EEXIST | Same name policy already deployed | 468 + +----------------+--------------------------------------------------------+ 469 + | -EBADMSG | Policy is invalid | 470 + +----------------+--------------------------------------------------------+ 471 + | -ENOMEM | Out of memory (OOM) | 472 + +----------------+--------------------------------------------------------+ 473 + | -ERANGE | Policy version number overflow | 474 + +----------------+--------------------------------------------------------+ 475 + | -EINVAL | Policy version parsing error | 476 + +----------------+--------------------------------------------------------+ 477 + | -ENOKEY | Key used to sign the IPE policy not found in keyring | 478 + +----------------+--------------------------------------------------------+ 479 + | -EKEYREJECTED | Policy signature verification failed | 480 + +----------------+--------------------------------------------------------+ 481 + | -ESTALE | Attempting to update an IPE policy with older version | 482 + +----------------+--------------------------------------------------------+ 483 + | -ENOENT | Policy was deleted while updating | 484 + +----------------+--------------------------------------------------------+ 454 485 455 486 1404 AUDIT_MAC_STATUS 456 487 ^^^^^^^^^^^^^^^^^^^^^
+14 -5
security/ipe/audit.c
··· 21 21 22 22 #define AUDIT_POLICY_LOAD_FMT "policy_name=\"%s\" policy_version=%hu.%hu.%hu "\ 23 23 "policy_digest=" IPE_AUDIT_HASH_ALG ":" 24 + #define AUDIT_POLICY_LOAD_NULL_FMT "policy_name=? policy_version=? "\ 25 + "policy_digest=?" 24 26 #define AUDIT_OLD_ACTIVE_POLICY_FMT "old_active_pol_name=\"%s\" "\ 25 27 "old_active_pol_version=%hu.%hu.%hu "\ 26 28 "old_policy_digest=" IPE_AUDIT_HASH_ALG ":" ··· 250 248 } 251 249 252 250 /** 253 - * ipe_audit_policy_load() - Audit a policy being loaded into the kernel. 254 - * @p: Supplies a pointer to the policy to audit. 251 + * ipe_audit_policy_load() - Audit a policy loading event. 252 + * @p: Supplies a pointer to the policy to audit or an error pointer. 255 253 */ 256 254 void ipe_audit_policy_load(const struct ipe_policy *const p) 257 255 { 258 256 struct audit_buffer *ab; 257 + int err = 0; 259 258 260 259 ab = audit_log_start(audit_context(), GFP_KERNEL, 261 260 AUDIT_IPE_POLICY_LOAD); 262 261 if (!ab) 263 262 return; 264 263 265 - audit_policy(ab, AUDIT_POLICY_LOAD_FMT, p); 266 - audit_log_format(ab, " auid=%u ses=%u lsm=ipe res=1", 264 + if (!IS_ERR(p)) { 265 + audit_policy(ab, AUDIT_POLICY_LOAD_FMT, p); 266 + } else { 267 + audit_log_format(ab, AUDIT_POLICY_LOAD_NULL_FMT); 268 + err = PTR_ERR(p); 269 + } 270 + 271 + audit_log_format(ab, " auid=%u ses=%u lsm=ipe res=%d errno=%d", 267 272 from_kuid(&init_user_ns, audit_get_loginuid(current)), 268 - audit_get_sessionid(current)); 273 + audit_get_sessionid(current), !err, err); 269 274 270 275 audit_log_end(ab); 271 276 }
+17 -8
security/ipe/fs.c
··· 133 133 * * %-ERANGE - Policy version number overflow 134 134 * * %-EINVAL - Policy version parsing error 135 135 * * %-EEXIST - Same name policy already deployed 136 + * * %-ENOKEY - Policy signing key not found 137 + * * %-EKEYREJECTED - Policy signature verification failed 136 138 */ 137 139 static ssize_t new_policy(struct file *f, const char __user *data, 138 140 size_t len, loff_t *offset) ··· 143 141 char *copy = NULL; 144 142 int rc = 0; 145 143 146 - if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN)) 147 - return -EPERM; 144 + if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN)) { 145 + rc = -EPERM; 146 + goto out; 147 + } 148 148 149 149 copy = memdup_user_nul(data, len); 150 - if (IS_ERR(copy)) 151 - return PTR_ERR(copy); 150 + if (IS_ERR(copy)) { 151 + rc = PTR_ERR(copy); 152 + copy = NULL; 153 + goto out; 154 + } 152 155 153 156 p = ipe_new_policy(NULL, 0, copy, len); 154 157 if (IS_ERR(p)) { ··· 165 158 if (rc) 166 159 goto out; 167 160 168 - ipe_audit_policy_load(p); 169 - 170 161 out: 171 - if (rc < 0) 172 - ipe_free_policy(p); 173 162 kfree(copy); 163 + if (rc < 0) { 164 + ipe_free_policy(p); 165 + ipe_audit_policy_load(ERR_PTR(rc)); 166 + } else { 167 + ipe_audit_policy_load(p); 168 + } 174 169 return (rc < 0) ? rc : len; 175 170 } 176 171
+11 -6
security/ipe/policy.c
··· 84 84 * ipe_new_policy. 85 85 * 86 86 * Context: Requires root->i_rwsem to be held. 87 - * Return: %0 on success. If an error occurs, the function will return 88 - * the -errno. 87 + * Return: 88 + * * %0 - Success 89 + * * %-ENOENT - Policy was deleted while updating 90 + * * %-EINVAL - Policy name mismatch 91 + * * %-ESTALE - Policy version too old 89 92 */ 90 93 int ipe_update_policy(struct inode *root, const char *text, size_t textlen, 91 94 const char *pkcs7, size_t pkcs7len) ··· 149 146 * 150 147 * Return: 151 148 * * a pointer to the ipe_policy structure - Success 152 - * * %-EBADMSG - Policy is invalid 153 - * * %-ENOMEM - Out of memory (OOM) 154 - * * %-ERANGE - Policy version number overflow 155 - * * %-EINVAL - Policy version parsing error 149 + * * %-EBADMSG - Policy is invalid 150 + * * %-ENOMEM - Out of memory (OOM) 151 + * * %-ERANGE - Policy version number overflow 152 + * * %-EINVAL - Policy version parsing error 153 + * * %-ENOKEY - Policy signing key not found 154 + * * %-EKEYREJECTED - Policy signature verification failed 156 155 */ 157 156 struct ipe_policy *ipe_new_policy(const char *text, size_t textlen, 158 157 const char *pkcs7, size_t pkcs7len)
+21 -7
security/ipe/policy_fs.c
··· 12 12 #include "policy.h" 13 13 #include "eval.h" 14 14 #include "fs.h" 15 + #include "audit.h" 15 16 16 17 #define MAX_VERSION_SIZE ARRAY_SIZE("65535.65535.65535") 17 18 ··· 287 286 * On success this updates the policy represented by $name, 288 287 * in-place. 289 288 * 290 - * Return: Length of buffer written on success. If an error occurs, 291 - * the function will return the -errno. 289 + * Return: 290 + * * Length of buffer written - Success 291 + * * %-EPERM - Insufficient permission 292 + * * %-ENOMEM - Out of memory (OOM) 293 + * * %-ENOENT - Policy was deleted while updating 294 + * * %-EINVAL - Policy name mismatch 295 + * * %-ESTALE - Policy version too old 292 296 */ 293 297 static ssize_t update_policy(struct file *f, const char __user *data, 294 298 size_t len, loff_t *offset) ··· 302 296 char *copy = NULL; 303 297 int rc = 0; 304 298 305 - if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN)) 306 - return -EPERM; 299 + if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN)) { 300 + rc = -EPERM; 301 + goto out; 302 + } 307 303 308 304 copy = memdup_user(data, len); 309 - if (IS_ERR(copy)) 310 - return PTR_ERR(copy); 305 + if (IS_ERR(copy)) { 306 + rc = PTR_ERR(copy); 307 + copy = NULL; 308 + goto out; 309 + } 311 310 312 311 root = d_inode(f->f_path.dentry->d_parent); 313 312 inode_lock(root); 314 313 rc = ipe_update_policy(root, NULL, 0, copy, len); 315 314 inode_unlock(root); 316 315 316 + out: 317 317 kfree(copy); 318 - if (rc) 318 + if (rc) { 319 + ipe_audit_policy_load(ERR_PTR(rc)); 319 320 return rc; 321 + } 320 322 321 323 return len; 322 324 }