1cdff2642SJohn Johansen /* 2cdff2642SJohn Johansen * AppArmor security module 3cdff2642SJohn Johansen * 4cdff2642SJohn Johansen * This file contains basic common functions used in AppArmor 5cdff2642SJohn Johansen * 6cdff2642SJohn Johansen * Copyright (C) 1998-2008 Novell/SUSE 7cdff2642SJohn Johansen * Copyright 2009-2010 Canonical Ltd. 8cdff2642SJohn Johansen * 9cdff2642SJohn Johansen * This program is free software; you can redistribute it and/or 10cdff2642SJohn Johansen * modify it under the terms of the GNU General Public License as 11cdff2642SJohn Johansen * published by the Free Software Foundation, version 2 of the 12cdff2642SJohn Johansen * License. 13cdff2642SJohn Johansen */ 14cdff2642SJohn Johansen 153b0aaf58SJohn Johansen #include <linux/ctype.h> 16b7f080cfSAlexey Dobriyan #include <linux/mm.h> 17cdff2642SJohn Johansen #include <linux/slab.h> 18cdff2642SJohn Johansen #include <linux/string.h> 19cdff2642SJohn Johansen #include <linux/vmalloc.h> 20cdff2642SJohn Johansen 21cdff2642SJohn Johansen #include "include/audit.h" 2232c3df63SJames Morris #include "include/apparmor.h" 2312557dcbSJohn Johansen #include "include/lib.h" 24fc7e0b26SJohn Johansen #include "include/perms.h" 25fe6bb31fSJohn Johansen #include "include/policy.h" 26cdff2642SJohn Johansen 272d679f3cSJohn Johansen struct aa_perms nullperms; 28aa9aeea8SJohn Johansen struct aa_perms allperms = { .allow = ALL_PERMS_MASK, 29aa9aeea8SJohn Johansen .quiet = ALL_PERMS_MASK, 30aa9aeea8SJohn Johansen .hide = ALL_PERMS_MASK }; 31aa9aeea8SJohn Johansen 32cdff2642SJohn Johansen /** 33cdff2642SJohn Johansen * aa_split_fqname - split a fqname into a profile and namespace name 34cdff2642SJohn Johansen * @fqname: a full qualified name in namespace profile format (NOT NULL) 35cdff2642SJohn Johansen * @ns_name: pointer to portion of the string containing the ns name (NOT NULL) 36cdff2642SJohn Johansen * 37cdff2642SJohn Johansen * Returns: profile name or NULL if one is not specified 38cdff2642SJohn Johansen * 39cdff2642SJohn Johansen * Split a namespace name from a profile name (see policy.c for naming 40cdff2642SJohn Johansen * description). If a portion of the name is missing it returns NULL for 41cdff2642SJohn Johansen * that portion. 42cdff2642SJohn Johansen * 43cdff2642SJohn Johansen * NOTE: may modify the @fqname string. The pointers returned point 44cdff2642SJohn Johansen * into the @fqname string. 45cdff2642SJohn Johansen */ 46cdff2642SJohn Johansen char *aa_split_fqname(char *fqname, char **ns_name) 47cdff2642SJohn Johansen { 48cdff2642SJohn Johansen char *name = strim(fqname); 49cdff2642SJohn Johansen 50cdff2642SJohn Johansen *ns_name = NULL; 51cdff2642SJohn Johansen if (name[0] == ':') { 52cdff2642SJohn Johansen char *split = strchr(&name[1], ':'); 5304ccd53fSJohn Johansen *ns_name = skip_spaces(&name[1]); 54cdff2642SJohn Johansen if (split) { 55cdff2642SJohn Johansen /* overwrite ':' with \0 */ 562654bfbcSJohn Johansen *split++ = 0; 572654bfbcSJohn Johansen if (strncmp(split, "//", 2) == 0) 582654bfbcSJohn Johansen split += 2; 592654bfbcSJohn Johansen name = skip_spaces(split); 60cdff2642SJohn Johansen } else 61cdff2642SJohn Johansen /* a ns name without a following profile is allowed */ 62cdff2642SJohn Johansen name = NULL; 63cdff2642SJohn Johansen } 64cdff2642SJohn Johansen if (name && *name == 0) 65cdff2642SJohn Johansen name = NULL; 66cdff2642SJohn Johansen 67cdff2642SJohn Johansen return name; 68cdff2642SJohn Johansen } 69cdff2642SJohn Johansen 70cdff2642SJohn Johansen /** 713b0aaf58SJohn Johansen * skipn_spaces - Removes leading whitespace from @str. 723b0aaf58SJohn Johansen * @str: The string to be stripped. 733b0aaf58SJohn Johansen * 743b0aaf58SJohn Johansen * Returns a pointer to the first non-whitespace character in @str. 753b0aaf58SJohn Johansen * if all whitespace will return NULL 763b0aaf58SJohn Johansen */ 773b0aaf58SJohn Johansen 78b91deb9dSJohn Johansen const char *skipn_spaces(const char *str, size_t n) 793b0aaf58SJohn Johansen { 803b0aaf58SJohn Johansen for (; n && isspace(*str); --n) 813b0aaf58SJohn Johansen ++str; 823b0aaf58SJohn Johansen if (n) 833b0aaf58SJohn Johansen return (char *)str; 843b0aaf58SJohn Johansen return NULL; 853b0aaf58SJohn Johansen } 863b0aaf58SJohn Johansen 873b0aaf58SJohn Johansen const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, 883b0aaf58SJohn Johansen size_t *ns_len) 893b0aaf58SJohn Johansen { 903b0aaf58SJohn Johansen const char *end = fqname + n; 913b0aaf58SJohn Johansen const char *name = skipn_spaces(fqname, n); 923b0aaf58SJohn Johansen 933b0aaf58SJohn Johansen if (!name) 943b0aaf58SJohn Johansen return NULL; 953b0aaf58SJohn Johansen *ns_name = NULL; 963b0aaf58SJohn Johansen *ns_len = 0; 973b0aaf58SJohn Johansen if (name[0] == ':') { 983b0aaf58SJohn Johansen char *split = strnchr(&name[1], end - &name[1], ':'); 993b0aaf58SJohn Johansen *ns_name = skipn_spaces(&name[1], end - &name[1]); 1003b0aaf58SJohn Johansen if (!*ns_name) 1013b0aaf58SJohn Johansen return NULL; 1023b0aaf58SJohn Johansen if (split) { 1033b0aaf58SJohn Johansen *ns_len = split - *ns_name; 1043b0aaf58SJohn Johansen if (*ns_len == 0) 1053b0aaf58SJohn Johansen *ns_name = NULL; 1063b0aaf58SJohn Johansen split++; 1073b0aaf58SJohn Johansen if (end - split > 1 && strncmp(split, "//", 2) == 0) 1083b0aaf58SJohn Johansen split += 2; 1093b0aaf58SJohn Johansen name = skipn_spaces(split, end - split); 1103b0aaf58SJohn Johansen } else { 1113b0aaf58SJohn Johansen /* a ns name without a following profile is allowed */ 1123b0aaf58SJohn Johansen name = NULL; 1133b0aaf58SJohn Johansen *ns_len = end - *ns_name; 1143b0aaf58SJohn Johansen } 1153b0aaf58SJohn Johansen } 1163b0aaf58SJohn Johansen if (name && *name == 0) 1173b0aaf58SJohn Johansen name = NULL; 1183b0aaf58SJohn Johansen 1193b0aaf58SJohn Johansen return name; 1203b0aaf58SJohn Johansen } 1213b0aaf58SJohn Johansen 1223b0aaf58SJohn Johansen /** 123cdff2642SJohn Johansen * aa_info_message - log a none profile related status message 124cdff2642SJohn Johansen * @str: message to log 125cdff2642SJohn Johansen */ 126cdff2642SJohn Johansen void aa_info_message(const char *str) 127cdff2642SJohn Johansen { 128cdff2642SJohn Johansen if (audit_enabled) { 129ef88a7acSJohn Johansen DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL); 130ef88a7acSJohn Johansen 131ef88a7acSJohn Johansen aad(&sa)->info = str; 132cdff2642SJohn Johansen aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); 133cdff2642SJohn Johansen } 134cdff2642SJohn Johansen printk(KERN_INFO "AppArmor: %s\n", str); 135cdff2642SJohn Johansen } 136cdff2642SJohn Johansen 137*a1bd627bSJohn Johansen __counted char *aa_str_alloc(int size, gfp_t gfp) 138*a1bd627bSJohn Johansen { 139*a1bd627bSJohn Johansen struct counted_str *str; 140*a1bd627bSJohn Johansen 141*a1bd627bSJohn Johansen str = kmalloc(sizeof(struct counted_str) + size, gfp); 142*a1bd627bSJohn Johansen if (!str) 143*a1bd627bSJohn Johansen return NULL; 144*a1bd627bSJohn Johansen 145*a1bd627bSJohn Johansen kref_init(&str->count); 146*a1bd627bSJohn Johansen return str->name; 147*a1bd627bSJohn Johansen } 148*a1bd627bSJohn Johansen 149*a1bd627bSJohn Johansen void aa_str_kref(struct kref *kref) 150*a1bd627bSJohn Johansen { 151*a1bd627bSJohn Johansen kfree(container_of(kref, struct counted_str, count)); 152*a1bd627bSJohn Johansen } 153*a1bd627bSJohn Johansen 154*a1bd627bSJohn Johansen 155e53cfe6cSJohn Johansen const char aa_file_perm_chrs[] = "xwracd km l "; 156e53cfe6cSJohn Johansen const char *aa_file_perm_names[] = { 157e53cfe6cSJohn Johansen "exec", 158e53cfe6cSJohn Johansen "write", 159e53cfe6cSJohn Johansen "read", 160e53cfe6cSJohn Johansen "append", 161e53cfe6cSJohn Johansen 162e53cfe6cSJohn Johansen "create", 163e53cfe6cSJohn Johansen "delete", 164e53cfe6cSJohn Johansen "open", 165e53cfe6cSJohn Johansen "rename", 166e53cfe6cSJohn Johansen 167e53cfe6cSJohn Johansen "setattr", 168e53cfe6cSJohn Johansen "getattr", 169e53cfe6cSJohn Johansen "setcred", 170e53cfe6cSJohn Johansen "getcred", 171e53cfe6cSJohn Johansen 172e53cfe6cSJohn Johansen "chmod", 173e53cfe6cSJohn Johansen "chown", 174e53cfe6cSJohn Johansen "chgrp", 175e53cfe6cSJohn Johansen "lock", 176e53cfe6cSJohn Johansen 177e53cfe6cSJohn Johansen "mmap", 178e53cfe6cSJohn Johansen "mprot", 179e53cfe6cSJohn Johansen "link", 180e53cfe6cSJohn Johansen "snapshot", 181e53cfe6cSJohn Johansen 182e53cfe6cSJohn Johansen "unknown", 183e53cfe6cSJohn Johansen "unknown", 184e53cfe6cSJohn Johansen "unknown", 185e53cfe6cSJohn Johansen "unknown", 186e53cfe6cSJohn Johansen 187e53cfe6cSJohn Johansen "unknown", 188e53cfe6cSJohn Johansen "unknown", 189e53cfe6cSJohn Johansen "unknown", 190e53cfe6cSJohn Johansen "unknown", 191e53cfe6cSJohn Johansen 192e53cfe6cSJohn Johansen "stack", 193e53cfe6cSJohn Johansen "change_onexec", 194e53cfe6cSJohn Johansen "change_profile", 195e53cfe6cSJohn Johansen "change_hat", 196e53cfe6cSJohn Johansen }; 197e53cfe6cSJohn Johansen 198e53cfe6cSJohn Johansen /** 199e53cfe6cSJohn Johansen * aa_perm_mask_to_str - convert a perm mask to its short string 200e53cfe6cSJohn Johansen * @str: character buffer to store string in (at least 10 characters) 201e53cfe6cSJohn Johansen * @mask: permission mask to convert 202e53cfe6cSJohn Johansen */ 203e53cfe6cSJohn Johansen void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask) 204e53cfe6cSJohn Johansen { 205e53cfe6cSJohn Johansen unsigned int i, perm = 1; 206e53cfe6cSJohn Johansen 207e53cfe6cSJohn Johansen for (i = 0; i < 32; perm <<= 1, i++) { 208e53cfe6cSJohn Johansen if (mask & perm) 209e53cfe6cSJohn Johansen *str++ = chrs[i]; 210e53cfe6cSJohn Johansen } 211e53cfe6cSJohn Johansen *str = '\0'; 212e53cfe6cSJohn Johansen } 213e53cfe6cSJohn Johansen 214aa9aeea8SJohn Johansen void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask) 215aa9aeea8SJohn Johansen { 216aa9aeea8SJohn Johansen const char *fmt = "%s"; 217aa9aeea8SJohn Johansen unsigned int i, perm = 1; 218aa9aeea8SJohn Johansen bool prev = false; 219aa9aeea8SJohn Johansen 220aa9aeea8SJohn Johansen for (i = 0; i < 32; perm <<= 1, i++) { 221aa9aeea8SJohn Johansen if (mask & perm) { 222aa9aeea8SJohn Johansen audit_log_format(ab, fmt, names[i]); 223aa9aeea8SJohn Johansen if (!prev) { 224aa9aeea8SJohn Johansen prev = true; 225aa9aeea8SJohn Johansen fmt = " %s"; 226aa9aeea8SJohn Johansen } 227aa9aeea8SJohn Johansen } 228aa9aeea8SJohn Johansen } 229aa9aeea8SJohn Johansen } 230aa9aeea8SJohn Johansen 231aa9aeea8SJohn Johansen void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, 232aa9aeea8SJohn Johansen u32 chrsmask, const char **names, u32 namesmask) 233aa9aeea8SJohn Johansen { 234aa9aeea8SJohn Johansen char str[33]; 235aa9aeea8SJohn Johansen 236aa9aeea8SJohn Johansen audit_log_format(ab, "\""); 237aa9aeea8SJohn Johansen if ((mask & chrsmask) && chrs) { 238aa9aeea8SJohn Johansen aa_perm_mask_to_str(str, chrs, mask & chrsmask); 239aa9aeea8SJohn Johansen mask &= ~chrsmask; 240aa9aeea8SJohn Johansen audit_log_format(ab, "%s", str); 241aa9aeea8SJohn Johansen if (mask & namesmask) 242aa9aeea8SJohn Johansen audit_log_format(ab, " "); 243aa9aeea8SJohn Johansen } 244aa9aeea8SJohn Johansen if ((mask & namesmask) && names) 245aa9aeea8SJohn Johansen aa_audit_perm_names(ab, names, mask & namesmask); 246aa9aeea8SJohn Johansen audit_log_format(ab, "\""); 247aa9aeea8SJohn Johansen } 248aa9aeea8SJohn Johansen 249aa9aeea8SJohn Johansen /** 250aa9aeea8SJohn Johansen * aa_apply_modes_to_perms - apply namespace and profile flags to perms 251aa9aeea8SJohn Johansen * @profile: that perms where computed from 252aa9aeea8SJohn Johansen * @perms: perms to apply mode modifiers to 253aa9aeea8SJohn Johansen * 254aa9aeea8SJohn Johansen * TODO: split into profile and ns based flags for when accumulating perms 255aa9aeea8SJohn Johansen */ 256aa9aeea8SJohn Johansen void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms) 257aa9aeea8SJohn Johansen { 258aa9aeea8SJohn Johansen switch (AUDIT_MODE(profile)) { 259aa9aeea8SJohn Johansen case AUDIT_ALL: 260aa9aeea8SJohn Johansen perms->audit = ALL_PERMS_MASK; 261aa9aeea8SJohn Johansen /* fall through */ 262aa9aeea8SJohn Johansen case AUDIT_NOQUIET: 263aa9aeea8SJohn Johansen perms->quiet = 0; 264aa9aeea8SJohn Johansen break; 265aa9aeea8SJohn Johansen case AUDIT_QUIET: 266aa9aeea8SJohn Johansen perms->audit = 0; 267aa9aeea8SJohn Johansen /* fall through */ 268aa9aeea8SJohn Johansen case AUDIT_QUIET_DENIED: 269aa9aeea8SJohn Johansen perms->quiet = ALL_PERMS_MASK; 270aa9aeea8SJohn Johansen break; 271aa9aeea8SJohn Johansen } 272aa9aeea8SJohn Johansen 273aa9aeea8SJohn Johansen if (KILL_MODE(profile)) 274aa9aeea8SJohn Johansen perms->kill = ALL_PERMS_MASK; 275aa9aeea8SJohn Johansen else if (COMPLAIN_MODE(profile)) 276aa9aeea8SJohn Johansen perms->complain = ALL_PERMS_MASK; 277aa9aeea8SJohn Johansen /* 278aa9aeea8SJohn Johansen * TODO: 279aa9aeea8SJohn Johansen * else if (PROMPT_MODE(profile)) 280aa9aeea8SJohn Johansen * perms->prompt = ALL_PERMS_MASK; 281aa9aeea8SJohn Johansen */ 282aa9aeea8SJohn Johansen } 283aa9aeea8SJohn Johansen 284aa9aeea8SJohn Johansen static u32 map_other(u32 x) 285aa9aeea8SJohn Johansen { 286aa9aeea8SJohn Johansen return ((x & 0x3) << 8) | /* SETATTR/GETATTR */ 287aa9aeea8SJohn Johansen ((x & 0x1c) << 18) | /* ACCEPT/BIND/LISTEN */ 288aa9aeea8SJohn Johansen ((x & 0x60) << 19); /* SETOPT/GETOPT */ 289aa9aeea8SJohn Johansen } 290aa9aeea8SJohn Johansen 291aa9aeea8SJohn Johansen void aa_compute_perms(struct aa_dfa *dfa, unsigned int state, 292aa9aeea8SJohn Johansen struct aa_perms *perms) 293aa9aeea8SJohn Johansen { 294aa9aeea8SJohn Johansen perms->deny = 0; 295aa9aeea8SJohn Johansen perms->kill = perms->stop = 0; 296aa9aeea8SJohn Johansen perms->complain = perms->cond = 0; 297aa9aeea8SJohn Johansen perms->hide = 0; 298aa9aeea8SJohn Johansen perms->prompt = 0; 299aa9aeea8SJohn Johansen perms->allow = dfa_user_allow(dfa, state); 300aa9aeea8SJohn Johansen perms->audit = dfa_user_audit(dfa, state); 301aa9aeea8SJohn Johansen perms->quiet = dfa_user_quiet(dfa, state); 302aa9aeea8SJohn Johansen 303aa9aeea8SJohn Johansen /* for v5 perm mapping in the policydb, the other set is used 304aa9aeea8SJohn Johansen * to extend the general perm set 305aa9aeea8SJohn Johansen */ 306aa9aeea8SJohn Johansen perms->allow |= map_other(dfa_other_allow(dfa, state)); 307aa9aeea8SJohn Johansen perms->audit |= map_other(dfa_other_audit(dfa, state)); 308aa9aeea8SJohn Johansen perms->quiet |= map_other(dfa_other_quiet(dfa, state)); 309aa9aeea8SJohn Johansen // perms->xindex = dfa_user_xindex(dfa, state); 310aa9aeea8SJohn Johansen } 311aa9aeea8SJohn Johansen 312cdff2642SJohn Johansen /** 313fe6bb31fSJohn Johansen * aa_policy_init - initialize a policy structure 314fe6bb31fSJohn Johansen * @policy: policy to initialize (NOT NULL) 315fe6bb31fSJohn Johansen * @prefix: prefix name if any is required. (MAYBE NULL) 316fe6bb31fSJohn Johansen * @name: name of the policy, init will make a copy of it (NOT NULL) 317*a1bd627bSJohn Johansen * @gfp: allocation mode 318fe6bb31fSJohn Johansen * 319fe6bb31fSJohn Johansen * Note: this fn creates a copy of strings passed in 320fe6bb31fSJohn Johansen * 321fe6bb31fSJohn Johansen * Returns: true if policy init successful 322fe6bb31fSJohn Johansen */ 323fe6bb31fSJohn Johansen bool aa_policy_init(struct aa_policy *policy, const char *prefix, 324d102d895SJohn Johansen const char *name, gfp_t gfp) 325fe6bb31fSJohn Johansen { 326*a1bd627bSJohn Johansen char *hname; 327*a1bd627bSJohn Johansen 328fe6bb31fSJohn Johansen /* freed by policy_free */ 329fe6bb31fSJohn Johansen if (prefix) { 330*a1bd627bSJohn Johansen hname = aa_str_alloc(strlen(prefix) + strlen(name) + 3, gfp); 331*a1bd627bSJohn Johansen if (hname) 332*a1bd627bSJohn Johansen sprintf(hname, "%s//%s", prefix, name); 333*a1bd627bSJohn Johansen } else { 334*a1bd627bSJohn Johansen hname = aa_str_alloc(strlen(name) + 1, gfp); 335*a1bd627bSJohn Johansen if (hname) 336*a1bd627bSJohn Johansen strcpy(hname, name); 337*a1bd627bSJohn Johansen } 338*a1bd627bSJohn Johansen if (!hname) 339b9c42ac7Skbuild test robot return false; 340*a1bd627bSJohn Johansen policy->hname = hname; 341fe6bb31fSJohn Johansen /* base.name is a substring of fqname */ 342d102d895SJohn Johansen policy->name = basename(policy->hname); 343fe6bb31fSJohn Johansen INIT_LIST_HEAD(&policy->list); 344fe6bb31fSJohn Johansen INIT_LIST_HEAD(&policy->profiles); 345fe6bb31fSJohn Johansen 346b9c42ac7Skbuild test robot return true; 347fe6bb31fSJohn Johansen } 348fe6bb31fSJohn Johansen 349fe6bb31fSJohn Johansen /** 350fe6bb31fSJohn Johansen * aa_policy_destroy - free the elements referenced by @policy 351fe6bb31fSJohn Johansen * @policy: policy that is to have its elements freed (NOT NULL) 352fe6bb31fSJohn Johansen */ 353fe6bb31fSJohn Johansen void aa_policy_destroy(struct aa_policy *policy) 354fe6bb31fSJohn Johansen { 3555fd1b95fSJohn Johansen AA_BUG(on_list_rcu(&policy->profiles)); 3565fd1b95fSJohn Johansen AA_BUG(on_list_rcu(&policy->list)); 357fe6bb31fSJohn Johansen 358fe6bb31fSJohn Johansen /* don't free name as its a subset of hname */ 359*a1bd627bSJohn Johansen aa_put_str(policy->hname); 360fe6bb31fSJohn Johansen } 361