1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Landlock - Audit helpers 4 * 5 * Copyright © 2023-2025 Microsoft Corporation 6 */ 7 8 #include <linux/audit.h> 9 #include <linux/bitops.h> 10 #include <linux/landlock.h> 11 #include <linux/lsm_audit.h> 12 #include <linux/pid.h> 13 #include <uapi/linux/landlock.h> 14 15 #include "access.h" 16 #include "audit.h" 17 #include "common.h" 18 #include "cred.h" 19 #include "domain.h" 20 #include "limits.h" 21 #include "log.h" 22 23 /* 24 * Access-right and scope names are built from the lists shared with the trace 25 * events (see <linux/landlock.h>). The designated initializer places each name 26 * at its bit index, so the lookup stays O(1) and does not depend on the entry 27 * order. log_blockers() adds the "fs."/"net."/"scope." category prefix. 28 */ 29 #define _LANDLOCK_NAME_ENTRY(mask, name) [BIT_INDEX(mask)] = name 30 31 static const char *const fs_access_strings[] = { _LANDLOCK_ACCESS_FS_NAMES }; 32 33 static_assert(ARRAY_SIZE(fs_access_strings) == LANDLOCK_NUM_ACCESS_FS); 34 35 static const char *const net_access_strings[] = { _LANDLOCK_ACCESS_NET_NAMES }; 36 37 static_assert(ARRAY_SIZE(net_access_strings) == LANDLOCK_NUM_ACCESS_NET); 38 39 static const char *const scope_strings[] = { _LANDLOCK_SCOPE_NAMES }; 40 41 static_assert(ARRAY_SIZE(scope_strings) == LANDLOCK_NUM_SCOPE); 42 43 #undef _LANDLOCK_NAME_ENTRY 44 45 static __attribute_const__ const char * 46 get_blocker(const enum landlock_request_type type, 47 const unsigned long access_bit) 48 { 49 switch (type) { 50 case LANDLOCK_REQUEST_PTRACE: 51 WARN_ON_ONCE(access_bit != -1); 52 return "ptrace"; 53 54 case LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY: 55 WARN_ON_ONCE(access_bit != -1); 56 return "change_topology"; 57 58 case LANDLOCK_REQUEST_FS_ACCESS: 59 if (WARN_ON_ONCE(access_bit >= ARRAY_SIZE(fs_access_strings))) 60 return "unknown"; 61 return fs_access_strings[access_bit]; 62 63 case LANDLOCK_REQUEST_NET_ACCESS: 64 if (WARN_ON_ONCE(access_bit >= ARRAY_SIZE(net_access_strings))) 65 return "unknown"; 66 return net_access_strings[access_bit]; 67 68 case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET: 69 WARN_ON_ONCE(access_bit != -1); 70 return scope_strings[BIT_INDEX( 71 LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET)]; 72 73 case LANDLOCK_REQUEST_SCOPE_SIGNAL: 74 WARN_ON_ONCE(access_bit != -1); 75 return scope_strings[BIT_INDEX(LANDLOCK_SCOPE_SIGNAL)]; 76 } 77 78 WARN_ON_ONCE(1); 79 return "unknown"; 80 } 81 82 /* 83 * Returns the audit category prefix prepended to the unprefixed blocker name 84 * returned by get_blocker() (filesystem and network access rights, 85 * change_topology, and scopes). The ptrace blocker is standalone and carries 86 * its full name in get_blocker(), so it uses no prefix. 87 */ 88 static __attribute_const__ const char * 89 blocker_prefix(const enum landlock_request_type type) 90 { 91 switch (type) { 92 case LANDLOCK_REQUEST_PTRACE: 93 return ""; 94 95 case LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY: 96 case LANDLOCK_REQUEST_FS_ACCESS: 97 return "fs."; 98 99 case LANDLOCK_REQUEST_NET_ACCESS: 100 return "net."; 101 102 case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET: 103 case LANDLOCK_REQUEST_SCOPE_SIGNAL: 104 return "scope."; 105 } 106 107 WARN_ON_ONCE(1); 108 return ""; 109 } 110 111 static void log_blockers(struct audit_buffer *const ab, 112 const enum landlock_request_type type, 113 const access_mask_t access) 114 { 115 const unsigned long access_mask = access; 116 const char *const prefix = blocker_prefix(type); 117 unsigned long access_bit; 118 bool is_first = true; 119 120 for_each_set_bit(access_bit, &access_mask, BITS_PER_TYPE(access)) { 121 audit_log_format(ab, "%s%s%s", is_first ? "" : ",", prefix, 122 get_blocker(type, access_bit)); 123 is_first = false; 124 } 125 if (is_first) 126 audit_log_format(ab, "%s%s", prefix, get_blocker(type, -1)); 127 } 128 129 static void log_domain(struct landlock_hierarchy *const hierarchy) 130 { 131 struct audit_buffer *ab; 132 133 /* Ignores already logged domains. */ 134 if (READ_ONCE(hierarchy->log_status) == LANDLOCK_LOG_RECORDED) 135 return; 136 137 /* Uses consistent allocation flags wrt common_lsm_audit(). */ 138 ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN, 139 AUDIT_LANDLOCK_DOMAIN); 140 if (!ab) 141 return; 142 143 WARN_ON_ONCE(hierarchy->id == 0); 144 audit_log_format( 145 ab, 146 "domain=%llx status=allocated mode=enforcing pid=%d uid=%u exe=", 147 hierarchy->id, pid_nr(hierarchy->details->pid), 148 hierarchy->details->uid); 149 audit_log_untrustedstring(ab, hierarchy->details->exe_path); 150 audit_log_format(ab, " comm="); 151 audit_log_untrustedstring(ab, hierarchy->details->comm); 152 audit_log_end(ab); 153 154 /* 155 * There may be race condition leading to logging of the same domain 156 * several times but that is OK. 157 */ 158 WRITE_ONCE(hierarchy->log_status, LANDLOCK_LOG_RECORDED); 159 } 160 161 /** 162 * landlock_audit_denial - Create an audit record for a denied access request 163 * 164 * @request: Detail of the user space request. 165 * @youngest_denied: The youngest hierarchy node that denied the access. 166 * @missing: The set of denied access rights. 167 * @logged: Whether the denial is selected for logging, as computed by 168 * landlock_log_denial() (domain policy and quiet rules). 169 * 170 * Emits the record when audit is enabled and the denial is selected for 171 * logging. 172 */ 173 void landlock_audit_denial(const struct landlock_request *const request, 174 struct landlock_hierarchy *const youngest_denied, 175 const access_mask_t missing, const bool logged) 176 { 177 struct audit_buffer *ab; 178 179 if (!audit_enabled) 180 return; 181 182 /* 183 * Skips denials the domain's policy or a quiet rule excludes from 184 * logging (folded into @logged by landlock_log_denial()). 185 */ 186 if (!logged) 187 return; 188 189 /* Uses consistent allocation flags wrt common_lsm_audit(). */ 190 ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN, 191 AUDIT_LANDLOCK_ACCESS); 192 if (!ab) 193 return; 194 195 audit_log_format(ab, "domain=%llx blockers=", youngest_denied->id); 196 log_blockers(ab, request->type, missing); 197 audit_log_lsm_data(ab, &request->audit); 198 audit_log_end(ab); 199 200 /* Logs this domain the first time it shows in log. */ 201 log_domain(youngest_denied); 202 } 203 204 /** 205 * landlock_audit_free_domain - Create an audit record on domain deallocation 206 * 207 * @hierarchy: The domain's hierarchy being deallocated. 208 * 209 * Only domains which previously appeared in the audit logs are logged again. 210 * This is useful to know when a domain will never show again in the audit log. 211 * 212 * Called from landlock_log_free_domain(). 213 */ 214 void landlock_audit_free_domain(const struct landlock_hierarchy *const hierarchy) 215 { 216 struct audit_buffer *ab; 217 218 if (!audit_enabled) 219 return; 220 221 /* Ignores domains that were not logged. */ 222 if (READ_ONCE(hierarchy->log_status) != LANDLOCK_LOG_RECORDED) 223 return; 224 225 /* 226 * If logging of domain allocation succeeded, warns about failure to log 227 * domain deallocation to highlight unbalanced domain lifetime logs. 228 */ 229 ab = audit_log_start(audit_context(), GFP_KERNEL, 230 AUDIT_LANDLOCK_DOMAIN); 231 if (!ab) 232 return; 233 234 audit_log_format(ab, "domain=%llx status=deallocated denials=%llu", 235 hierarchy->id, atomic64_read(&hierarchy->num_denials)); 236 audit_log_end(ab); 237 } 238