1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * AppArmor security module 4 * 5 * This file contains basic common functions used in AppArmor 6 * 7 * Copyright (C) 1998-2008 Novell/SUSE 8 * Copyright 2009-2010 Canonical Ltd. 9 */ 10 11 #include <linux/ctype.h> 12 #include <linux/mm.h> 13 #include <linux/slab.h> 14 #include <linux/string.h> 15 #include <linux/vmalloc.h> 16 17 #include "include/audit.h" 18 #include "include/apparmor.h" 19 #include "include/lib.h" 20 #include "include/perms.h" 21 #include "include/policy.h" 22 23 struct aa_perms nullperms; 24 struct aa_perms allperms = { .allow = ALL_PERMS_MASK, 25 .quiet = ALL_PERMS_MASK, 26 .hide = ALL_PERMS_MASK }; 27 28 /** 29 * aa_free_str_table - free entries str table 30 * @t: the string table to free (MAYBE NULL) 31 */ 32 void aa_free_str_table(struct aa_str_table *t) 33 { 34 int i; 35 36 if (t) { 37 if (!t->table) 38 return; 39 40 for (i = 0; i < t->size; i++) 41 kfree_sensitive(t->table[i]); 42 kfree_sensitive(t->table); 43 t->table = NULL; 44 t->size = 0; 45 } 46 } 47 48 /** 49 * aa_split_fqname - split a fqname into a profile and namespace name 50 * @fqname: a full qualified name in namespace profile format (NOT NULL) 51 * @ns_name: pointer to portion of the string containing the ns name (NOT NULL) 52 * 53 * Returns: profile name or NULL if one is not specified 54 * 55 * Split a namespace name from a profile name (see policy.c for naming 56 * description). If a portion of the name is missing it returns NULL for 57 * that portion. 58 * 59 * NOTE: may modify the @fqname string. The pointers returned point 60 * into the @fqname string. 61 */ 62 char *aa_split_fqname(char *fqname, char **ns_name) 63 { 64 char *name = strim(fqname); 65 66 *ns_name = NULL; 67 if (name[0] == ':') { 68 char *split = strchr(&name[1], ':'); 69 *ns_name = skip_spaces(&name[1]); 70 if (split) { 71 /* overwrite ':' with \0 */ 72 *split++ = 0; 73 if (strncmp(split, "//", 2) == 0) 74 split += 2; 75 name = skip_spaces(split); 76 } else 77 /* a ns name without a following profile is allowed */ 78 name = NULL; 79 } 80 if (name && *name == 0) 81 name = NULL; 82 83 return name; 84 } 85 86 /** 87 * skipn_spaces - Removes leading whitespace from @str. 88 * @str: The string to be stripped. 89 * @n: length of str to parse, will stop at \0 if encountered before n 90 * 91 * Returns a pointer to the first non-whitespace character in @str. 92 * if all whitespace will return NULL 93 */ 94 95 const char *skipn_spaces(const char *str, size_t n) 96 { 97 for (; n && isspace(*str); --n) 98 ++str; 99 if (n) 100 return (char *)str; 101 return NULL; 102 } 103 104 const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, 105 size_t *ns_len) 106 { 107 const char *end = fqname + n; 108 const char *name = skipn_spaces(fqname, n); 109 110 *ns_name = NULL; 111 *ns_len = 0; 112 113 if (!name) 114 return NULL; 115 116 if (name[0] == ':') { 117 char *split = strnchr(&name[1], end - &name[1], ':'); 118 *ns_name = skipn_spaces(&name[1], end - &name[1]); 119 if (!*ns_name) 120 return NULL; 121 if (split) { 122 *ns_len = split - *ns_name; 123 if (*ns_len == 0) 124 *ns_name = NULL; 125 split++; 126 if (end - split > 1 && strncmp(split, "//", 2) == 0) 127 split += 2; 128 name = skipn_spaces(split, end - split); 129 } else { 130 /* a ns name without a following profile is allowed */ 131 name = NULL; 132 *ns_len = end - *ns_name; 133 } 134 } 135 if (name && *name == 0) 136 name = NULL; 137 138 return name; 139 } 140 141 /** 142 * aa_info_message - log a none profile related status message 143 * @str: message to log 144 */ 145 void aa_info_message(const char *str) 146 { 147 if (audit_enabled) { 148 DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, NULL); 149 150 ad.info = str; 151 aa_audit_msg(AUDIT_APPARMOR_STATUS, &ad, NULL); 152 } 153 printk(KERN_INFO "AppArmor: %s\n", str); 154 } 155 156 __counted char *aa_str_alloc(int size, gfp_t gfp) 157 { 158 struct counted_str *str; 159 160 str = kmalloc(struct_size(str, name, size), gfp); 161 if (!str) 162 return NULL; 163 164 kref_init(&str->count); 165 return str->name; 166 } 167 168 void aa_str_kref(struct kref *kref) 169 { 170 kfree(container_of(kref, struct counted_str, count)); 171 } 172 173 174 const char aa_file_perm_chrs[] = "xwracd km l "; 175 const char *aa_file_perm_names[] = { 176 "exec", 177 "write", 178 "read", 179 "append", 180 181 "create", 182 "delete", 183 "open", 184 "rename", 185 186 "setattr", 187 "getattr", 188 "setcred", 189 "getcred", 190 191 "chmod", 192 "chown", 193 "chgrp", 194 "lock", 195 196 "mmap", 197 "mprot", 198 "link", 199 "snapshot", 200 201 "unknown", 202 "unknown", 203 "unknown", 204 "unknown", 205 206 "unknown", 207 "unknown", 208 "unknown", 209 "unknown", 210 211 "stack", 212 "change_onexec", 213 "change_profile", 214 "change_hat", 215 }; 216 217 /** 218 * aa_perm_mask_to_str - convert a perm mask to its short string 219 * @str: character buffer to store string in (at least 10 characters) 220 * @str_size: size of the @str buffer 221 * @chrs: NUL-terminated character buffer of permission characters 222 * @mask: permission mask to convert 223 */ 224 void aa_perm_mask_to_str(char *str, size_t str_size, const char *chrs, u32 mask) 225 { 226 unsigned int i, perm = 1; 227 size_t num_chrs = strlen(chrs); 228 229 for (i = 0; i < num_chrs; perm <<= 1, i++) { 230 if (mask & perm) { 231 /* Ensure that one byte is left for NUL-termination */ 232 if (WARN_ON_ONCE(str_size <= 1)) 233 break; 234 235 *str++ = chrs[i]; 236 str_size--; 237 } 238 } 239 *str = '\0'; 240 } 241 242 void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names, 243 u32 mask) 244 { 245 const char *fmt = "%s"; 246 unsigned int i, perm = 1; 247 bool prev = false; 248 249 for (i = 0; i < 32; perm <<= 1, i++) { 250 if (mask & perm) { 251 audit_log_format(ab, fmt, names[i]); 252 if (!prev) { 253 prev = true; 254 fmt = " %s"; 255 } 256 } 257 } 258 } 259 260 void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, 261 u32 chrsmask, const char * const *names, u32 namesmask) 262 { 263 char str[33]; 264 265 audit_log_format(ab, "\""); 266 if ((mask & chrsmask) && chrs) { 267 aa_perm_mask_to_str(str, sizeof(str), chrs, mask & chrsmask); 268 mask &= ~chrsmask; 269 audit_log_format(ab, "%s", str); 270 if (mask & namesmask) 271 audit_log_format(ab, " "); 272 } 273 if ((mask & namesmask) && names) 274 aa_audit_perm_names(ab, names, mask & namesmask); 275 audit_log_format(ab, "\""); 276 } 277 278 /** 279 * aa_audit_perms_cb - generic callback fn for auditing perms 280 * @ab: audit buffer (NOT NULL) 281 * @va: audit struct to audit values of (NOT NULL) 282 */ 283 static void aa_audit_perms_cb(struct audit_buffer *ab, void *va) 284 { 285 struct common_audit_data *sa = va; 286 struct apparmor_audit_data *ad = aad(sa); 287 288 if (ad->request) { 289 audit_log_format(ab, " requested_mask="); 290 aa_audit_perm_mask(ab, ad->request, aa_file_perm_chrs, 291 PERMS_CHRS_MASK, aa_file_perm_names, 292 PERMS_NAMES_MASK); 293 } 294 if (ad->denied) { 295 audit_log_format(ab, "denied_mask="); 296 aa_audit_perm_mask(ab, ad->denied, aa_file_perm_chrs, 297 PERMS_CHRS_MASK, aa_file_perm_names, 298 PERMS_NAMES_MASK); 299 } 300 audit_log_format(ab, " peer="); 301 aa_label_xaudit(ab, labels_ns(ad->subj_label), ad->peer, 302 FLAGS_NONE, GFP_ATOMIC); 303 } 304 305 /** 306 * aa_apply_modes_to_perms - apply namespace and profile flags to perms 307 * @profile: that perms where computed from 308 * @perms: perms to apply mode modifiers to 309 * 310 * TODO: split into profile and ns based flags for when accumulating perms 311 */ 312 void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms) 313 { 314 switch (AUDIT_MODE(profile)) { 315 case AUDIT_ALL: 316 perms->audit = ALL_PERMS_MASK; 317 fallthrough; 318 case AUDIT_NOQUIET: 319 perms->quiet = 0; 320 break; 321 case AUDIT_QUIET: 322 perms->audit = 0; 323 fallthrough; 324 case AUDIT_QUIET_DENIED: 325 perms->quiet = ALL_PERMS_MASK; 326 break; 327 } 328 329 if (KILL_MODE(profile)) 330 perms->kill = ALL_PERMS_MASK; 331 else if (COMPLAIN_MODE(profile)) 332 perms->complain = ALL_PERMS_MASK; 333 else if (USER_MODE(profile)) 334 perms->prompt = ALL_PERMS_MASK; 335 } 336 337 void aa_profile_match_label(struct aa_profile *profile, 338 struct aa_ruleset *rules, 339 struct aa_label *label, 340 int type, u32 request, struct aa_perms *perms) 341 { 342 /* TODO: doesn't yet handle extended types */ 343 aa_state_t state; 344 345 state = aa_dfa_next(rules->policy->dfa, 346 rules->policy->start[AA_CLASS_LABEL], 347 type); 348 aa_label_match(profile, rules, label, state, false, request, perms); 349 } 350 351 352 /* currently unused */ 353 int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target, 354 u32 request, int type, u32 *deny, 355 struct apparmor_audit_data *ad) 356 { 357 struct aa_ruleset *rules = list_first_entry(&profile->rules, 358 typeof(*rules), list); 359 struct aa_perms perms; 360 361 ad->peer = &target->label; 362 ad->request = request; 363 364 aa_profile_match_label(profile, rules, &target->label, type, request, 365 &perms); 366 aa_apply_modes_to_perms(profile, &perms); 367 *deny |= request & perms.deny; 368 return aa_check_perms(profile, &perms, request, ad, aa_audit_perms_cb); 369 } 370 371 /** 372 * aa_check_perms - do audit mode selection based on perms set 373 * @profile: profile being checked 374 * @perms: perms computed for the request 375 * @request: requested perms 376 * @ad: initialized audit structure (MAY BE NULL if not auditing) 377 * @cb: callback fn for type specific fields (MAY BE NULL) 378 * 379 * Returns: 0 if permission else error code 380 * 381 * Note: profile audit modes need to be set before calling by setting the 382 * perm masks appropriately. 383 * 384 * If not auditing then complain mode is not enabled and the 385 * error code will indicate whether there was an explicit deny 386 * with a positive value. 387 */ 388 int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms, 389 u32 request, struct apparmor_audit_data *ad, 390 void (*cb)(struct audit_buffer *, void *)) 391 { 392 int type, error; 393 u32 denied = request & (~perms->allow | perms->deny); 394 395 if (likely(!denied)) { 396 /* mask off perms that are not being force audited */ 397 request &= perms->audit; 398 if (!request || !ad) 399 return 0; 400 401 type = AUDIT_APPARMOR_AUDIT; 402 error = 0; 403 } else { 404 error = -EACCES; 405 406 if (denied & perms->kill) 407 type = AUDIT_APPARMOR_KILL; 408 else if (denied == (denied & perms->complain)) 409 type = AUDIT_APPARMOR_ALLOWED; 410 else 411 type = AUDIT_APPARMOR_DENIED; 412 413 if (denied == (denied & perms->hide)) 414 error = -ENOENT; 415 416 denied &= ~perms->quiet; 417 if (!ad || !denied) 418 return error; 419 } 420 421 if (ad) { 422 ad->subj_label = &profile->label; 423 ad->request = request; 424 ad->denied = denied; 425 ad->error = error; 426 aa_audit_msg(type, ad, cb); 427 } 428 429 if (type == AUDIT_APPARMOR_ALLOWED) 430 error = 0; 431 432 return error; 433 } 434 435 436 /** 437 * aa_policy_init - initialize a policy structure 438 * @policy: policy to initialize (NOT NULL) 439 * @prefix: prefix name if any is required. (MAYBE NULL) 440 * @name: name of the policy, init will make a copy of it (NOT NULL) 441 * @gfp: allocation mode 442 * 443 * Note: this fn creates a copy of strings passed in 444 * 445 * Returns: true if policy init successful 446 */ 447 bool aa_policy_init(struct aa_policy *policy, const char *prefix, 448 const char *name, gfp_t gfp) 449 { 450 char *hname; 451 452 /* freed by policy_free */ 453 if (prefix) { 454 hname = aa_str_alloc(strlen(prefix) + strlen(name) + 3, gfp); 455 if (hname) 456 sprintf(hname, "%s//%s", prefix, name); 457 } else { 458 hname = aa_str_alloc(strlen(name) + 1, gfp); 459 if (hname) 460 strcpy(hname, name); 461 } 462 if (!hname) 463 return false; 464 policy->hname = hname; 465 /* base.name is a substring of fqname */ 466 policy->name = basename(policy->hname); 467 INIT_LIST_HEAD(&policy->list); 468 INIT_LIST_HEAD(&policy->profiles); 469 470 return true; 471 } 472 473 /** 474 * aa_policy_destroy - free the elements referenced by @policy 475 * @policy: policy that is to have its elements freed (NOT NULL) 476 */ 477 void aa_policy_destroy(struct aa_policy *policy) 478 { 479 AA_BUG(on_list_rcu(&policy->profiles)); 480 AA_BUG(on_list_rcu(&policy->list)); 481 482 /* don't free name as its a subset of hname */ 483 aa_put_str(policy->hname); 484 } 485
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.