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