Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

AppArmor: userspace interfaces

The /proc/<pid>/attr/* interface is used for process introspection and
commands. While the apparmorfs interface is used for global introspection
and loading and removing policy.

The interface currently only contains the files necessary for loading
policy, and will be extended in the future to include sysfs style
single per file introspection inteface.

The old AppArmor 2.4 interface files have been removed into a compatibility
patch, that distros can use to maintain backwards compatibility.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Signed-off-by: James Morris <jmorris@namei.org>

authored by

John Johansen and committed by
James Morris
63e2b423 e06f75a6

+455
+239
security/apparmor/apparmorfs.c
··· 1 + /* 2 + * AppArmor security module 3 + * 4 + * This file contains AppArmor /sys/kernel/security/apparmor interface functions 5 + * 6 + * Copyright (C) 1998-2008 Novell/SUSE 7 + * Copyright 2009-2010 Canonical Ltd. 8 + * 9 + * This program is free software; you can redistribute it and/or 10 + * modify it under the terms of the GNU General Public License as 11 + * published by the Free Software Foundation, version 2 of the 12 + * License. 13 + */ 14 + 15 + #include <linux/security.h> 16 + #include <linux/vmalloc.h> 17 + #include <linux/module.h> 18 + #include <linux/seq_file.h> 19 + #include <linux/uaccess.h> 20 + #include <linux/namei.h> 21 + 22 + #include "include/apparmor.h" 23 + #include "include/apparmorfs.h" 24 + #include "include/audit.h" 25 + #include "include/context.h" 26 + #include "include/policy.h" 27 + 28 + /** 29 + * aa_simple_write_to_buffer - common routine for getting policy from user 30 + * @op: operation doing the user buffer copy 31 + * @userbuf: user buffer to copy data from (NOT NULL) 32 + * @alloc_size: size of user buffer 33 + * @copy_size: size of data to copy from user buffer 34 + * @pos: position write is at in the file (NOT NULL) 35 + * 36 + * Returns: kernel buffer containing copy of user buffer data or an 37 + * ERR_PTR on failure. 38 + */ 39 + static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, 40 + size_t alloc_size, size_t copy_size, 41 + loff_t *pos) 42 + { 43 + char *data; 44 + 45 + if (*pos != 0) 46 + /* only writes from pos 0, that is complete writes */ 47 + return ERR_PTR(-ESPIPE); 48 + 49 + /* 50 + * Don't allow profile load/replace/remove from profiles that don't 51 + * have CAP_MAC_ADMIN 52 + */ 53 + if (!aa_may_manage_policy(op)) 54 + return ERR_PTR(-EACCES); 55 + 56 + /* freed by caller to simple_write_to_buffer */ 57 + data = kvmalloc(alloc_size); 58 + if (data == NULL) 59 + return ERR_PTR(-ENOMEM); 60 + 61 + if (copy_from_user(data, userbuf, copy_size)) { 62 + kvfree(data); 63 + return ERR_PTR(-EFAULT); 64 + } 65 + 66 + return data; 67 + } 68 + 69 + 70 + /* .load file hook fn to load policy */ 71 + static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, 72 + loff_t *pos) 73 + { 74 + char *data; 75 + ssize_t error; 76 + 77 + data = aa_simple_write_to_buffer(OP_PROF_LOAD, buf, size, size, pos); 78 + 79 + error = PTR_ERR(data); 80 + if (!IS_ERR(data)) { 81 + error = aa_replace_profiles(data, size, PROF_ADD); 82 + kvfree(data); 83 + } 84 + 85 + return error; 86 + } 87 + 88 + static const struct file_operations aa_fs_profile_load = { 89 + .write = profile_load 90 + }; 91 + 92 + /* .replace file hook fn to load and/or replace policy */ 93 + static ssize_t profile_replace(struct file *f, const char __user *buf, 94 + size_t size, loff_t *pos) 95 + { 96 + char *data; 97 + ssize_t error; 98 + 99 + data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos); 100 + error = PTR_ERR(data); 101 + if (!IS_ERR(data)) { 102 + error = aa_replace_profiles(data, size, PROF_REPLACE); 103 + kvfree(data); 104 + } 105 + 106 + return error; 107 + } 108 + 109 + static const struct file_operations aa_fs_profile_replace = { 110 + .write = profile_replace 111 + }; 112 + 113 + /* .remove file hook fn to remove loaded policy */ 114 + static ssize_t profile_remove(struct file *f, const char __user *buf, 115 + size_t size, loff_t *pos) 116 + { 117 + char *data; 118 + ssize_t error; 119 + 120 + /* 121 + * aa_remove_profile needs a null terminated string so 1 extra 122 + * byte is allocated and the copied data is null terminated. 123 + */ 124 + data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, pos); 125 + 126 + error = PTR_ERR(data); 127 + if (!IS_ERR(data)) { 128 + data[size] = 0; 129 + error = aa_remove_profiles(data, size); 130 + kvfree(data); 131 + } 132 + 133 + return error; 134 + } 135 + 136 + static const struct file_operations aa_fs_profile_remove = { 137 + .write = profile_remove 138 + }; 139 + 140 + /** Base file system setup **/ 141 + 142 + static struct dentry *aa_fs_dentry __initdata; 143 + 144 + static void __init aafs_remove(const char *name) 145 + { 146 + struct dentry *dentry; 147 + 148 + dentry = lookup_one_len(name, aa_fs_dentry, strlen(name)); 149 + if (!IS_ERR(dentry)) { 150 + securityfs_remove(dentry); 151 + dput(dentry); 152 + } 153 + } 154 + 155 + /** 156 + * aafs_create - create an entry in the apparmor filesystem 157 + * @name: name of the entry (NOT NULL) 158 + * @mask: file permission mask of the file 159 + * @fops: file operations for the file (NOT NULL) 160 + * 161 + * Used aafs_remove to remove entries created with this fn. 162 + */ 163 + static int __init aafs_create(const char *name, int mask, 164 + const struct file_operations *fops) 165 + { 166 + struct dentry *dentry; 167 + 168 + dentry = securityfs_create_file(name, S_IFREG | mask, aa_fs_dentry, 169 + NULL, fops); 170 + 171 + return IS_ERR(dentry) ? PTR_ERR(dentry) : 0; 172 + } 173 + 174 + /** 175 + * aa_destroy_aafs - cleanup and free aafs 176 + * 177 + * releases dentries allocated by aa_create_aafs 178 + */ 179 + void __init aa_destroy_aafs(void) 180 + { 181 + if (aa_fs_dentry) { 182 + aafs_remove(".remove"); 183 + aafs_remove(".replace"); 184 + aafs_remove(".load"); 185 + 186 + securityfs_remove(aa_fs_dentry); 187 + aa_fs_dentry = NULL; 188 + } 189 + } 190 + 191 + /** 192 + * aa_create_aafs - create the apparmor security filesystem 193 + * 194 + * dentries created here are released by aa_destroy_aafs 195 + * 196 + * Returns: error on failure 197 + */ 198 + int __init aa_create_aafs(void) 199 + { 200 + int error; 201 + 202 + if (!apparmor_initialized) 203 + return 0; 204 + 205 + if (aa_fs_dentry) { 206 + AA_ERROR("%s: AppArmor securityfs already exists\n", __func__); 207 + return -EEXIST; 208 + } 209 + 210 + aa_fs_dentry = securityfs_create_dir("apparmor", NULL); 211 + if (IS_ERR(aa_fs_dentry)) { 212 + error = PTR_ERR(aa_fs_dentry); 213 + aa_fs_dentry = NULL; 214 + goto error; 215 + } 216 + 217 + error = aafs_create(".load", 0640, &aa_fs_profile_load); 218 + if (error) 219 + goto error; 220 + error = aafs_create(".replace", 0640, &aa_fs_profile_replace); 221 + if (error) 222 + goto error; 223 + error = aafs_create(".remove", 0640, &aa_fs_profile_remove); 224 + if (error) 225 + goto error; 226 + 227 + /* TODO: add support for apparmorfs_null and apparmorfs_mnt */ 228 + 229 + /* Report that AppArmor fs is enabled */ 230 + aa_info_message("AppArmor Filesystem Enabled"); 231 + return 0; 232 + 233 + error: 234 + aa_destroy_aafs(); 235 + AA_ERROR("Error creating AppArmor securityfs\n"); 236 + return error; 237 + } 238 + 239 + fs_initcall(aa_create_aafs);
+20
security/apparmor/include/apparmorfs.h
··· 1 + /* 2 + * AppArmor security module 3 + * 4 + * This file contains AppArmor filesystem definitions. 5 + * 6 + * Copyright (C) 1998-2008 Novell/SUSE 7 + * Copyright 2009-2010 Canonical Ltd. 8 + * 9 + * This program is free software; you can redistribute it and/or 10 + * modify it under the terms of the GNU General Public License as 11 + * published by the Free Software Foundation, version 2 of the 12 + * License. 13 + */ 14 + 15 + #ifndef __AA_APPARMORFS_H 16 + #define __AA_APPARMORFS_H 17 + 18 + extern void __init aa_destroy_aafs(void); 19 + 20 + #endif /* __AA_APPARMORFS_H */
+26
security/apparmor/include/procattr.h
··· 1 + /* 2 + * AppArmor security module 3 + * 4 + * This file contains AppArmor /proc/<pid>/attr/ interface function definitions. 5 + * 6 + * Copyright (C) 1998-2008 Novell/SUSE 7 + * Copyright 2009-2010 Canonical Ltd. 8 + * 9 + * This program is free software; you can redistribute it and/or 10 + * modify it under the terms of the GNU General Public License as 11 + * published by the Free Software Foundation, version 2 of the 12 + * License. 13 + */ 14 + 15 + #ifndef __AA_PROCATTR_H 16 + #define __AA_PROCATTR_H 17 + 18 + #define AA_DO_TEST 1 19 + #define AA_ONEXEC 1 20 + 21 + int aa_getprocattr(struct aa_profile *profile, char **string); 22 + int aa_setprocattr_changehat(char *args, size_t size, int test); 23 + int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test); 24 + int aa_setprocattr_permipc(char *fqname); 25 + 26 + #endif /* __AA_PROCATTR_H */
+170
security/apparmor/procattr.c
··· 1 + /* 2 + * AppArmor security module 3 + * 4 + * This file contains AppArmor /proc/<pid>/attr/ interface functions 5 + * 6 + * Copyright (C) 1998-2008 Novell/SUSE 7 + * Copyright 2009-2010 Canonical Ltd. 8 + * 9 + * This program is free software; you can redistribute it and/or 10 + * modify it under the terms of the GNU General Public License as 11 + * published by the Free Software Foundation, version 2 of the 12 + * License. 13 + */ 14 + 15 + #include "include/apparmor.h" 16 + #include "include/context.h" 17 + #include "include/policy.h" 18 + #include "include/domain.h" 19 + 20 + 21 + /** 22 + * aa_getprocattr - Return the profile information for @profile 23 + * @profile: the profile to print profile info about (NOT NULL) 24 + * @string: Returns - string containing the profile info (NOT NULL) 25 + * 26 + * Returns: length of @string on success else error on failure 27 + * 28 + * Requires: profile != NULL 29 + * 30 + * Creates a string containing the namespace_name://profile_name for 31 + * @profile. 32 + * 33 + * Returns: size of string placed in @string else error code on failure 34 + */ 35 + int aa_getprocattr(struct aa_profile *profile, char **string) 36 + { 37 + char *str; 38 + int len = 0, mode_len = 0, ns_len = 0, name_len; 39 + const char *mode_str = profile_mode_names[profile->mode]; 40 + const char *ns_name = NULL; 41 + struct aa_namespace *ns = profile->ns; 42 + struct aa_namespace *current_ns = __aa_current_profile()->ns; 43 + char *s; 44 + 45 + if (!aa_ns_visible(current_ns, ns)) 46 + return -EACCES; 47 + 48 + ns_name = aa_ns_name(current_ns, ns); 49 + ns_len = strlen(ns_name); 50 + 51 + /* if the visible ns_name is > 0 increase size for : :// seperator */ 52 + if (ns_len) 53 + ns_len += 4; 54 + 55 + /* unconfined profiles don't have a mode string appended */ 56 + if (!unconfined(profile)) 57 + mode_len = strlen(mode_str) + 3; /* + 3 for _() */ 58 + 59 + name_len = strlen(profile->base.hname); 60 + len = mode_len + ns_len + name_len + 1; /* + 1 for \n */ 61 + s = str = kmalloc(len + 1, GFP_KERNEL); /* + 1 \0 */ 62 + if (!str) 63 + return -ENOMEM; 64 + 65 + if (ns_len) { 66 + /* skip over prefix current_ns->base.hname and separating // */ 67 + sprintf(s, ":%s://", ns_name); 68 + s += ns_len; 69 + } 70 + if (unconfined(profile)) 71 + /* mode string not being appended */ 72 + sprintf(s, "%s\n", profile->base.hname); 73 + else 74 + sprintf(s, "%s (%s)\n", profile->base.hname, mode_str); 75 + *string = str; 76 + 77 + /* NOTE: len does not include \0 of string, not saved as part of file */ 78 + return len; 79 + } 80 + 81 + /** 82 + * split_token_from_name - separate a string of form <token>^<name> 83 + * @op: operation being checked 84 + * @args: string to parse (NOT NULL) 85 + * @token: stores returned parsed token value (NOT NULL) 86 + * 87 + * Returns: start position of name after token else NULL on failure 88 + */ 89 + static char *split_token_from_name(int op, char *args, u64 * token) 90 + { 91 + char *name; 92 + 93 + *token = simple_strtoull(args, &name, 16); 94 + if ((name == args) || *name != '^') { 95 + AA_ERROR("%s: Invalid input '%s'", op_table[op], args); 96 + return ERR_PTR(-EINVAL); 97 + } 98 + 99 + name++; /* skip ^ */ 100 + if (!*name) 101 + name = NULL; 102 + return name; 103 + } 104 + 105 + /** 106 + * aa_setprocattr_chagnehat - handle procattr interface to change_hat 107 + * @args: args received from writing to /proc/<pid>/attr/current (NOT NULL) 108 + * @size: size of the args 109 + * @test: true if this is a test of change_hat permissions 110 + * 111 + * Returns: %0 or error code if change_hat fails 112 + */ 113 + int aa_setprocattr_changehat(char *args, size_t size, int test) 114 + { 115 + char *hat; 116 + u64 token; 117 + const char *hats[16]; /* current hard limit on # of names */ 118 + int count = 0; 119 + 120 + hat = split_token_from_name(OP_CHANGE_HAT, args, &token); 121 + if (IS_ERR(hat)) 122 + return PTR_ERR(hat); 123 + 124 + if (!hat && !token) { 125 + AA_ERROR("change_hat: Invalid input, NULL hat and NULL magic"); 126 + return -EINVAL; 127 + } 128 + 129 + if (hat) { 130 + /* set up hat name vector, args guaranteed null terminated 131 + * at args[size] by setprocattr. 132 + * 133 + * If there are multiple hat names in the buffer each is 134 + * separated by a \0. Ie. userspace writes them pre tokenized 135 + */ 136 + char *end = args + size; 137 + for (count = 0; (hat < end) && count < 16; ++count) { 138 + char *next = hat + strlen(hat) + 1; 139 + hats[count] = hat; 140 + hat = next; 141 + } 142 + } 143 + 144 + AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n", 145 + __func__, token, hat ? hat : NULL); 146 + 147 + return aa_change_hat(hats, count, token, test); 148 + } 149 + 150 + /** 151 + * aa_setprocattr_changeprofile - handle procattr interface to changeprofile 152 + * @fqname: args received from writting to /proc/<pid>/attr/current (NOT NULL) 153 + * @onexec: true if change_profile should be delayed until exec 154 + * @test: true if this is a test of change_profile permissions 155 + * 156 + * Returns: %0 or error code if change_profile fails 157 + */ 158 + int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test) 159 + { 160 + char *name, *ns_name; 161 + 162 + name = aa_split_fqname(fqname, &ns_name); 163 + return aa_change_profile(ns_name, name, onexec, test); 164 + } 165 + 166 + int aa_setprocattr_permipc(char *fqname) 167 + { 168 + /* TODO: add ipc permission querying */ 169 + return -ENOTSUPP; 170 + }