/** * aa_create_aafs - create the apparmor security filesystem * * dentries created here are released by aa_destroy_aafs * * Returns: error on failure */ static int __init aa_create_aafs(void) { int error; if (!apparmor_initialized) return 0; if (aa_fs_dentry) { AA_ERROR("%s: AppArmor securityfs already exists\n", __func__); return -EEXIST; } aa_fs_dentry = securityfs_create_dir("apparmor", NULL); if (IS_ERR(aa_fs_dentry)) { error = PTR_ERR(aa_fs_dentry); aa_fs_dentry = NULL; goto error; } #ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 error = aafs_create("matching", 0444, &aa_fs_matching_fops); if (error) goto error; error = aafs_create("features", 0444, &aa_fs_features_fops); if (error) goto error; #endif error = aafs_create("profiles", 0440, &aa_fs_profiles_fops); if (error) goto error; error = aafs_create(".load", 0640, &aa_fs_profile_load); if (error) goto error; error = aafs_create(".replace", 0640, &aa_fs_profile_replace); if (error) goto error; error = aafs_create(".remove", 0640, &aa_fs_profile_remove); if (error) goto error; /* TODO: add support for apparmorfs_null and apparmorfs_mnt */ /* Report that AppArmor fs is enabled */ aa_info_message("AppArmor Filesystem Enabled"); return 0; error: aa_destroy_aafs(); AA_ERROR("Error creating AppArmor securityfs\n"); return error; }
static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name, struct dentry *dir) { struct aa_ns *ns; int error; AA_BUG(!parent); AA_BUG(!name); AA_BUG(!mutex_is_locked(&parent->lock)); ns = alloc_ns(parent->base.hname, name); if (!ns) return NULL; mutex_lock(&ns->lock); error = __aa_fs_ns_mkdir(ns, ns_subns_dir(parent), name); if (error) { AA_ERROR("Failed to create interface for ns %s\n", ns->base.name); mutex_unlock(&ns->lock); aa_free_ns(ns); return ERR_PTR(error); } ns->parent = aa_get_ns(parent); ns->level = parent->level + 1; list_add_rcu(&ns->base.list, &parent->sub_ns); /* add list ref */ aa_get_ns(ns); mutex_unlock(&ns->lock); return ns; }
/** * policy_destroy - free the elements referenced by @policy * @policy: policy that is to have its elements freed (NOT NULL) */ static void policy_destroy(struct aa_policy *policy) { /* still contains profiles -- invalid */ if (!list_empty(&policy->profiles)) { AA_ERROR("%s: internal error, " "policy '%s' still contains profiles\n", __func__, policy->name); BUG(); } if (!list_empty(&policy->list)) { AA_ERROR("%s: internal error, policy '%s' still on list\n", __func__, policy->name); BUG(); } /* don't free name as its a subset of hname */ kzfree(policy->hname); }
static void policy_destroy(struct aa_policy *policy) { if (!list_empty(&policy->profiles)) { AA_ERROR("%s: internal error, " "policy '%s' still contains profiles\n", __func__, policy->name); BUG(); } if (!list_empty(&policy->list)) { AA_ERROR("%s: internal error, policy '%s' still on list\n", __func__, policy->name); BUG(); } kzfree(policy->hname); }
/** * split_token_from_name - separate a string of form <token>^<name> * @op: operation being checked * @args: string to parse (NOT NULL) * @token: stores returned parsed token value (NOT NULL) * * Returns: start position of name after token else NULL on failure */ static char *split_token_from_name(int op, char *args, u64 * token) { char *name; *token = simple_strtoull(args, &name, 16); if ((name == args) || *name != '^') { AA_ERROR("%s: Invalid input '%s'", op_table[op], args); return ERR_PTR(-EINVAL); } name++; /* skip ^ */ if (!*name) name = NULL; return name; }
static int __init init_profile_hash(void) { struct crypto_shash *tfm; if (!apparmor_initialized) return 0; tfm = crypto_alloc_shash("sha1", 0, 0); if (IS_ERR(tfm)) { int error = PTR_ERR(tfm); AA_ERROR("failed to setup profile sha1 hashing: %d\n", error); return error; } apparmor_tfm = tfm; apparmor_hash_size = crypto_shash_digestsize(apparmor_tfm); aa_info_message("AppArmor sha1 policy hashing enabled"); return 0; }
/** * aa_setprocattr_chagnehat - handle procattr interface to change_hat * @args: args received from writing to /proc/<pid>/attr/current (NOT NULL) * @size: size of the args * @test: true if this is a test of change_hat permissions * * Returns: %0 or error code if change_hat fails */ int aa_setprocattr_changehat(char *args, size_t size, int test) { char *hat; u64 token; const char *hats[16]; /* current hard limit on # of names */ int count = 0; hat = split_token_from_name(OP_CHANGE_HAT, args, &token); if (IS_ERR(hat)) return PTR_ERR(hat); if (!hat && !token) { AA_ERROR("change_hat: Invalid input, NULL hat and NULL magic"); return -EINVAL; } if (hat) { /* set up hat name vector, args guaranteed null terminated * at args[size] by setprocattr. * * If there are multiple hat names in the buffer each is * separated by a \0. Ie. userspace writes them pre tokenized */ char *end = args + size; for (count = 0; (hat < end) && count < 16; ++count) { char *next = hat + strlen(hat) + 1; hats[count] = hat; hat = next; } } AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n", __func__, token, hat ? hat : NULL); return aa_change_hat(hats, count, token, test); }
/** * aa_audit_base - core AppArmor function. * @type: type of audit message (see include/linux/apparmor.h) * @profile: active profile for event (MAY BE NULL) * @sa: audit structure containing data to audit * @audit_cxt: audit_cxt that event is under * @cb: audit cb for this event * * Record an audit message for data is @sa, and handle deal with kill and * complain messages switches. * * Returns: 0 or sa->error on success, else error */ static int aa_audit_base(int type, struct aa_profile *profile, struct aa_audit *sa, struct audit_context *audit_cxt, void (*cb) (struct audit_buffer *, struct aa_audit *)) { struct audit_buffer *ab = NULL; struct task_struct *task = sa->task ? sa->task : current; if (profile && DO_KILL(profile) && type == AUDIT_APPARMOR_DENIED) type = AUDIT_APPARMOR_KILL; /* ab freed below in audit_log_end */ ab = audit_log_start(audit_cxt, sa->gfp_mask, type); if (!ab) { AA_ERROR("(%d) Unable to log event of type (%d)\n", -ENOMEM, type); sa->error = -ENOMEM; goto out; } if (aa_g_audit_header) { audit_log_format(ab, " type="); audit_log_string(ab, aa_audit_type[type - AUDIT_APPARMOR_AUDIT]); } if (sa->operation) { audit_log_format(ab, " operation="); audit_log_string(ab, sa->operation); } if (sa->info) { audit_log_format(ab, " info="); audit_log_string(ab, sa->info); if (sa->error) audit_log_format(ab, " error=%d", sa->error); } audit_log_format(ab, " pid=%d", task->pid); if (profile && !unconfined(profile)) { pid_t pid; rcu_read_lock(); pid = task->real_parent->pid; rcu_read_unlock(); audit_log_format(ab, " parent=%d", pid); audit_log_format(ab, " profile="); audit_log_untrustedstring(ab, profile->base.hname); if (profile->ns != root_ns) { audit_log_format(ab, " namespace="); audit_log_untrustedstring(ab, profile->ns->base.hname); } } if (cb) cb(ab, sa); audit_log_end(ab); out: if (type == AUDIT_APPARMOR_KILL) (void)send_sig_info(SIGKILL, NULL, task); return type == AUDIT_APPARMOR_ALLOWED ? 0 : sa->error; }