diff options
author | John Johansen <john.johansen@canonical.com> | 2010-07-23 04:57:23 -0700 |
---|---|---|
committer | Leann Ogasawara <leann.ogasawara@canonical.com> | 2010-08-11 07:43:10 -0700 |
commit | 23a79fa7d4143b6b270259c19b64d29e9dfc3873 (patch) | |
tree | 1554b84a39ffeb3f0a682c1ff0b0c9b15bcddd8e /security | |
parent | 3bf4c3a4f65a41062623d9cd50e9ed9f36beebe3 (diff) |
UBUNTU: SAUCE: AppArmor: -- sync to AppArmor mainline 2010-07-27
Sync AppArmor mainline upstreaming version 2010-06-27
contains fixes for
BugLink: http://bugs.launchpad.net/bugs/599450
BugLink: http://bugs.launchpad.net/bugs/581525
Signed-off-by: John Johansen <john.johansen@canonical.com>
Signed-off-by: Leann Ogasawara <leann.ogasawara@canonical.com>
Diffstat (limited to 'security')
26 files changed, 385 insertions, 661 deletions
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 5c57df79ca0..c054cf79f1c 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -29,12 +29,3 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE bootup. If you are unsure how to answer this question, answer 1. - -config SECURITY_APPARMOR_COMPAT_24 - bool "Enable AppArmor 2.4 compatability" - depends on SECURITY_APPARMOR - default y - help - This option enables compatability with AppArmor 2.4. It is - recommended if compatability with older versions of AppArmor - is desired. diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile index e5e8968cb8b..f204869399e 100644 --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -4,23 +4,17 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ - resource.o sid.o file.o net.o - -apparmor-$(CONFIG_SECURITY_APPARMOR_COMPAT_24) += apparmorfs-24.o + resource.o sid.o file.o clean-files: capability_names.h af_names.h quiet_cmd_make-caps = GEN $@ cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ; sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ -quiet_cmd_make-af = GEN $@ -cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ; sed -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "s/^\#define[ \\t]\\+AF_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ - quiet_cmd_make-rlim = GEN $@ cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ; sed -n --e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+RLIMIT_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ ; echo "static const int rlim_map[] = {" >> $@ ; sed -n -e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+\\(RLIMIT_[A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/\\1,/p" $< >> $@ ; echo "};" >> $@ $(obj)/capability.o : $(obj)/capability_names.h -$(obj)/net.o : $(obj)/af_names.h $(obj)/resource.o : $(obj)/rlim_names.h $(obj)/capability_names.h : $(srctree)/include/linux/capability.h $(call cmd,make-caps) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 5fb9385bb1f..d8a27a4c84c 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -26,51 +26,12 @@ #include "include/policy.h" /** - * kvmalloc - do allocation prefering kmalloc but falling back to vmalloc - * @size: size of allocation - * - * Return: allocated buffer or NULL if failed - * - * It is possible that policy being loaded from the user is larger than - * what can be allocated by kmalloc, in those cases fall back to vmalloc. - */ -static void *kvmalloc(size_t size) -{ - void *buffer; - - if (size == 0) - return NULL; - - buffer = kmalloc(size, GFP_KERNEL | __GFP_NOWARN); - if (!buffer) - buffer = vmalloc(size); - return buffer; -} - -/** - * kvfree - free an allocation do by kvmalloc - * @buffer: buffer to free - * - * Free a buffer allocated by kvmalloc - */ -static void kvfree(void *buffer) -{ - if (!buffer) - return; - - if (is_vmalloc_addr(buffer)) - vfree(buffer); - else - kfree(buffer); -} - -/** * aa_simple_write_to_buffer - common routine for getting policy from user * @op: operation doing the user buffer copy * @userbuf: user buffer to copy data from (NOT NULL) * @alloc_size: size of user buffer * @copy_size: size of data to copy from user buffer - * @pos: position write is at in the file + * @pos: position write is at in the file (NOT NULL) * * Returns: kernel buffer containing copy of user buffer data or an * ERR_PTR on failure. @@ -81,11 +42,9 @@ static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, { char *data; - if (*pos != 0) { + if (*pos != 0) /* only writes from pos 0, that is complete writes */ - data = ERR_PTR(-ESPIPE); - goto out; - } + return ERR_PTR(-ESPIPE); /* * Don't allow profile load/replace/remove from profiles that don't @@ -101,11 +60,9 @@ static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, if (copy_from_user(data, userbuf, copy_size)) { kvfree(data); - data = ERR_PTR(-EFAULT); - goto out; + return ERR_PTR(-EFAULT); } -out: return data; } @@ -183,8 +140,6 @@ static const struct file_operations aa_fs_profile_remove = { /** Base file system setup **/ static struct dentry *aa_fs_dentry; -struct dentry *aa_fs_null; -struct vfsmount *aa_fs_mnt; static void aafs_remove(const char *name) { @@ -199,9 +154,9 @@ static void aafs_remove(const char *name) /** * aafs_create - create an entry in the apparmor filesystem - * @name: name of the entry + * @name: name of the entry (NOT NULL) * @mask: file permission mask of the file - * @fops: file operations for the file + * @fops: file operations for the file (NOT NULL) * * Used aafs_remove to remove entries created with this fn. */ @@ -227,11 +182,7 @@ void aa_destroy_aafs(void) aafs_remove(".remove"); aafs_remove(".replace"); aafs_remove(".load"); - aafs_remove("profiles"); -#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 - aafs_remove("matching"); - aafs_remove("features"); -#endif + securityfs_remove(aa_fs_dentry); aa_fs_dentry = NULL; } @@ -262,17 +213,7 @@ int aa_create_aafs(void) 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; diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 12a86c00a7f..d0311eb71a6 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -82,20 +82,19 @@ const char *audit_mode_names[] = { }; static char *aa_audit_type[] = { - "APPARMOR_AUDIT", - "APPARMOR_ALLOWED", - "APPARMOR_DENIED", - "APPARMOR_HINT", - "APPARMOR_STATUS", - "APPARMOR_ERROR", - "APPARMOR_KILLED" + "AUDIT", + "ALLOWED", + "DENIED", + "HINT", + "STATUS", + "ERROR", + "KILLED" }; /* * Currently AppArmor auditing is fed straight into the audit framework. * * TODO: - * convert to LSM audit * netlink interface for complain mode * user auditing, - send user auditing to netlink interface * system control of whether user audit messages go to system log @@ -103,8 +102,8 @@ static char *aa_audit_type[] = { /** * audit_base - core AppArmor function. - * @ab: audit buffer to fill - * @sa: audit structure containing data to audit + * @ab: audit buffer to fill (NOT NULL) + * @ca: audit structure containing data to audit (NOT NULL) * * Record common AppArmor audit data from @sa */ @@ -114,7 +113,7 @@ static void audit_pre(struct audit_buffer *ab, void *ca) struct task_struct *tsk = sa->tsk ? sa->tsk : current; if (aa_g_audit_header) { - audit_log_format(ab, " type="); + audit_log_format(ab, "apparmor="); audit_log_string(ab, aa_audit_type[sa->aad.type]); } @@ -130,8 +129,6 @@ static void audit_pre(struct audit_buffer *ab, void *ca) audit_log_format(ab, " error=%d", sa->aad.error); } - audit_log_format(ab, " pid=%d", tsk->pid); - if (sa->aad.profile) { struct aa_profile *profile = sa->aad.profile; pid_t pid; @@ -139,12 +136,11 @@ static void audit_pre(struct audit_buffer *ab, void *ca) pid = tsk->real_parent->pid; rcu_read_unlock(); audit_log_format(ab, " parent=%d", pid); - audit_log_format(ab, " profile="); if (profile->ns != root_ns) { - audit_log_format(ab, ":"); + audit_log_format(ab, " namespace="); audit_log_untrustedstring(ab, profile->ns->base.hname); - audit_log_format(ab, "://"); } + audit_log_format(ab, " profile="); audit_log_untrustedstring(ab, profile->base.hname); } @@ -155,12 +151,26 @@ static void audit_pre(struct audit_buffer *ab, void *ca) } /** - * aa_audit - Log an audit event to the audit subsystem + * aa_audit_msg - Log a message to the audit subsystem + * @sa: audit event structure (NOT NULL) + * @cb: optional callback fn for type specific fields (MAYBE NULL) + */ +void aa_audit_msg(int type, struct common_audit_data *sa, + void (*cb) (struct audit_buffer *, void *)) +{ + sa->aad.type = type; + sa->lsm_pre_audit = audit_pre; + sa->lsm_post_audit = cb; + common_lsm_audit(sa); +} + +/** + * aa_audit - Log a profile based audit event to the audit subsystem * @type: audit type for the message - * @profile: profile to check against + * @profile: profile to check against (NOT NULL) * @gfp: allocation flags to use - * @sa: audit event - * @cb: optional callback fn for type specific fields + * @sa: audit event (NOT NULL) + * @cb: optional callback fn for type specific fields (MAYBE NULL) * * Handle default message switching based off of audit mode flags * @@ -170,6 +180,8 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, struct common_audit_data *sa, void (*cb) (struct audit_buffer *, void *)) { + BUG_ON(!profile); + if (type == AUDIT_APPARMOR_AUTO) { if (likely(!sa->aad.error)) { if (AUDIT_MODE(profile) != AUDIT_ALL) @@ -185,16 +197,13 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, AUDIT_MODE(profile) == AUDIT_QUIET)) return sa->aad.error; - if (profile && DO_KILL(profile) && type == AUDIT_APPARMOR_DENIED) + if (profile && KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED) type = AUDIT_APPARMOR_KILL; - sa->aad.type = type; if (profile && !unconfined(profile)) sa->aad.profile = profile; - sa->lsm_pre_audit = audit_pre; - sa->lsm_post_audit = cb; - common_lsm_audit(sa); + aa_audit_msg(type, sa, cb); if (sa->aad.type == AUDIT_APPARMOR_KILL) (void)send_sig_info(SIGKILL, NULL, sa->tsk ? sa->tsk : current); diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 9c28d92ace1..80f710fc2ca 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -48,15 +48,15 @@ static void audit_cb(struct audit_buffer *ab, void *va) /** * audit_caps - audit a capability - * @profile: profile confining task - * @task: task capability test was performed against + * @profile: profile confining task (NOT NULL) + * @task: task capability test was performed against (NOT NULL) * @cap: capability tested * @error: error code returned by test * * Do auditing of capability and handle, audit/complain/kill modes switching * and duplicate message elimination. * - * returns: 0 or sa->error on succes, error code on failure + * Returns: 0 or sa->error on succes, error code on failure */ static int audit_caps(struct aa_profile *profile, struct task_struct *task, int cap, int error) @@ -76,7 +76,7 @@ static int audit_caps(struct aa_profile *profile, struct task_struct *task, !cap_raised(profile->caps.audit, cap))) return 0; type = AUDIT_APPARMOR_AUDIT; - } else if (DO_KILL(profile) || + } else if (KILL_MODE(profile) || cap_raised(profile->caps.kill, cap)) { type = AUDIT_APPARMOR_KILL; } else if (cap_raised(profile->caps.quiet, cap) && @@ -117,8 +117,8 @@ static int profile_capable(struct aa_profile *profile, int cap) /** * aa_capable - test permission to use capability - * @task: task doing capability test against - * @profile: profile confining @task + * @task: task doing capability test against (NOT NULL) + * @profile: profile confining @task (NOT NULL) * @cap: capability to be tested * @audit: whether an audit record should be generated * diff --git a/security/apparmor/context.c b/security/apparmor/context.c index 6e12185bc67..deb4a30e0f3 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c @@ -14,7 +14,7 @@ * * * AppArmor sets confinement on every task, via the the aa_task_cxt and - * the aa_task_cxt profile, both of which are required and are not allowed + * the aa_task_cxt.profile, both of which are required and are not allowed * to be NULL. The aa_task_cxt is not reference counted and is unique * to each cred (which is reference count). The profile pointed to by * the task_cxt is reference counted. @@ -112,7 +112,7 @@ int aa_replace_current_profile(struct aa_profile *profile) /** * aa_set_current_onexec - set the tasks change_profile to happen onexec - * @profile: system profile to set at exec (MAYBE NULL) + * @profile: system profile to set at exec (MAYBE NULL to clear value) * * Returns: 0 or error on failure */ diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 2ae4e7aacb6..9efb5d91f22 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -100,7 +100,7 @@ out: */ static struct file_perms change_profile_perms(struct aa_profile *profile, struct aa_namespace *ns, - const char *name, u16 request, + const char *name, u32 request, unsigned int start) { struct file_perms perms; @@ -109,7 +109,6 @@ static struct file_perms change_profile_perms(struct aa_profile *profile, if (unconfined(profile)) { perms.allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC; - perms.xindex = perms.xdelegate = perms.dindex = 0; perms.audit = perms.quiet = perms.kill = 0; return perms; } else if (!profile->file.dfa) { @@ -155,7 +154,7 @@ static struct aa_profile *__attach_match(const char *name, if (profile->xmatch && profile->xmatch_len > len) { unsigned int state = aa_dfa_match(profile->xmatch, DFA_START, name); - u16 perm = dfa_user_allow(profile->xmatch, state); + u32 perm = dfa_user_allow(profile->xmatch, state); /* any accepting state means a valid match. */ if (perm & MAY_EXEC) { candidate = profile; @@ -237,13 +236,13 @@ static const char *next_name(int xtype, const char *name) * @profile: current profile (NOT NULL) * @xindex: index into x transition table * - * Returns: refcounted profile, or NULL on failure + * Returns: refcounted profile, or NULL on failure (MAYBE NULL) */ -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u16 xindex) +static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) { struct aa_profile *new_profile = NULL; struct aa_namespace *ns = profile->ns; - u16 xtype = xindex & AA_X_TYPE_MASK; + u32 xtype = xindex & AA_X_TYPE_MASK; int index = xindex & AA_X_INDEX_MASK; const char *name; @@ -293,7 +292,7 @@ static struct aa_profile *x_table_lookup(struct aa_profile *profile, u16 xindex) /** * x_to_profile - get target profile for a given xindex * @profile: current profile (NOT NULL) - * @name: to to lookup if specified (NOT NULL) + * @name: to to lookup (NOT NULL) * @xindex: index into x transition table * * find profile for a transition index @@ -301,11 +300,11 @@ static struct aa_profile *x_table_lookup(struct aa_profile *profile, u16 xindex) * Returns: refcounted profile or NULL if not found available */ static struct aa_profile *x_to_profile(struct aa_profile *profile, - const char *name, u16 xindex) + const char *name, u32 xindex) { struct aa_profile *new_profile = NULL; struct aa_namespace *ns = profile->ns; - u16 xtype = xindex & AA_X_TYPE_MASK; + u32 xtype = xindex & AA_X_TYPE_MASK; switch (xtype) { case AA_X_NONE: @@ -407,7 +406,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) * onexec permission is linked to exec with a standard pairing * exec\0change_profile */ - state = aa_dfa_null_transition(profile->file.dfa, state, 0); + state = aa_dfa_null_transition(profile->file.dfa, state); cp = change_profile_perms(profile, cxt->onexec->ns, name, AA_MAY_ONEXEC, state); @@ -424,7 +423,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (perms.xindex & AA_X_INHERIT) { /* (p|c|n)ix - don't change profile but do * use the newest version, which was picked - * up above when getting profile + * up above when getting profile */ info = "ix fallback"; new_profile = aa_get_profile(profile); @@ -583,7 +582,7 @@ static char *new_compound_name(const char *n1, const char *n2) /** * aa_change_hat - change hat to/from subprofile - * @hats: vector of hat names to try changing into (unused if @count == 0) + * @hats: vector of hat names to try changing into (MAYBE NULL if @count == 0) * @count: number of hat names in @hats * @token: magic value to validate the hat change * @permtest: true if this is just a permission test @@ -629,7 +628,6 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) hat = aa_find_child(root, hats[i]); if (!hat) { if (!COMPLAIN_MODE(root) || permtest) { - info = "hat not found"; if (list_empty(&root->base.profiles)) error = -ECHILD; else @@ -708,18 +706,20 @@ out: /** * aa_change_profile - perform a one-way profile transition - * @ns_name: name of the profile namespace to change to - * @hname: name of profile to change to + * @ns_name: name of the profile namespace to change to (MAYBE NULL) + * @hname: name of profile to change to (MAYBE NULL) * @onexec: whether this transition is to take place immediately or at exec * @permtest: true if this is just a permission test * * Change to new profile @name. Unlike with hats, there is no way - * to change back. If @onexec then the transition is delayed until + * to change back. If @name isn't specified the current profile name is + * used. + * If @onexec then the transition is delayed until * the next exec. * * Returns %0 on success, error otherwise. */ -int aa_change_profile(const char *ns_name, const char *hname, int onexec, +int aa_change_profile(const char *ns_name, const char *hname, bool onexec, bool permtest) { const struct cred *cred; @@ -729,7 +729,7 @@ int aa_change_profile(const char *ns_name, const char *hname, int onexec, struct file_perms perms = {}; const char *name = NULL, *info = NULL; int op, error = 0; - u16 request; + u32 request; if (!hname && !ns_name) return -EINVAL; diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 6db30491a6e..1b3c0a9edee 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -26,9 +26,8 @@ struct file_perms nullperms; * audit_file_mask - convert mask to owner::other string * @buffer: buffer to write string to (NOT NULL) * @mask: permission mask to convert - * @owner: if the mask is for owner or other */ -static void audit_file_mask(struct audit_buffer *ab, u16 mask) +static void audit_file_mask(struct audit_buffer *ab, u32 mask) { char str[10]; @@ -53,7 +52,7 @@ static void audit_file_mask(struct audit_buffer *ab, u16 mask) *m++ = 'k'; if (mask & MAY_EXEC) *m++ = 'x'; - *m++ = '\0'; + *m = '\0'; audit_log_string(ab, str); } @@ -99,15 +98,16 @@ static void file_audit_cb(struct audit_buffer *ab, void *va) * @ouid: object uid * @info: extra information message (MAYBE NULL) * @error: 0 if operation allowed else failure error code + * * Returns: %0 or error on failure */ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, - gfp_t gfp, int op, u16 request, const char *name, + gfp_t gfp, int op, u32 request, const char *name, const char *target, uid_t ouid, const char *info, int error) { int type = AUDIT_APPARMOR_AUTO; struct common_audit_data sa; - COMMON_AUDIT_DATA_INIT_NONE(&sa); + COMMON_AUDIT_DATA_INIT(&sa, NONE); sa.aad.op = op, sa.aad.fs.request = request; sa.aad.name = name; @@ -117,7 +117,7 @@ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, sa.aad.error = error; if (likely(!sa.aad.error)) { - u16 mask = perms->audit; + u32 mask = perms->audit; if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) mask = 0xffff; @@ -155,9 +155,9 @@ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, * * Returns: new permission mapping */ -static u16 map_old_perms(u32 old) +static u32 map_old_perms(u32 old) { - u16 new = old & 0xf; + u32 new = old & 0xf; if (old & MAY_READ) new |= AA_MAY_META_READ; if (old & MAY_WRITE) @@ -173,6 +173,8 @@ static u16 map_old_perms(u32 old) if (old & 0x40) /* AA_EXEC_MMAP */ new |= AA_EXEC_MMAP; + new |= AA_MAY_META_READ; + return new; } @@ -198,7 +200,6 @@ static struct file_perms compute_perms(struct aa_dfa *dfa, unsigned int state, * done at profile load */ perms.kill = 0; - perms.dindex = 0; if (current_fsuid() == cond->uid) { perms.allow = map_old_perms(dfa_user_allow(dfa, state)); @@ -221,7 +222,7 @@ static struct file_perms compute_perms(struct aa_dfa *dfa, unsigned int state, /** * aa_str_perms - find permission that match @name - * @dfa: to match against (NOT NULL) + * @dfa: to match against (MAYBE NULL) * @state: state to start matching in * @name: string to match against dfa (NOT NULL) * @cond: conditions to consider for permission set computation (NOT NULL) @@ -246,33 +247,6 @@ unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, } /** - * aa_pathstr_perm - do permission check & audit for @name - * @op: operation being checked - * @profile: profile being enforced (NOT NULL) - * @name: path string to check permission for (NOT NULL) - * @request: requested permissions - * @cond: conditional info for this request (NOT NULL) - * - * Do permission check for paths that are predefined. This fn will - * be removed once security_sysctl goes away. - * - * Returns: %0 else error if access denied or other error - */ -int aa_pathstr_perm(int op, struct aa_profile *profile, const char *name, - u16 request, struct path_cond *cond) -{ - struct file_perms perms = { }; - int error = 0; - - aa_str_perms(profile->file.dfa, profile->file.start, name, cond, - &perms); - if (request & ~perms.allow) - error = -EACCES; - return aa_audit_file(profile, &perms, GFP_KERNEL, op, request, name, - NULL, cond->uid, NULL, error); -} - -/** * is_deleted - test if a file has been completely unlinked * @dentry: dentry of file to test for deletion (NOT NULL) * @@ -297,7 +271,7 @@ static inline bool is_deleted(struct dentry *dentry) * Returns: %0 else error if access denied or other error */ int aa_path_perm(int op, struct aa_profile *profile, struct path *path, - int flags, u16 request, struct path_cond *cond) + int flags, u32 request, struct path_cond *cond) { char *buffer = NULL; struct file_perms perms = {}; @@ -345,7 +319,7 @@ int aa_path_perm(int op, struct aa_profile *profile, struct path *path, * * Returns: %1 if subset else %0 */ -static inline bool xindex_is_subset(u16 link, u16 target) +static inline bool xindex_is_subset(u32 link, u32 target) { if (((link & ~AA_X_UNSAFE) != (target & ~AA_X_UNSAFE)) || ((link & AA_X_UNSAFE) && !(target & AA_X_UNSAFE))) @@ -384,7 +358,7 @@ int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, char *buffer = NULL, *buffer2 = NULL; const char *lname, *tname = NULL, *info = NULL; struct file_perms lperms, perms; - u16 request = AA_MAY_LINK; + u32 request = AA_MAY_LINK; unsigned int state; int error; @@ -409,8 +383,7 @@ int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, goto audit; /* test to see if target can be paired with link */ - state = aa_dfa_null_transition(profile->file.dfa, state, - profile->flags & PFLAG_OLD_NULL_TRANS); + state = aa_dfa_null_transition(profile->file.dfa, state); aa_str_perms(profile->file.dfa, state, tname, &cond, &perms); /* force audit/quiet masks for link are stored in the second entry @@ -472,7 +445,7 @@ audit: * Returns: %0 if access allowed else error */ int aa_file_perm(int op, struct aa_profile *profile, struct file *file, - u16 request) + u32 request) { struct path_cond cond = { .uid = file->f_path.dentry->d_inode->i_uid, diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 25c16470ac8..c51e551ec1a 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -48,30 +48,41 @@ extern unsigned int aa_g_path_max; /* Flag indicating whether initialization completed */ extern int apparmor_initialized; -void apparmor_disable(void); /* fn's in lib */ char *aa_split_fqname(char *args, char **ns_name); -bool aa_strneq(const char *str, const char *sub, int len); void aa_info_message(const char *str); +void *kvmalloc(size_t size); +void kvfree(void *buffer); + + +/** + * aa_strneq - compare null terminated @str to a non null terminated substring + * @str: a null terminated string + * @sub: a substring, not necessarily null terminated + * @len: length of @sub to compare + * + * The @str string must be full consumed for this to be considered a match + */ +static inline bool aa_strneq(const char *str, const char *sub, int len) +{ + return !strncmp(str, sub, len) && !str[len]; +} /** * aa_dfa_null_transition - step to next state after null character * @dfa: the dfa to match against * @start: the state of the dfa to start matching in - * @old: true if using // as the null transition * * aa_dfa_null_transition transitions to the next state after a null * character which is not used in standard matching and is only * used to seperate pairs. */ static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, - unsigned int start, bool old) + unsigned int start) { - if (unlikely(old)) - return aa_dfa_match_len(dfa, start, "//", 2); - else - return aa_dfa_match_len(dfa, start, "\0", 1); + /* the null transition only needs a single null byte of the string */ + return aa_dfa_match_len(dfa, start, "", 1); } static inline bool mediated_filesystem(struct inode *inode) diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index ba6652f0aac..cfbae70b7cb 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -15,15 +15,6 @@ #ifndef __AA_APPARMORFS_H #define __AA_APPARMORFS_H -extern struct dentry *aa_fs_null; -extern struct vfsmount *aa_fs_mnt; - extern void aa_destroy_aafs(void); -#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24 -extern const struct file_operations aa_fs_matching_fops; -extern const struct file_operations aa_fs_features_fops; -extern const struct file_operations aa_fs_profiles_fops; -#endif - #endif /* __AA_APPARMORFS_H */ diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 24cdfe836b4..1951786d32e 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -107,6 +107,8 @@ enum aa_ops { /* define a short hand for apparmor_audit_data portion of common_audit_data */ #define aad apparmor_audit_data +void aa_audit_msg(int type, struct common_audit_data *sa, + void (*cb) (struct audit_buffer *, void *)); int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, struct common_audit_data *sa, void (*cb) (struct audit_buffer *, void *)); @@ -118,10 +120,4 @@ static inline int complain_error(int error) return error; } -#define COMMON_AUDIT_DATA_INIT_NONE(_d) \ - do { \ - memset((_d), 0, sizeof(struct common_audit_data)); \ - (_d)->type = LSM_AUDIT_DATA_NONE; \ - } while (0) - #endif /* __AA_AUDIT_H */ diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index b1ba488cf82..de04464f0a3 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -30,7 +30,7 @@ void apparmor_bprm_committed_creds(struct linux_binprm *bprm); void aa_free_domain_entries(struct aa_domain *domain); int aa_change_hat(const char *hats[], int count, u64 token, bool permtest); -int aa_change_profile(const char *ns_name, const char *name, int onexec, +int aa_change_profile(const char *ns_name, const char *name, bool onexec, bool permtest); #endif /* __AA_DOMAIN_H */ diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index dc79248272f..bba5ced35bd 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -35,12 +35,12 @@ struct aa_profile; #define AA_MAY_CHOWN 0x0200 #define AA_MAY_LOCK 0x0400 #define AA_EXEC_MMAP 0x0800 - + #define AA_MAY_LINK 0x1000 -#define AA_MAY_ONEXEC 0x4000 /* exec allows onexec */ -#define AA_MAY_CHANGE_PROFILE 0x8000 #define AA_LINK_SUBSET AA_MAY_LOCK /* overlayed */ -#define AA_MAY_CHANGEHAT 0x8000 /* ctrl auditing only */ +#define AA_MAY_ONEXEC 0x40000000 /* exec allows onexec */ +#define AA_MAY_CHANGE_PROFILE 0x80000000 +#define AA_MAY_CHANGEHAT 0x80000000 /* ctrl auditing only */ #define AA_AUDIT_FILE_MASK (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND |\ AA_MAY_CREATE | AA_MAY_DELETE | \ @@ -82,19 +82,15 @@ struct path_cond { * @quiet: mask of permissions to quiet audit messages for * @kill: mask of permissions that when matched will kill the task * @xindex: exec transition index if @allow contains MAY_EXEC - * @xdelegate: used by exec to determine set of delegates allowed - * @dindex: delegate table index, 0 if no delegation allowed * * The @audit and @queit mask should be mutually exclusive. */ struct file_perms { - u16 allow; - u16 audit; - u16 quiet; - u16 kill; + u32 allow; + u32 audit; + u32 quiet; + u32 kill; u16 xindex; - u16 xdelegate; - u16 dindex; }; extern struct file_perms nullperms; @@ -134,15 +130,15 @@ static inline u16 dfa_map_xindex(u16 mask) * map old dfa inline permissions to new format */ #define dfa_user_allow(dfa, state) (((ACCEPT_TABLE(dfa)[state]) & 0x7f) | \ - ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) + ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) #define dfa_user_audit(dfa, state) ((ACCEPT_TABLE2(dfa)[state]) & 0x7f) #define dfa_user_quiet(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 7) & 0x7f) #define dfa_user_xindex(dfa, state) \ (dfa_map_xindex(ACCEPT_TABLE(dfa)[state] & 0x3fff)) #define dfa_other_allow(dfa, state) ((((ACCEPT_TABLE(dfa)[state]) >> 14) & \ - 0x7f) | \ - ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) + 0x7f) | \ + ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) #define dfa_other_audit(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 14) & 0x7f) #define dfa_other_quiet(dfa, state) \ ((((ACCEPT_TABLE2(dfa)[state]) >> 7) >> 14) & 0x7f) @@ -150,7 +146,7 @@ static inline u16 dfa_map_xindex(u16 mask) dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff) int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, - gfp_t gfp, int op, u16 request, const char *name, + gfp_t gfp, int op, u32 request, const char *name, const char *target, uid_t ouid, const char *info, int error); /** @@ -176,17 +172,14 @@ unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, const char *name, struct path_cond *cond, struct file_perms *perms); -int aa_pathstr_perm(int op, struct aa_profile *profile, const char *name, - u16 request, struct path_cond *cond); - int aa_path_perm(int op, struct aa_profile *profile, struct path *path, - int flags, u16 request, struct path_cond *cond); + int flags, u32 request, struct path_cond *cond); int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, struct path *new_dir, struct dentry *new_dentry); int aa_file_perm(int op, struct aa_profile *profile, struct file *file, - u16 request); + u32 request); static inline void aa_free_file_rules(struct aa_file_rules *rules) { @@ -205,10 +198,10 @@ static inline void aa_free_file_rules(struct aa_file_rules *rules) * * Returns: apparmor permission set for the file */ -static inline u16 aa_map_file_to_perms(struct file *file) +static inline u32 aa_map_file_to_perms(struct file *file) { int flags = MAP_OPEN_FLAGS(file->f_flags); - u16 perms = ACC_FMODE(file->f_mode); + u32 perms = ACC_FMODE(file->f_mode); if ((flags & O_APPEND) && (perms & MAY_WRITE)) perms = (perms & ~MAY_WRITE) | MAY_APPEND; diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index cdc5e2cf633..3cd089f7536 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -110,13 +110,6 @@ static inline size_t table_size(size_t len, size_t el_size) return ALIGN(sizeof(struct table_header) + len * el_size, 8); } -static inline size_t table_alloc_size(size_t size) -{ - if (size > sizeof(struct work_struct)) - return size; - return sizeof(struct work_struct); -} - struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags); unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, const char *str, int len); diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index 746a322d830..27b327a7fae 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h @@ -27,6 +27,5 @@ enum path_flags { }; int aa_get_name(struct path *path, int flags, char **buffer, const char **name); -char *sysctl_pathname(struct ctl_table *table, char *buffer, int buflen); #endif /* __AA_PATH_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 94ca559b472..7d757b8f59d 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -27,22 +27,20 @@ #include "capability.h" #include "domain.h" #include "file.h" -#include "net.h" #include "resource.h" extern const char *profile_mode_names[]; #define APPARMOR_NAMES_MAX_INDEX 3 -#define COMPLAIN_MODE(_profile) \ - ((aa_g_profile_mode == APPARMOR_COMPLAIN) || ((_profile) && \ - (_profile)->mode == APPARMOR_COMPLAIN)) +#define COMPLAIN_MODE(_profile) \ + ((aa_g_profile_mode == APPARMOR_COMPLAIN) || \ + ((_profile)->mode == APPARMOR_COMPLAIN)) -#define DO_KILL(_profile) \ - ((aa_g_profile_mode == APPARMOR_KILL) || ((_profile) && \ - (_profile)->mode == APPARMOR_KILL)) +#define KILL_MODE(_profile) \ + ((aa_g_profile_mode == APPARMOR_KILL) || \ + ((_profile)->mode == APPARMOR_KILL)) -#define PROFILE_IS_HAT(_profile) \ - ((_profile) && (_profile)->flags & PFLAG_HAT) +#define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) /* * FIXME: currently need a clean way to replace and remove profiles as a @@ -64,7 +62,6 @@ enum profile_flags { PFLAG_IMMUTABLE = 0x10, /* don't allow changes/replacement */ PFLAG_USER_DEFINED = 0x20, /* user based profile */ PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ - PFLAG_MMAP_MIN_ADDR = 0x80, /* profile controls mmap_min_addr */ PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ /* These flags must coorespond with PATH_flags */ @@ -149,7 +146,6 @@ struct aa_namespace { * @size: the memory consumed by this profiles rules * @file: The set of rules governing basic file access and domain transitions * @caps: capabilities for the profile - * @net: network controls for the profile * @rlimits: rlimits for the profile * * The AppArmor profile contains the basic confinement data. Each profile @@ -184,11 +180,8 @@ struct aa_profile { u32 path_flags; int size; - unsigned long mmap_min_addr; - struct aa_file_rules file; struct aa_caps caps; - struct aa_net net; struct aa_rlimit rlimits; }; @@ -197,7 +190,7 @@ extern enum profile_mode aa_g_profile_mode; void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); -bool aa_ns_visible(struct aa_namespace *parent, struct aa_namespace *child); +bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view); const char *aa_ns_name(struct aa_namespace *parent, struct aa_namespace *child); int aa_alloc_root_ns(void); void aa_free_root_ns(void); @@ -270,7 +263,8 @@ ssize_t aa_remove_profiles(char *name, size_t size); static inline struct aa_profile *aa_newest_version(struct aa_profile *profile) { if (unlikely(profile && profile->replacedby)) - for (; profile->replacedby; profile = profile->replacedby) ; + for (; profile->replacedby; profile = profile->replacedby) + ; return profile; } @@ -304,9 +298,8 @@ static inline int AUDIT_MODE(struct aa_profile *profile) { if (aa_g_audit != AUDIT_NORMAL) return aa_g_audit; - if (profile) - return profile->audit; - return AUDIT_NORMAL; + + return profile->audit; } bool aa_may_manage_policy(int op); diff --git a/security/apparmor/include/procattr.h b/security/apparmor/include/procattr.h index 6c6f271ae80..88025222cd7 100644 --- a/security/apparmor/include/procattr.h +++ b/security/apparmor/include/procattr.h @@ -20,7 +20,7 @@ int aa_getprocattr(struct aa_profile *profile, char **string); int aa_setprocattr_changehat(char *args, size_t size, int test); -int aa_setprocattr_changeprofile(char *fqname, int onexec, int test); +int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test); int aa_setprocattr_permipc(char *fqname); #endif /* __AA_PROCATTR_H */ diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index d667f89bb24..9013a78a166 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -40,7 +40,7 @@ static int aa_audit_ptrace(struct aa_profile *profile, struct aa_profile *target, int error) { struct common_audit_data sa; - COMMON_AUDIT_DATA_INIT_NONE(&sa); + COMMON_AUDIT_DATA_INIT(&sa, NONE); sa.aad.op = OP_PTRACE; sa.aad.target = target; sa.aad.error = error; @@ -66,7 +66,7 @@ int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer, * Test mode for PTRACE_MODE_READ || PTRACE_MODE_ATTACH */ - if (!tracer || tracer == tracee) + if (unconfined(tracer) || tracer == tracee) return 0; /* log this capability request */ return aa_capable(tracer_task, tracer, CAP_SYS_PTRACE, 1); @@ -74,8 +74,8 @@ int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer, /** * aa_ptrace - do ptrace permission check and auditing - * @tracer: task doing the tracing - * @tracee: task being traced + * @tracer: task doing the tracing (NOT NULL) + * @tracee: task being traced (NOT NULL) * @mode: ptrace mode either PTRACE_MODE_READ || PTRACE_MODE_ATTACH * * Returns: %0 else error code if permission denied or error diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 035d8ee3268..51837b5a10a 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -14,14 +14,15 @@ #include <linux/slab.h> #include <linux/string.h> +#include <linux/vmalloc.h> #include "include/audit.h" /** * aa_split_fqname - split a fqname into a profile and namespace name - * @fqname: a full qualified name in namespace profile format - * @ns_name: pointer to portion of the string containing the ns name + * @fqname: a full qualified name in namespace profile format (NOT NULL) + * @ns_name: pointer to portion of the string containing the ns name (NOT NULL) * * Returns: profile name or NULL if one is not specified * @@ -55,30 +56,75 @@ char *aa_split_fqname(char *fqname, char **ns_name) } /** - * aa_strneq - compare null terminated @str to a non null terminated substring - * @str: a null terminated string - * @sub: a substring, not necessarily null terminated - * @len: length of @sub to compare + * aa_info_message - log a none profile related status message + * @str: message to log + */ +void aa_info_message(const char *str) +{ + if (audit_enabled) { + struct common_audit_data sa; + COMMON_AUDIT_DATA_INIT(&sa, NONE); + sa.aad.info = str; + printk(KERN_INFO "AppArmor: %s\n", str); + aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); + } +} + +/** + * kvmalloc - do allocation prefering kmalloc but falling back to vmalloc + * @size: size of allocation + * + * Return: allocated buffer or NULL if failed * - * The @str string must be full consumed for this to be considered a match + * It is possible that policy being loaded from the user is larger than + * what can be allocated by kmalloc, in those cases fall back to vmalloc. */ -bool aa_strneq(const char *str, const char *sub, int len) +void *kvmalloc(size_t size) { - int res = strncmp(str, sub, len); - if (res) - return 0; - if (str[len] == 0) - return 1; - return 0; + void *buffer = NULL; + + if (size == 0) + return NULL; + + /* do not attempt kmalloc if we need more than 16 pages at once */ + if (size <= (16*PAGE_SIZE)) + buffer = kmalloc(size, GFP_NOIO | __GFP_NOWARN); + if (!buffer) { + if (size < sizeof(struct work_struct)) + size = sizeof(struct work_struct); + buffer = vmalloc(size); + } + return buffer; } -void aa_info_message(const char *str) +/** + * do_vfree - workqueue routine for freeing vmalloced memory + * @work: data to be freed + * + * The work_struct is overlayed to the data being freed, as at the point + * the work is scheduled the data is no longer valid, be its freeing + * needs to be delayed until safe. + */ +static void do_vfree(struct work_struct *work) { - struct common_audit_data sa; - COMMON_AUDIT_DATA_INIT_NONE(&sa); - sa.aad.info = str, - printk(KERN_INFO "AppArmor: %s\n", str); - if (audit_enabled) - aa_audit(AUDIT_APPARMOR_STATUS, NULL, GFP_KERNEL, &sa, NULL); + vfree(work); } +/** + * kvfree - free an allocation do by kvmalloc + * @buffer: buffer to free (MAYBE_NULL) + * + * Free a buffer allocated by kvmalloc + */ +void kvfree(void *buffer) +{ + if (is_vmalloc_addr(buffer)) { + /* Data is no longer valid so just use the allocated space + * as the work_struct + */ + struct work_struct *work = (struct work_struct *) buffer; + INIT_WORK(work, do_vfree); + schedule_work(work); + } else + kfree(buffer); +} diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 5e3f180ac2b..b4828d74a1d 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -31,7 +31,6 @@ #include "include/context.h" #include "include/file.h" #include "include/ipc.h" -#include "include/net.h" #include "include/path.h" #include "include/policy.h" #include "include/procattr.h" @@ -96,17 +95,19 @@ static void apparmor_cred_transfer(struct cred *new, const struct cred *old) static int apparmor_ptrace_access_check(struct task_struct *child, unsigned int mode) { - int rc; - - rc = cap_ptrace_access_check(child, mode); - if (rc) - return rc; + int error = cap_ptrace_access_check(child, mode); + if (error) + return error; return aa_ptrace(current, child, mode); } static int apparmor_ptrace_traceme(struct task_struct *parent) { + int error = cap_ptrace_traceme(parent); + if (error) + return error; + return aa_ptrace(parent, current, PTRACE_MODE_ATTACH); } @@ -125,8 +126,10 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective, *inheritable = cred->cap_inheritable; *permitted = cred->cap_permitted; - if (!unconfined(profile)) + if (!unconfined(profile)) { *effective = cap_intersect(*effective, profile->caps.allow); + *permitted = cap_intersect(*permitted, profile->caps.allow); + } rcu_read_unlock(); return 0; @@ -138,51 +141,11 @@ static int apparmor_capable(struct task_struct *task, const struct cred *cred, struct aa_profile *profile; /* cap_capable returns 0 on success, else -EPERM */ int error = cap_capable(task, cred, cap, audit); - - profile = aa_cred_profile(cred); - if (!error && !unconfined(profile)) - error = aa_capable(task, profile, cap, audit); - - return error; -} - -static int apparmor_sysctl(struct ctl_table *table, int sysctl_op) -{ - int error = 0; - struct aa_profile *profile = aa_current_profile(); - - if (!unconfined(profile)) { - char *buffer, *name; - int mask; - - mask = 0; - if (sysctl_op & 4) - mask |= MAY_READ; - if (sysctl_op & 2) - mask |= MAY_WRITE; - - error = -ENOMEM; - /* freed below */ - buffer = (char *)__get_free_page(GFP_KERNEL); - if (!buffer) - goto out; - - /* - * TODO: convert this over to using a global or per - * namespace control instead of a hard coded /proc - */ - name = sysctl_pathname(table, buffer, PAGE_SIZE); - if (name && name - buffer >= 5) { - struct path_cond cond = { 0, S_IFREG }; - name -= 5; - memcpy(name, "/proc", 5); - error = aa_pathstr_perm(OP_SYSCTL, profile, name, mask, - &cond); - } - free_page((unsigned long)buffer); + if (!error) { + profile = aa_cred_profile(cred); + if (!unconfined(profile)) + error = aa_capable(task, profile, cap, audit); } - -out: return error; } @@ -195,7 +158,7 @@ out: * * Returns: %0 else error code if error or permission denied */ -static int common_perm(int op, struct path *path, u16 mask, +static int common_perm(int op, struct path *path, u32 mask, struct path_cond *cond) { struct aa_profile *profile; @@ -219,7 +182,7 @@ static int common_perm(int op, struct path *path, u16 mask, * Returns: %0 else error code if error or permission denied */ static int common_perm_dir_dentry(int op, struct path *dir, - struct dentry *dentry, u16 mask, + struct dentry *dentry, u32 mask, struct path_cond *cond) { struct path path = { dir->mnt, dentry }; @@ -230,14 +193,14 @@ static int common_perm_dir_dentry(int op, struct path *dir, /** * common_perm_mnt_dentry - common permission wrapper when mnt, dentry * @op: operation being checked - * @mnt: mount point of dentry + * @mnt: mount point of dentry (NOT NULL) * @dentry: dentry to check (NOT NULL) * @mask: requested permissions mask * * Returns: %0 else error code if error or permission denied */ static int common_perm_mnt_dentry(int op, struct vfsmount *mnt, - struct dentry *dentry, u16 mask) + struct dentry *dentry, u32 mask) { struct path path = { mnt, dentry }; struct path_cond cond = { dentry->d_inode->i_uid, @@ -257,7 +220,7 @@ static int common_perm_mnt_dentry(int op, struct vfsmount *mnt, * Returns: %0 else error code if error or permission denied */ static int common_perm_rm(int op, struct path *dir, - struct dentry *dentry, u16 mask) + struct dentry *dentry, u32 mask) { struct inode *inode = dentry->d_inode; struct path_cond cond = { }; @@ -282,7 +245,7 @@ static int common_perm_rm(int op, struct path *dir, * Returns: %0 else error code if error or permission denied */ static int common_perm_create(int op, struct path *dir, struct dentry *dentry, - u16 mask, umode_t mode) + u32 mask, umode_t mode) { struct path_cond cond = { current_fsuid(), mode }; @@ -461,7 +424,7 @@ static void apparmor_file_free_security(struct file *file) aa_free_file_context(cxt); } -static int common_file_perm(int op, struct file *file, u16 mask) +static int common_file_perm(int op, struct file *file, u32 mask) { struct aa_file_cxt *fcxt = file->f_security; struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred); @@ -496,7 +459,7 @@ static int apparmor_file_permission(struct file *file, int mask) static int apparmor_file_lock(struct file *file, unsigned int cmd) { - u16 mask = AA_MAY_LOCK; + u32 mask = AA_MAY_LOCK; if (cmd == F_WRLCK) mask |= MAY_WRITE; @@ -579,19 +542,27 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, static int apparmor_setprocattr(struct task_struct *task, char *name, void *value, size_t size) { - char *command, *args; + char *command, *args = value; size_t arg_size; int error; - if (size == 0 || size >= PAGE_SIZE) + if (size == 0) return -EINVAL; + /* args points to a PAGE_SIZE buffer, AppArmor requires that + * the buffer must be null terminated or have size <= PAGE_SIZE -1 + * so that AppArmor can null terminate them + */ + if (args[size - 1] != '\0') { + if (size == PAGE_SIZE) + return -EINVAL; + args[size] = '\0'; + } /* task can only write its own attributes */ if (current != task) return -EACCES; args = value; - args[size] = '\0'; args = strim(args); command = strsep(&args, " "); if (!args) @@ -618,7 +589,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, error = aa_setprocattr_permipc(args); } else { struct common_audit_data sa; - COMMON_AUDIT_DATA_INIT_NONE(&sa); + COMMON_AUDIT_DATA_INIT(&sa, NONE); sa.aad.op = OP_SETPROCATTR; sa.aad.info = name; sa.aad.error = -EINVAL; @@ -649,111 +620,12 @@ static int apparmor_task_setrlimit(unsigned int resource, return error; } -static int apparmor_socket_create(int family, int type, int protocol, int kern) -{ - struct aa_profile *profile; - int error = 0; - - if (kern) - return 0; - - profile = __aa_current_profile(); - if (!unconfined(profile)) - error = aa_net_perm(OP_CREATE, profile, family, type, protocol, - NULL); - return error; -} - -static int apparmor_socket_bind(struct socket *sock, - struct sockaddr *address, int addrlen) -{ - struct sock *sk = sock->sk; - - return aa_revalidate_sk(OP_BIND, sk); -} - -static int apparmor_socket_connect(struct socket *sock, - struct sockaddr *address, int addrlen) -{ - struct sock *sk = sock->sk; - - return aa_revalidate_sk(OP_CONNECT, sk); -} - -static int apparmor_socket_listen(struct socket *sock, int backlog) -{ - struct sock *sk = sock->sk; - - return aa_revalidate_sk(OP_LISTEN, sk); -} - -static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) -{ - struct sock *sk = sock->sk; - - return aa_revalidate_sk(OP_ACCEPT, sk); -} - -static int apparmor_socket_sendmsg(struct socket *sock, - struct msghdr *msg, int size) -{ - struct sock *sk = sock->sk; - - return aa_revalidate_sk(OP_SENDMSG, sk); -} - -static int apparmor_socket_recvmsg(struct socket *sock, - struct msghdr *msg, int size, int flags) -{ - struct sock *sk = sock->sk; - - return aa_revalidate_sk(OP_RECVMSG, sk); -} - -static int apparmor_socket_getsockname(struct socket *sock) -{ - struct sock *sk = sock->sk; - - return aa_revalidate_sk(OP_GETSOCKNAME, sk); -} - -static int apparmor_socket_getpeername(struct socket *sock) -{ - struct sock *sk = sock->sk; - - return aa_revalidate_sk(OP_GETPEERNAME, sk); -} - -static int apparmor_socket_getsockopt(struct socket *sock, int level, - int optname) -{ - struct sock *sk = sock->sk; - - return aa_revalidate_sk(OP_GETSOCKOPT, sk); -} - -static int apparmor_socket_setsockopt(struct socket *sock, int level, - int optname) -{ - struct sock *sk = sock->sk; - - return aa_revalidate_sk(OP_SETSOCKOPT, sk); -} - -static int apparmor_socket_shutdown(struct socket *sock, int how) -{ - struct sock *sk = sock->sk; - - return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); -} - static struct security_operations apparmor_ops = { .name = "apparmor", .ptrace_access_check = apparmor_ptrace_access_check, .ptrace_traceme = apparmor_ptrace_traceme, .capget = apparmor_capget, - .sysctl = apparmor_sysctl, .capable = apparmor_capable, .path_link = apparmor_path_link, @@ -779,19 +651,6 @@ static struct security_operations apparmor_ops = { .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, - .socket_create = apparmor_socket_create, - .socket_bind = apparmor_socket_bind, - .socket_connect = apparmor_socket_connect, - .socket_listen = apparmor_socket_listen, - .socket_accept = apparmor_socket_accept, - .socket_sendmsg = apparmor_socket_sendmsg, - .socket_recvmsg = apparmor_socket_recvmsg, - .socket_getsockname = apparmor_socket_getsockname, - .socket_getpeername = apparmor_socket_getpeername, - .socket_getsockopt = apparmor_socket_getsockopt, - .socket_setsockopt = apparmor_socket_setsockopt, - .socket_shutdown = apparmor_socket_shutdown, - .cred_alloc_blank = apparmor_cred_alloc_blank, .cred_free = apparmor_cred_free, .cred_prepare = apparmor_cred_prepare, @@ -850,7 +709,7 @@ module_param_call(audit, param_set_audit, param_get_audit, /* Determines if audit header is included in audited messages. This * provides more context if the audit daemon is not running */ -int aa_g_audit_header; +int aa_g_audit_header = 1; module_param_named(audit_header, aa_g_audit_header, aabool, S_IRUSR | S_IWUSR); @@ -1032,7 +891,7 @@ static int __init apparmor_init(void) int error; if (!apparmor_enabled || !security_module_enable(&apparmor_ops)) { - aa_info_message("AppArmor disabled by boot time parameter\n"); + aa_info_message("AppArmor disabled by boot time parameter"); apparmor_enabled = 0; return 0; } diff --git a/security/apparmor/match.c b/security/apparmor/match.c index e4bddf6dea1..8e7523ab392 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -20,55 +20,22 @@ #include <linux/err.h> #include <linux/kref.h> +#include "include/apparmor.h" #include "include/match.h" /** - * do_vfree - workqueue routine for freeing vmalloced memory - * @work: data to be freed - * - * The work_struct is overlayed to the data being freed, as at the point - * the work is scheduled the data is no longer valid, be its freeing - * needs to be delayed until safe. - */ -static void do_vfree(struct work_struct *work) -{ - vfree(work); -} - -/** - * free_table - free a table allocated by unpack table - * @table: table to unpack (MAYBE NULL) - */ -static void free_table(struct table_header *table) -{ - if (!table) - return; - - if (is_vmalloc_addr(table)) { - /* Data is no longer valid so just use the allocated space - * as the work_struct - */ - struct work_struct *work = (struct work_struct *) table; - INIT_WORK(work, do_vfree); - schedule_work(work); - } else - kzfree(table); -} - -/** * unpack_table - unpack a dfa table (one of accept, default, base, next check) - * @blob: data to unpack + * @blob: data to unpack (NOT NULL) * @bsize: size of blob * * Returns: pointer to table else NULL on failure * - * NOTE: must be freed by free_table (not kmalloc) + * NOTE: must be freed by kvfree (not kmalloc) */ static struct table_header *unpack_table(char *blob, size_t bsize) { struct table_header *table = NULL; struct table_header th; - int unmap_alias = 0; size_t tsize; if (bsize < sizeof(struct table_header)) @@ -90,13 +57,7 @@ static struct table_header *unpack_table(char *blob, size_t bsize) if (bsize < tsize) goto out; - /* freed by free_table */ - table = kmalloc(table_alloc_size(tsize), GFP_KERNEL | __GFP_NOWARN); - if (!table) { - table = vmalloc(tsize); - if (table) - unmap_alias = 1; - } + table = kvmalloc(tsize); if (table) { *table = th; if (th.td_flags == YYTD_DATA8) @@ -113,17 +74,19 @@ static struct table_header *unpack_table(char *blob, size_t bsize) } out: - if (unmap_alias) + /* if table was vmalloced make sure the page tables are synced + * before it is used, as it goes live to all cpus. + */ + if (is_vmalloc_addr(table)) vm_unmap_aliases(); return table; fail: - free_table(table); + kvfree(table); return NULL; } /** - * verify_dfa - verify that all the transitions and states in the dfa tables - * are in bounds. + * verify_dfa - verify that transitions and states in the tables are in bounds. * @dfa: dfa to test (NOT NULL) * @flags: flags controlling what type of accept table are acceptable * @@ -175,8 +138,11 @@ static int verify_dfa(struct aa_dfa *dfa, int flags) if (DEFAULT_TABLE(dfa)[i] >= state_count) goto out; /* TODO: do check that DEF state recursion terminates */ - if (BASE_TABLE(dfa)[i] >= trans_count + 256) + if (BASE_TABLE(dfa)[i] + 255 >= trans_count) { + printk(KERN_ERR "AppArmor DFA next/check upper " + "bounds error\n"); goto out; + } } for (i = 0; i < trans_count; i++) { @@ -204,11 +170,11 @@ static void dfa_free(struct aa_dfa *dfa) int i; for (i = 0; i < ARRAY_SIZE(dfa->tables); i++) { - free_table(dfa->tables[i]); + kvfree(dfa->tables[i]); dfa->tables[i] = NULL; } + kfree(dfa); } - kfree(dfa); } /** @@ -309,7 +275,7 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags) return dfa; fail: - free_table(table); + kvfree(table); dfa_free(dfa); return ERR_PTR(error); } diff --git a/security/apparmor/path.c b/security/apparmor/path.c index a360793d20a..a19ba058993 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -12,6 +12,7 @@ * License. */ +#include <linux/magic.h> #include <linux/mnt_namespace.h> #include <linux/mount.h> #include <linux/namei.h> @@ -25,12 +26,26 @@ #include "include/path.h" #include "include/policy.h" + +/* modified from dcache.c */ +static int prepend(char **buffer, int buflen, const char *str, int namelen) +{ + buflen -= namelen; + if (buflen < 0) + return -ENAMETOOLONG; + *buffer -= namelen; + memcpy(*buffer, str, namelen); + return 0; +} + +#define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT) + /** * d_namespace_path - lookup a name associated with a given path * @path: path to lookup (NOT NULL) * @buf: buffer to store path to (NOT NULL) * @buflen: length of @buf - * @name: return pointer for start of path name with in @buf (NOT NULL) + * @name: Returns - pointer for start of path name with in @buf (NOT NULL) * @flags: flags controling path lookup * * Handle path name lookup. @@ -108,8 +123,9 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, /* Determine if the path is connected to the expected root */ connected = tmp.dentry == root.dentry && tmp.mnt == root.mnt; - /* If the path is not connected, then remove any leading / that - * __d_path may have returned. + /* If the path is not connected, + * check if it is a sysctl and handle specially else remove any + * leading / that __d_path may have returned. * Unless * specifically directed to connect the path, * OR @@ -118,15 +134,25 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, * of chroot) and specifically directed to connect paths to * namespace root. */ - if (!connected && - !(flags & PATH_CONNECT_PATH) && - !((flags & PATH_CHROOT_REL) && (flags & PATH_CHROOT_NSCONNECT) && - (tmp.mnt == current->nsproxy->mnt_ns->root && - tmp.dentry == current->nsproxy->mnt_ns->root->mnt_root))) { - /* disconnected path, don't return pathname starting with '/' */ - error = -ESTALE; - if (*res == '/') - *name = res + 1; + if (!connected) { + /* is the disconnect path a sysctl? */ + if (tmp.dentry->d_sb->s_magic == PROC_SUPER_MAGIC && + strncmp(*name, "/sys/", 5) == 0) { + /* TODO: convert over to using a per namespace + * control instead of hard coded /proc + */ + error = prepend(name, *name - buf, "/proc", 5); + } else if (!(flags & PATH_CONNECT_PATH) && + !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && + (tmp.mnt == current->nsproxy->mnt_ns->root && + tmp.dentry == tmp.mnt->mnt_root))) { + /* disconnected path, don't return pathname starting + * with '/' + */ + error = -ESTALE; + if (*res == '/') + *name = res + 1; + } } out: @@ -141,7 +167,7 @@ out: * @flags: flags controlling path lookup * @buffer: buffer to put name in (NOT NULL) * @size: size of buffer - * @name: on return contains position of path name in @buffer (NOT NULL) + * @name: Returns - contains position of path name in @buffer (NOT NULL) * * Returns: %0 else error on failure */ @@ -166,7 +192,7 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer, * @path: path the file (NOT NULL) * @flags: flags controling path name generation * @buffer: buffer that aa_get_name() allocated (NOT NULL) - * @name: the generated path name if !error + * @name: Returns - the generated path name if !error (NOT NULL) * * @name is a pointer to the beginning of the pathname (which usually differs * from the beginning of the buffer), or NULL. If there is an error @name @@ -207,37 +233,3 @@ int aa_get_name(struct path *path, int flags, char **buffer, const char **name) return error; } - -/** - * sysctl_pathname - generate a pathname for a sysctl - * @table: sysctl name table (NOT NULL) - * @buffer: buffer to put name in (NOT NULL) - * @buflen: length of @buffer - * - * Returns: sysctl path name in @buffer or NULL on error - */ -char *sysctl_pathname(struct ctl_table *table, char *buffer, int buflen) -{ - if (buflen < 1) - return NULL; - buffer += --buflen; - *buffer = '\0'; - - while (table) { - int namelen = strlen(table->procname); - - if (buflen < namelen + 1) - return NULL; - buflen -= namelen + 1; - buffer -= namelen; - memcpy(buffer, table->procname, namelen); - *--buffer = '/'; - table = table->parent; - } - if (buflen < 4) - return NULL; - buffer -= 4; - memcpy(buffer, "/sys", 4); - - return buffer; -} diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index a0add9d9db3..7fecdf2d2df 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -130,7 +130,7 @@ static bool policy_init(struct aa_policy *policy, const char *prefix, { /* freed by policy_free */ if (prefix) { - policy->hname = kmalloc(strlen(prefix) + strlen(name) +3, + policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3, GFP_KERNEL); if (policy->hname) sprintf(policy->hname, "%s//%s", prefix, name); @@ -222,38 +222,45 @@ static struct aa_policy *__policy_strn_find(struct list_head *head, static const char *hidden_ns_name = "---"; /** - * aa_ns_visible - test if @child is visible from @parent - * @parent: namespace to treat as the parent - * @child: namespace to test if visible from @parent + * aa_ns_visible - test if @view is visible from @curr + * @curr: namespace to treat as the parent (NOT NULL) + * @view: namespace to test if visible from @curr (NOT NULL) * - * Returns: true if @child is visible from @parent else false + * Returns: true if @view is visible from @curr else false */ -bool aa_ns_visible(struct aa_namespace *parent, struct aa_namespace *child) +bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view) { - if (parent == child) + if (curr == view) return true; - for ( ; child; child = child->parent) { - if (child->parent == parent) + for ( ; view; view = view->parent) { + if (view->parent == curr) return true; } return false; } -const char *aa_ns_name(struct aa_namespace *parent, struct aa_namespace *child) +/** + * aa_na_name - Find the ns name to display for @view from @curr + * @curr - current namespace (NOT NULL) + * @view - namespace attempting to view (NOT NULL) + * + * Returns: name of @view visible from @curr + */ +const char *aa_ns_name(struct aa_namespace *curr, struct aa_namespace *view) { - /* if child == parent then the namespace name isn't displayed */ - if (parent == child) + /* if view == curr then the namespace name isn't displayed */ + if (curr == view) return ""; - if (aa_ns_visible(parent, child)) { - /* at this point if a ns is visible it is in a child ns - * thus the parent ns.hname is a prefix of its name. + if (aa_ns_visible(curr, view)) { + /* at this point if a ns is visible it is in a view ns + * thus the curr ns.hname is a prefix of its name. * Only output the virtualized portion of the name - * Add + 2 to skip over // seperating parent hname prefix - * from the visible tail of the childs hname + * Add + 2 to skip over // seperating curr hname prefix + * from the visible tail of the views hname */ - return child->base.hname + strlen(parent->base.hname) + 2; + return view->base.hname + strlen(curr->base.hname) + 2; } else return hidden_ns_name; } @@ -375,7 +382,7 @@ struct aa_namespace *aa_find_namespace(struct aa_namespace *root, /** * aa_prepare_namespace - find an existing or create a new namespace of @name - * @name: the namespace to find or add (NOT NULL) + * @name: the namespace to find or add (MAYBE NULL) * * Returns: refcounted namespace or NULL if failed to create one */ @@ -559,7 +566,7 @@ static void destroy_namespace(struct aa_namespace *ns) /** * __remove_namespace - remove a namespace and all its children * @ns: namespace to be removed (NOT NULL) - * + * * Requires: ns->parent->lock be held and ns removed from parent. */ static void __remove_namespace(struct aa_namespace *ns) @@ -619,11 +626,11 @@ int __init aa_alloc_root_ns(void) /** * aa_free_root_ns - free the root profile namespace */ -void aa_free_root_ns(void) +void __init aa_free_root_ns(void) { struct aa_namespace *ns = root_ns; root_ns = NULL; - + destroy_namespace(ns); aa_put_namespace(ns); } @@ -738,7 +745,6 @@ static void free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); - aa_free_net_rules(&profile->net); aa_free_rlimit_rules(&profile->rlimits); aa_free_sid(profile->sid); @@ -761,7 +767,7 @@ void aa_free_profile_kref(struct kref *kref) free_profile(p); } -/* TODO: profile count accounting - setup in remove */ +/* TODO: profile accounting - setup in remove */ /** * __find_child - find a profile on @head list with a name matching @name @@ -883,8 +889,8 @@ static struct aa_profile *__lookup_profile(struct aa_policy *base, /** * aa_lookup_profile - find a profile by its full or partial name - * @ns: the namespace to start from - * @hname: name to do lookup on. Does not contain namespace prefix + * @ns: the namespace to start from (NOT NULL) + * @hname: name to do lookup on. Does not contain namespace prefix (NOT NULL) * * Returns: refcounted profile or NULL if not found */ @@ -904,7 +910,7 @@ struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname) * replacement_allowed - test to see if replacement is allowed * @profile: profile to test if it can be replaced (MAYBE NULL) * @noreplace: true if replacement shouldn't be allowed but addition is okay - * @info: return pointer to info about why replacement failed + * @info: Returns - info about why replacement failed (NOT NULL) * * Returns: %0 if replacement allowed else error code */ @@ -947,8 +953,8 @@ static void __add_new_profile(struct aa_namespace *ns, struct aa_policy *policy, * aa_audit_policy - Do auditing of policy changes * @op: policy operation being performed * @gfp: memory allocation flags - * @name: name of profile being manipulated - * @info: any extra information to be audited + * @name: name of profile being manipulated (NOT NULL) + * @info: any extra information to be audited (MAYBE NULL) * @error: error code * * Returns: the error to be returned after audit is done @@ -957,7 +963,7 @@ static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, int error) { struct common_audit_data sa; - COMMON_AUDIT_DATA_INIT_NONE(&sa); + COMMON_AUDIT_DATA_INIT(&sa, NONE); sa.aad.op = op; sa.aad.name = name; sa.aad.info = info; @@ -1015,6 +1021,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) new_profile = aa_unpack(udata, size, &ns_name); if (IS_ERR(new_profile)) { error = PTR_ERR(new_profile); + new_profile = NULL; goto fail; } @@ -1097,7 +1104,7 @@ out: return size; fail: - error = audit_policy(op, GFP_KERNEL, name, info, error); + error = audit_policy(op, GFP_KERNEL, name, info, error); goto out; } diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 4b9a615c7a7..5b5dacb9623 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -81,10 +81,10 @@ static void audit_cb(struct audit_buffer *ab, void *va) /** * audit_iface - do audit message for policy unpacking/load/replace/remove - * @new: profile if it has been allocated - * @name: name of the profile being manipulated - * @info: any extra info about the failure - * @e: buffer position info + * @new: profile if it has been allocated (MAYBE NULL) + * @name: name of the profile being manipulated (MAYBE NULL) + * @info: any extra info about the failure (MAYBE NULL) + * @e: buffer position info (NOT NULL) * @error: error code * * Returns: %0 or error @@ -94,7 +94,7 @@ static int audit_iface(struct aa_profile *new, const char *name, { struct aa_profile *profile = __aa_current_profile(); struct common_audit_data sa; - COMMON_AUDIT_DATA_INIT_NONE(&sa); + COMMON_AUDIT_DATA_INIT(&sa, NONE); sa.aad.iface.pos = e->pos - e->start; sa.aad.iface.target = new; sa.aad.name = name; @@ -113,8 +113,8 @@ static bool inbounds(struct aa_ext *e, size_t size) /** * aa_u16_chunck - test and do bounds checking for a u16 size based chunk - * @e: serialized data read head - * @chunk: start address for chunk of data + * @e: serialized data read head (NOT NULL) + * @chunk: start address for chunk of data (NOT NULL) * * Returns: the size of chunk found with the read head at the end of the chunk. */ @@ -189,19 +189,6 @@ fail: return 0; } -static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) -{ - if (unpack_nameX(e, AA_U16, name)) { - if (!inbounds(e, sizeof(u16))) - return 0; - if (data) - *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); - e->pos += sizeof(u16); - return 1; - } - return 0; -} - static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) { if (unpack_nameX(e, AA_U32, name)) { @@ -299,6 +286,13 @@ static int unpack_strdup(struct aa_ext *e, char **string, const char *name) return res; } +/** + * verify_accept - verify the accept tables of a dfa + * @dfa: dfa to verify accept tables of (NOT NULL) + * @flags: flags governing dfa + * + * Returns: 1 if valid accept tables else 0 if error + */ static bool verify_accept(struct aa_dfa *dfa, int flags) { int i; @@ -318,7 +312,7 @@ static bool verify_accept(struct aa_dfa *dfa, int flags) /** * unpack_dfa - unpack a file rule dfa - * @e: serialized data extent information + * @e: serialized data extent information (NOT NULL) * * returns dfa or ERR_PTR or NULL if no dfa */ @@ -359,6 +353,13 @@ fail: return ERR_PTR(-EPROTO); } +/** + * unpack_trans_table - unpack a profile transition table + * @e: serialized data extent information (NOT NULL) + * @profile: profile to add the accept table to (NOT NULL) + * + * Returns: 1 if table succesfully unpacked + */ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) { void *pos = e->pos; @@ -452,19 +453,17 @@ fail: /** * unpack_profile - unpack a serialized profile - * @e: serialized data extent information + * @e: serialized data extent information (NOT NULL) * * NOTE: unpack profile sets audit struct if there is a failure */ static struct aa_profile *unpack_profile(struct aa_ext *e) { struct aa_profile *profile = NULL; - const char *name = NULL, *info = NULL; - size_t size = 0; - int i, error = -EPROTO; + const char *name = NULL; + int error = -EPROTO; kernel_cap_t tmpcap; u32 tmp; - u64 tmp64; /* check that we have the right struct being passed */ if (!unpack_nameX(e, AA_STRUCT, "profile")) @@ -519,17 +518,6 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) /* set a default value if path_flags field is not present */ profile->path_flags = PFLAG_MEDIATE_DELETED; - /* mmap_min_addr is optional */ - if (unpack_u64(e, &tmp64, "mmap_min_addr")) { - profile->mmap_min_addr = (unsigned long)tmp64; - if (((u64) profile->mmap_min_addr) == tmp64) { - profile->flags |= PFLAG_MMAP_MIN_ADDR; - } else { - info = "invalid set mmap_min_addr"; - goto fail; - } - } - if (!unpack_u32(e, &(profile->caps.allow.cap[0]), NULL)) goto fail; if (!unpack_u32(e, &(profile->caps.audit.cap[0]), NULL)) @@ -564,29 +552,6 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) if (!unpack_rlimits(e, profile)) goto fail; - size = unpack_array(e, "net_allowed_af"); - if (size) { - if (size > AF_MAX) - goto fail; - - for (i = 0; i < size; i++) { - if (!unpack_u16(e, &profile->net.allow[i], NULL)) - goto fail; - if (!unpack_u16(e, &profile->net.audit[i], NULL)) - goto fail; - if (!unpack_u16(e, &profile->net.quiet[i], NULL)) - goto fail; - } - if (!unpack_nameX(e, AA_ARRAYEND, NULL)) - goto fail; - /* - * allow unix domain and netlink sockets they are handled - * by IPC - */ - } - profile->net.allow[AF_UNIX] = 0xffff; - profile->net.allow[AF_NETLINK] = 0xffff; - /* get file rules */ profile->file.dfa = unpack_dfa(e); if (IS_ERR(profile->file.dfa)) { @@ -620,8 +585,8 @@ fail: /** * verify_head - unpack serialized stream header - * @e: serialized data read head - * @ns: Returns namespace if one is specified else NULL + * @e: serialized data read head (NOT NULL) + * @ns: Returns - namespace if one is specified else NULL (NOT NULL) * * Returns: error or 0 if header is good */ @@ -648,12 +613,6 @@ static int verify_header(struct aa_ext *e, const char **ns) return 0; } -/** - * verify_profile - Do post unpack analysis to verify profile consistency - * @profile: profile to verify - * - * Returns: 0 if passes verification else error - */ static bool verify_xindex(int xindex, int table_size) { int index, xtype; @@ -677,6 +636,12 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size) return 1; } +/** + * verify_profile - Do post unpack analysis to verify profile consistency + * @profile: profile to verify (NOT NULL) + * + * Returns: 0 if passes verification else error + */ static int verify_profile(struct aa_profile *profile) { if (aa_g_paranoid_load) { @@ -696,7 +661,7 @@ static int verify_profile(struct aa_profile *profile) * aa_unpack - unpack packed binary profile data loaded from user space * @udata: user data copied to kmem (NOT NULL) * @size: the size of the user data - * @ns: Returns namespace profile is in if specified else NULL + * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) * * Unpack user data and return refcounted allocated profile or ERR_PTR * diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index fffc3cea326..8a2d22cbd4c 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -21,7 +21,7 @@ /** * aa_getprocattr - Return the profile information for @profile * @profile: the profile to print profile info about (NOT NULL) - * @string: the string that will contain the profile and namespace info (!NULL) + * @string: Returns - string containing the profile info (NOT NULL) * * Returns: length of @string on success else error on failure * @@ -128,7 +128,10 @@ int aa_setprocattr_changehat(char *args, size_t size, int test) if (hat) { /* set up hat name vector, args guarenteed null terminated - * at args[size] + * 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) { @@ -152,7 +155,7 @@ int aa_setprocattr_changehat(char *args, size_t size, int test) * * Returns: %0 or error code if change_profile fails */ -int aa_setprocattr_changeprofile(char *fqname, int onexec, int test) +int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test) { char *name, *ns_name; diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index ca8773ab68f..ad69bf3782b 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -35,7 +35,9 @@ static void audit_cb(struct audit_buffer *ab, void *va) /** * audit_resource - audit setting resource limit * @profile: profile being enforced (NOT NULL) - * @sa: audit data (NOT NULL) + * @resoure: rlimit being auditing + * @value: value being set + * @error: error value * * Returns: 0 or sa->error else other error code on failure */ @@ -44,7 +46,7 @@ static int audit_resource(struct aa_profile *profile, unsigned int resource, { struct common_audit_data sa; - COMMON_AUDIT_DATA_INIT_NONE(&sa); + COMMON_AUDIT_DATA_INIT(&sa, NONE); sa.aad.op = OP_SETRLIMIT, sa.aad.rlim.rlim = resource; sa.aad.rlim.max = value; @@ -93,7 +95,7 @@ int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource, /** * __aa_transition_rlimits - apply new profile rlimits - * @old: old profile on task (MAYBE NULL) + * @old: old profile on task (NOT NULL) * @new: new profile with rlimits to apply (NOT NULL) */ void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new) @@ -105,7 +107,7 @@ void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new) /* for any rlimits the profile controlled reset the soft limit * to the less of the tasks hard limit and the init tasks soft limit */ - if (old && old->rlimits.mask) { + if (old->rlimits.mask) { for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) { if (old->rlimits.mask & mask) { rlim = current->signal->rlim + i; @@ -117,7 +119,7 @@ void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new) } /* set any new hard limits as dictated by the new profile */ - if (!(new && new->rlimits.mask)) + if (!new->rlimits.mask) return; for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) { if (!(new->rlimits.mask & mask)) |