2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-31 06:16:03 +00:00

Compare commits

...

2 Commits

Author SHA1 Message Date
John Johansen
ffa6034243 remove for-mainline dir from kernel patches 2008-05-27 12:04:33 +00:00
John Johansen
446f3fc533 setup AppArmor 2.3 branch 2008-05-27 12:02:45 +00:00
157 changed files with 0 additions and 27832 deletions

View File

@@ -1,58 +0,0 @@
From: tonyj@suse.de
Subject: Export audit subsystem for use by modules
Patch-mainline: no
Adds necessary export symbols for audit subsystem routines.
Changes audit_log_vformat to be externally visible (analagous to vprintf)
Patch is not in mainline -- pending AppArmor code submission to lkml
Index: linux-2.6.14/include/linux/audit.h
===================================================================
--- linux-2.6.14.orig/include/linux/audit.h
+++ linux-2.6.14/include/linux/audit.h
@@ -73,6 +73,8 @@
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
#define AUDIT_AVC_PATH 1402 /* dentry, vfsmount pair from avc */
+#define AUDIT_SD 1500 /* AppArmor (SubDomain) audit */
+
#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
/* Rule flags */
@@ -265,6 +267,9 @@ extern void audit_log(struct audit_
__attribute__((format(printf,4,5)));
extern struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type);
+extern void audit_log_vformat(struct audit_buffer *ab,
+ const char *fmt, va_list args)
+ __attribute__((format(printf,2,0)));
extern void audit_log_format(struct audit_buffer *ab,
const char *fmt, ...)
__attribute__((format(printf,2,3)));
Index: linux-2.6.14/kernel/audit.c
===================================================================
--- linux-2.6.14.orig/kernel/audit.c
+++ linux-2.6.14/kernel/audit.c
@@ -733,8 +733,8 @@ static inline int audit_expand(struct au
* room in the audit buffer, more room will be allocated and vsnprint
* will be called a second time. Currently, we assume that a printk
* can't format message larger than 1024 bytes, so we don't either. */
-static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
- va_list args)
+void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
+ va_list args)
{
int len, avail;
struct sk_buff *skb;
@@ -895,3 +895,11 @@ void audit_log(struct audit_context *ctx
audit_log_end(ab);
}
}
+
+EXPORT_SYMBOL_GPL(audit_log_start);
+EXPORT_SYMBOL_GPL(audit_log_vformat);
+EXPORT_SYMBOL_GPL(audit_log_format);
+EXPORT_SYMBOL_GPL(audit_log_untrustedstring);
+EXPORT_SYMBOL_GPL(audit_log_d_path);
+EXPORT_SYMBOL_GPL(audit_log_end);
+EXPORT_SYMBOL_GPL(audit_log);

View File

@@ -1,36 +0,0 @@
From: tonyj@suse.de
Subject: Export namespace semaphore
Patch-mainline: no
Export global namespace_sem (this used to be a per namespace semaphore).
Alas, this isn't going to win _any_ points for style.
Patch is not in mainline -- pending AppArmor code submission to lkml
Index: linux-2.6.15/fs/namespace.c
===================================================================
--- linux-2.6.15.orig/fs/namespace.c
+++ linux-2.6.15/fs/namespace.c
@@ -46,7 +46,8 @@ static int event;
static struct list_head *mount_hashtable;
static int hash_mask __read_mostly, hash_bits __read_mostly;
static kmem_cache_t *mnt_cache;
-static struct rw_semaphore namespace_sem;
+struct rw_semaphore namespace_sem;
+EXPORT_SYMBOL_GPL(namespace_sem);
/* /sys/fs */
decl_subsys(fs, NULL, NULL);
Index: linux-2.6.15/include/linux/namespace.h
===================================================================
--- linux-2.6.15.orig/include/linux/namespace.h
+++ linux-2.6.15/include/linux/namespace.h
@@ -5,6 +5,9 @@
#include <linux/mount.h>
#include <linux/sched.h>
+/* exported for AppArmor (SubDomain) */
+extern struct rw_semaphore namespace_sem;
+
struct namespace {
atomic_t count;
struct vfsmount * root;

View File

@@ -1,24 +0,0 @@
Index: b/security/Makefile
===================================================================
--- a/security/Makefile
+++ b/security/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_KEYS) += keys/
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
+obj-$(CONFIG_SECURITY_APPARMOR) += commoncap.o apparmor/
# if we don't select a security model, use the default capabilities
ifneq ($(CONFIG_SECURITY),y)
Index: b/security/Kconfig
===================================================================
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -100,6 +100,7 @@ config SECURITY_SECLVL
If you are unsure how to answer this question, answer N.
source security/selinux/Kconfig
+source security/apparmor/Kconfig
endmenu

View File

@@ -1,3 +0,0 @@
apparmor_audit.patch
apparmor_namespacesem.patch
apparmor_security.patch

View File

@@ -1 +0,0 @@
undo_netlinkrecv.patch

View File

@@ -1,14 +0,0 @@
--- linux-2.6.18.orig/security/apparmor/lsm.c
+++ linux-2.6.18/security/apparmor/lsm.c
@@ -199,9 +199,9 @@
return cap_netlink_send(sk, skb);
}
-static int subdomain_netlink_recv(struct sk_buff *skb, int cap)
+static int subdomain_netlink_recv(struct sk_buff *skb)
{
- return cap_netlink_recv(skb, cap);
+ return cap_netlink_recv(skb);
}
static void subdomain_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)

View File

@@ -1,2 +0,0 @@
undo_2.6.20_mnt_namespace.patch
undo_netlinkrecv.patch

View File

@@ -1,37 +0,0 @@
Index: linux-2.6.18.6/security/apparmor/apparmor.h
===================================================================
--- linux-2.6.18.6.orig/security/apparmor/apparmor.h
+++ linux-2.6.18.6/security/apparmor/apparmor.h
@@ -210,7 +210,7 @@ typedef int (*aa_iter) (struct subdomain
*/
struct aa_path_data {
struct dentry *root, *dentry;
- struct mnt_namespace *mnt_namespace;
+ struct namespace *namespace;
struct list_head *head, *pos;
int errno;
};
Index: linux-2.6.18.6/security/apparmor/inline.h
===================================================================
--- linux-2.6.18.6.orig/security/apparmor/inline.h
+++ linux-2.6.18.6/security/apparmor/inline.h
@@ -10,7 +10,7 @@
#ifndef __INLINE_H
#define __INLINE_H
-#include <linux/mnt_namespace.h>
+#include <linux/namespace.h>
static inline int __aa_is_confined(struct subdomain *sd)
{
@@ -323,8 +323,8 @@ static inline void __aa_path_begin(struc
{
data->dentry = dentry;
data->root = dget(rdentry->d_sb->s_root);
- data->mnt_namespace = current->nsproxy->mnt_ns;
- data->head = &data->mnt_namespace->list;
+ data->namespace = current->namespace;
+ data->head = &data->namespace->list;
data->pos = data->head->next;
prefetch(data->pos->next);
data->errno = 0;

View File

@@ -1,16 +0,0 @@
Index: linux-2.6.16.29/security/apparmor/lsm.c
===================================================================
--- linux-2.6.16.29.orig/security/apparmor/lsm.c
+++ linux-2.6.16.29/security/apparmor/lsm.c
@@ -176,9 +176,9 @@ static int apparmor_netlink_send(struct
return cap_netlink_send(sk, skb);
}
-static int apparmor_netlink_recv(struct sk_buff *skb, int cap)
+static int apparmor_netlink_recv(struct sk_buff *skb)
{
- return cap_netlink_recv(skb, cap);
+ return cap_netlink_recv(skb);
}
static void apparmor_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)

View File

@@ -1,58 +0,0 @@
From: tonyj@suse.de
Subject: Export audit subsystem for use by modules
Patch-mainline: no
Adds necessary export symbols for audit subsystem routines.
Changes audit_log_vformat to be externally visible (analagous to vprintf)
Patch is not in mainline -- pending AppArmor code submission to lkml
---
include/linux/audit.h | 5 +++++
kernel/audit.c | 6 ++++--
2 files changed, 9 insertions(+), 2 deletions(-)
Index: linux-2.6.17.9/include/linux/audit.h
===================================================================
--- linux-2.6.17.9.orig/include/linux/audit.h
+++ linux-2.6.17.9/include/linux/audit.h
@@ -96,6 +96,8 @@
#define AUDIT_LAST_KERN_ANOM_MSG 1799
#define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */
+#define AUDIT_SD 1500 /* AppArmor (SubDomain) audit */
+
#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
/* Rule flags */
@@ -357,6 +359,9 @@ extern void audit_log(struct audit_
__attribute__((format(printf,4,5)));
extern struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type);
+extern void audit_log_vformat(struct audit_buffer *ab,
+ const char *fmt, va_list args)
+ __attribute__((format(printf,2,0)));
extern void audit_log_format(struct audit_buffer *ab,
const char *fmt, ...)
__attribute__((format(printf,2,3)));
Index: linux-2.6.17.9/kernel/audit.c
===================================================================
--- linux-2.6.17.9.orig/kernel/audit.c
+++ linux-2.6.17.9/kernel/audit.c
@@ -893,8 +893,7 @@ static inline int audit_expand(struct au
* will be called a second time. Currently, we assume that a printk
* can't format message larger than 1024 bytes, so we don't either.
*/
-static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
- va_list args)
+void audit_log_vformat(struct audit_buffer *ab, const char *fmt, va_list args)
{
int len, avail;
struct sk_buff *skb;
@@ -1096,3 +1095,6 @@ EXPORT_SYMBOL(audit_log_start);
EXPORT_SYMBOL(audit_log_end);
EXPORT_SYMBOL(audit_log_format);
EXPORT_SYMBOL(audit_log);
+EXPORT_SYMBOL_GPL(audit_log_vformat);
+EXPORT_SYMBOL_GPL(audit_log_untrustedstring);
+EXPORT_SYMBOL_GPL(audit_log_d_path);

View File

@@ -1,42 +0,0 @@
From: tonyj@suse.de
Subject: Export namespace semaphore
Patch-mainline: no
Export global namespace_sem (this used to be a per namespace semaphore).
Alas, this isn't going to win _any_ points for style.
Patch is not in mainline -- pending AppArmor code submission to lkml
---
fs/namespace.c | 3 ++-
include/linux/namespace.h | 3 +++
2 files changed, 5 insertions(+), 1 deletion(-)
Index: linux-2.6.17.9/fs/namespace.c
===================================================================
--- linux-2.6.17.9.orig/fs/namespace.c
+++ linux-2.6.17.9/fs/namespace.c
@@ -46,7 +46,8 @@ static int event;
static struct list_head *mount_hashtable __read_mostly;
static int hash_mask __read_mostly, hash_bits __read_mostly;
static kmem_cache_t *mnt_cache __read_mostly;
-static struct rw_semaphore namespace_sem;
+struct rw_semaphore namespace_sem;
+EXPORT_SYMBOL_GPL(namespace_sem);
/* /sys/fs */
decl_subsys(fs, NULL, NULL);
Index: linux-2.6.17.9/include/linux/namespace.h
===================================================================
--- linux-2.6.17.9.orig/include/linux/namespace.h
+++ linux-2.6.17.9/include/linux/namespace.h
@@ -5,6 +5,9 @@
#include <linux/mount.h>
#include <linux/sched.h>
+/* exported for AppArmor (SubDomain) */
+extern struct rw_semaphore namespace_sem;
+
struct namespace {
atomic_t count;
struct vfsmount * root;

View File

@@ -1,24 +0,0 @@
Index: linux-2.6.17.9/security/Makefile
===================================================================
--- linux-2.6.17.9.orig/security/Makefile
+++ linux-2.6.17.9/security/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_KEYS) += keys/
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
+obj-$(CONFIG_SECURITY_APPARMOR) += commoncap.o apparmor/
# if we don't select a security model, use the default capabilities
ifneq ($(CONFIG_SECURITY),y)
Index: linux-2.6.17.9/security/Kconfig
===================================================================
--- linux-2.6.17.9.orig/security/Kconfig
+++ linux-2.6.17.9/security/Kconfig
@@ -100,6 +100,7 @@ config SECURITY_SECLVL
If you are unsure how to answer this question, answer N.
source security/selinux/Kconfig
+source security/apparmor/Kconfig
endmenu

View File

@@ -1,3 +0,0 @@
apparmor_audit.patch
apparmor_namespacesem.patch
apparmor_security.patch

View File

@@ -1 +0,0 @@
undo_netlinkrecv.patch

View File

@@ -1,14 +0,0 @@
--- linux-2.6.18.orig/security/apparmor/lsm.c
+++ linux-2.6.18/security/apparmor/lsm.c
@@ -199,9 +199,9 @@
return cap_netlink_send(sk, skb);
}
-static int subdomain_netlink_recv(struct sk_buff *skb, int cap)
+static int subdomain_netlink_recv(struct sk_buff *skb)
{
- return cap_netlink_recv(skb, cap);
+ return cap_netlink_recv(skb);
}
static void subdomain_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)

View File

@@ -1,2 +0,0 @@
undo_2.6.20_mnt_namespace.patch
undo_netlinkrecv.patch

View File

@@ -1,37 +0,0 @@
Index: linux-2.6.18.6/security/apparmor/apparmor.h
===================================================================
--- linux-2.6.18.6.orig/security/apparmor/apparmor.h
+++ linux-2.6.18.6/security/apparmor/apparmor.h
@@ -210,7 +210,7 @@ typedef int (*aa_iter) (struct subdomain
*/
struct aa_path_data {
struct dentry *root, *dentry;
- struct mnt_namespace *mnt_namespace;
+ struct namespace *namespace;
struct list_head *head, *pos;
int errno;
};
Index: linux-2.6.18.6/security/apparmor/inline.h
===================================================================
--- linux-2.6.18.6.orig/security/apparmor/inline.h
+++ linux-2.6.18.6/security/apparmor/inline.h
@@ -10,7 +10,7 @@
#ifndef __INLINE_H
#define __INLINE_H
-#include <linux/mnt_namespace.h>
+#include <linux/namespace.h>
static inline int __aa_is_confined(struct subdomain *sd)
{
@@ -323,8 +323,8 @@ static inline void __aa_path_begin(struc
{
data->dentry = dentry;
data->root = dget(rdentry->d_sb->s_root);
- data->mnt_namespace = current->nsproxy->mnt_ns;
- data->head = &data->mnt_namespace->list;
+ data->namespace = current->namespace;
+ data->head = &data->namespace->list;
data->pos = data->head->next;
prefetch(data->pos->next);
data->errno = 0;

View File

@@ -1,16 +0,0 @@
Index: linux-2.6.16.29/security/apparmor/lsm.c
===================================================================
--- linux-2.6.16.29.orig/security/apparmor/lsm.c
+++ linux-2.6.16.29/security/apparmor/lsm.c
@@ -176,9 +176,9 @@ static int apparmor_netlink_send(struct
return cap_netlink_send(sk, skb);
}
-static int apparmor_netlink_recv(struct sk_buff *skb, int cap)
+static int apparmor_netlink_recv(struct sk_buff *skb)
{
- return cap_netlink_recv(skb, cap);
+ return cap_netlink_recv(skb);
}
static void apparmor_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)

View File

@@ -1,54 +0,0 @@
From: tonyj@suse.de
Subject: Export audit subsystem for use by modules
Patch-mainline: no
Adds necessary export symbols for audit subsystem routines.
Changes audit_log_vformat to be externally visible (analagous to vprintf)
Patch is not in mainline -- pending AppArmor code submission to lkml
---
include/linux/audit.h | 5 +++++
kernel/audit.c | 6 ++++--
2 files changed, 9 insertions(+), 2 deletions(-)
--- linux-2.6.18.orig/include/linux/audit.h
+++ linux-2.6.18/include/linux/audit.h
@@ -100,6 +100,8 @@
#define AUDIT_LAST_KERN_ANOM_MSG 1799
#define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */
+#define AUDIT_SD 1500 /* AppArmor (SubDomain) audit */
+
#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
/* Rule flags */
@@ -466,6 +468,9 @@ extern void audit_log(struct audit_
__attribute__((format(printf,4,5)));
extern struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type);
+extern void audit_log_vformat(struct audit_buffer *ab,
+ const char *fmt, va_list args)
+ __attribute__((format(printf,2,0)));
extern void audit_log_format(struct audit_buffer *ab,
const char *fmt, ...)
__attribute__((format(printf,2,3)));
--- linux-2.6.18.orig/kernel/audit.c
+++ linux-2.6.18/kernel/audit.c
@@ -954,8 +954,7 @@ static inline int audit_expand(struct au
* will be called a second time. Currently, we assume that a printk
* can't format message larger than 1024 bytes, so we don't either.
*/
-static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
- va_list args)
+void audit_log_vformat(struct audit_buffer *ab, const char *fmt, va_list args)
{
int len, avail;
struct sk_buff *skb;
@@ -1211,3 +1210,6 @@ EXPORT_SYMBOL(audit_log_start);
EXPORT_SYMBOL(audit_log_end);
EXPORT_SYMBOL(audit_log_format);
EXPORT_SYMBOL(audit_log);
+EXPORT_SYMBOL_GPL(audit_log_vformat);
+EXPORT_SYMBOL_GPL(audit_log_untrustedstring);
+EXPORT_SYMBOL_GPL(audit_log_d_path);

View File

@@ -1,38 +0,0 @@
From: tonyj@suse.de
Subject: Export namespace semaphore
Patch-mainline: no
Export global namespace_sem (this used to be a per namespace semaphore).
Alas, this isn't going to win _any_ points for style.
Patch is not in mainline -- pending AppArmor code submission to lkml
---
fs/namespace.c | 3 ++-
include/linux/namespace.h | 3 +++
2 files changed, 5 insertions(+), 1 deletion(-)
--- linux-2.6.18.orig/fs/namespace.c
+++ linux-2.6.18/fs/namespace.c
@@ -45,7 +45,8 @@ static int event;
static struct list_head *mount_hashtable __read_mostly;
static int hash_mask __read_mostly, hash_bits __read_mostly;
static kmem_cache_t *mnt_cache __read_mostly;
-static struct rw_semaphore namespace_sem;
+struct rw_semaphore namespace_sem;
+EXPORT_SYMBOL_GPL(namespace_sem);
/* /sys/fs */
decl_subsys(fs, NULL, NULL);
--- linux-2.6.18.orig/include/linux/namespace.h
+++ linux-2.6.18/include/linux/namespace.h
@@ -5,6 +5,9 @@
#include <linux/mount.h>
#include <linux/sched.h>
+/* exported for AppArmor (SubDomain) */
+extern struct rw_semaphore namespace_sem;
+
struct namespace {
atomic_t count;
struct vfsmount * root;

View File

@@ -1,22 +0,0 @@
Index: linux-2.6.18/security/Makefile
===================================================================
--- linux-2.6.18.orig/security/Makefile
+++ linux-2.6.18/security/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_KEYS) += keys/
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
+obj-$(CONFIG_SECURITY_APPARMOR) += commoncap.o apparmor/
# if we don't select a security model, use the default capabilities
ifneq ($(CONFIG_SECURITY),y)
--- linux-2.6.17.orig/security/Kconfig
+++ linux-2.6.17/security/Kconfig
@@ -106,6 +106,7 @@ config SECURITY_SECLVL
If you are unsure how to answer this question, answer N.
source security/selinux/Kconfig
+source security/apparmor/Kconfig
endmenu

View File

@@ -1,3 +0,0 @@
apparmor_audit.patch
apparmor_namespacesem.patch
apparmor_security.patch

View File

@@ -1 +0,0 @@
undo_2.6.20_mnt_namespace.patch

View File

@@ -1,37 +0,0 @@
Index: linux-2.6.18.6/security/apparmor/apparmor.h
===================================================================
--- linux-2.6.18.6.orig/security/apparmor/apparmor.h
+++ linux-2.6.18.6/security/apparmor/apparmor.h
@@ -210,7 +210,7 @@ typedef int (*aa_iter) (struct subdomain
*/
struct aa_path_data {
struct dentry *root, *dentry;
- struct mnt_namespace *mnt_namespace;
+ struct namespace *namespace;
struct list_head *head, *pos;
int errno;
};
Index: linux-2.6.18.6/security/apparmor/inline.h
===================================================================
--- linux-2.6.18.6.orig/security/apparmor/inline.h
+++ linux-2.6.18.6/security/apparmor/inline.h
@@ -10,7 +10,7 @@
#ifndef __INLINE_H
#define __INLINE_H
-#include <linux/mnt_namespace.h>
+#include <linux/namespace.h>
static inline int __aa_is_confined(struct subdomain *sd)
{
@@ -323,8 +323,8 @@ static inline void __aa_path_begin(struc
{
data->dentry = dentry;
data->root = dget(rdentry->d_sb->s_root);
- data->mnt_namespace = current->nsproxy->mnt_ns;
- data->head = &data->mnt_namespace->list;
+ data->namespace = current->namespace;
+ data->head = &data->namespace->list;
data->pos = data->head->next;
prefetch(data->pos->next);
data->errno = 0;

View File

@@ -1,54 +0,0 @@
From: tonyj@suse.de
Subject: Export audit subsystem for use by modules
Patch-mainline: no
Adds necessary export symbols for audit subsystem routines.
Changes audit_log_vformat to be externally visible (analagous to vprintf)
Patch is not in mainline -- pending AppArmor code submission to lkml
---
include/linux/audit.h | 5 +++++
kernel/audit.c | 6 ++++--
2 files changed, 9 insertions(+), 2 deletions(-)
--- linux-2.6.18.orig/include/linux/audit.h
+++ linux-2.6.18/include/linux/audit.h
@@ -100,6 +100,8 @@
#define AUDIT_LAST_KERN_ANOM_MSG 1799
#define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */
+#define AUDIT_SD 1500 /* AppArmor (SubDomain) audit */
+
#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
/* Rule flags */
@@ -466,6 +468,9 @@ extern void audit_log(struct audit_
__attribute__((format(printf,4,5)));
extern struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type);
+extern void audit_log_vformat(struct audit_buffer *ab,
+ const char *fmt, va_list args)
+ __attribute__((format(printf,2,0)));
extern void audit_log_format(struct audit_buffer *ab,
const char *fmt, ...)
__attribute__((format(printf,2,3)));
--- linux-2.6.18.orig/kernel/audit.c
+++ linux-2.6.18/kernel/audit.c
@@ -954,8 +954,7 @@ static inline int audit_expand(struct au
* will be called a second time. Currently, we assume that a printk
* can't format message larger than 1024 bytes, so we don't either.
*/
-static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
- va_list args)
+void audit_log_vformat(struct audit_buffer *ab, const char *fmt, va_list args)
{
int len, avail;
struct sk_buff *skb;
@@ -1211,3 +1210,6 @@ EXPORT_SYMBOL(audit_log_start);
EXPORT_SYMBOL(audit_log_end);
EXPORT_SYMBOL(audit_log_format);
EXPORT_SYMBOL(audit_log);
+EXPORT_SYMBOL_GPL(audit_log_vformat);
+EXPORT_SYMBOL_GPL(audit_log_untrustedstring);
+EXPORT_SYMBOL_GPL(audit_log_d_path);

View File

@@ -1,38 +0,0 @@
From: tonyj@suse.de
Subject: Export namespace semaphore
Patch-mainline: no
Export global namespace_sem (this used to be a per namespace semaphore).
Alas, this isn't going to win _any_ points for style.
Patch is not in mainline -- pending AppArmor code submission to lkml
---
fs/namespace.c | 3 ++-
include/linux/namespace.h | 3 +++
2 files changed, 5 insertions(+), 1 deletion(-)
--- linux-2.6.18.orig/fs/namespace.c
+++ linux-2.6.18/fs/namespace.c
@@ -45,7 +45,8 @@ static int event;
static struct list_head *mount_hashtable __read_mostly;
static int hash_mask __read_mostly, hash_bits __read_mostly;
static kmem_cache_t *mnt_cache __read_mostly;
-static struct rw_semaphore namespace_sem;
+struct rw_semaphore namespace_sem;
+EXPORT_SYMBOL_GPL(namespace_sem);
/* /sys/fs */
decl_subsys(fs, NULL, NULL);
--- linux-2.6.18.orig/include/linux/namespace.h
+++ linux-2.6.18/include/linux/namespace.h
@@ -5,6 +5,9 @@
#include <linux/mount.h>
#include <linux/sched.h>
+/* exported for AppArmor (SubDomain) */
+extern struct rw_semaphore namespace_sem;
+
struct namespace {
atomic_t count;
struct vfsmount * root;

View File

@@ -1,22 +0,0 @@
Index: linux-2.6.18/security/Makefile
===================================================================
--- linux-2.6.18.orig/security/Makefile
+++ linux-2.6.18/security/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_KEYS) += keys/
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
+obj-$(CONFIG_SECURITY_APPARMOR) += commoncap.o apparmor/
# if we don't select a security model, use the default capabilities
ifneq ($(CONFIG_SECURITY),y)
--- linux-2.6.17.orig/security/Kconfig
+++ linux-2.6.17/security/Kconfig
@@ -106,6 +106,7 @@ config SECURITY_SECLVL
If you are unsure how to answer this question, answer N.
source security/selinux/Kconfig
+source security/apparmor/Kconfig
endmenu

View File

@@ -1,3 +0,0 @@
apparmor_audit.patch
apparmor_namespacesem.patch
apparmor_security.patch

View File

@@ -1 +0,0 @@
undo_2.6.20_mnt_namespace.patch

View File

@@ -1,37 +0,0 @@
Index: linux-2.6.18.6/security/apparmor/apparmor.h
===================================================================
--- linux-2.6.18.6.orig/security/apparmor/apparmor.h
+++ linux-2.6.18.6/security/apparmor/apparmor.h
@@ -210,7 +210,7 @@ typedef int (*aa_iter) (struct subdomain
*/
struct aa_path_data {
struct dentry *root, *dentry;
- struct mnt_namespace *mnt_namespace;
+ struct namespace *namespace;
struct list_head *head, *pos;
int errno;
};
Index: linux-2.6.18.6/security/apparmor/inline.h
===================================================================
--- linux-2.6.18.6.orig/security/apparmor/inline.h
+++ linux-2.6.18.6/security/apparmor/inline.h
@@ -10,7 +10,7 @@
#ifndef __INLINE_H
#define __INLINE_H
-#include <linux/mnt_namespace.h>
+#include <linux/namespace.h>
static inline int __aa_is_confined(struct subdomain *sd)
{
@@ -323,8 +323,8 @@ static inline void __aa_path_begin(struc
{
data->dentry = dentry;
data->root = dget(rdentry->d_sb->s_root);
- data->mnt_namespace = current->nsproxy->mnt_ns;
- data->head = &data->mnt_namespace->list;
+ data->namespace = current->namespace;
+ data->head = &data->namespace->list;
data->pos = data->head->next;
prefetch(data->pos->next);
data->errno = 0;

View File

@@ -1,54 +0,0 @@
From: tonyj@suse.de
Subject: Export audit subsystem for use by modules
Patch-mainline: no
Adds necessary export symbols for audit subsystem routines.
Changes audit_log_vformat to be externally visible (analagous to vprintf)
Patch is not in mainline -- pending AppArmor code submission to lkml
---
include/linux/audit.h | 5 +++++
kernel/audit.c | 6 ++++--
2 files changed, 9 insertions(+), 2 deletions(-)
--- linux-2.6.18.orig/include/linux/audit.h
+++ linux-2.6.18/include/linux/audit.h
@@ -100,6 +100,8 @@
#define AUDIT_LAST_KERN_ANOM_MSG 1799
#define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */
+#define AUDIT_SD 1500 /* AppArmor (SubDomain) audit */
+
#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
/* Rule flags */
@@ -466,6 +468,9 @@ extern void audit_log(struct audit_
__attribute__((format(printf,4,5)));
extern struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type);
+extern void audit_log_vformat(struct audit_buffer *ab,
+ const char *fmt, va_list args)
+ __attribute__((format(printf,2,0)));
extern void audit_log_format(struct audit_buffer *ab,
const char *fmt, ...)
__attribute__((format(printf,2,3)));
--- linux-2.6.18.orig/kernel/audit.c
+++ linux-2.6.18/kernel/audit.c
@@ -954,8 +954,7 @@ static inline int audit_expand(struct au
* will be called a second time. Currently, we assume that a printk
* can't format message larger than 1024 bytes, so we don't either.
*/
-static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
- va_list args)
+void audit_log_vformat(struct audit_buffer *ab, const char *fmt, va_list args)
{
int len, avail;
struct sk_buff *skb;
@@ -1211,3 +1210,6 @@ EXPORT_SYMBOL(audit_log_start);
EXPORT_SYMBOL(audit_log_end);
EXPORT_SYMBOL(audit_log_format);
EXPORT_SYMBOL(audit_log);
+EXPORT_SYMBOL_GPL(audit_log_vformat);
+EXPORT_SYMBOL_GPL(audit_log_untrustedstring);
+EXPORT_SYMBOL_GPL(audit_log_d_path);

View File

@@ -1,38 +0,0 @@
From: tonyj@suse.de
Subject: Export namespace semaphore
Patch-mainline: no
Export global namespace_sem (this used to be a per namespace semaphore).
Alas, this isn't going to win _any_ points for style.
Patch is not in mainline -- pending AppArmor code submission to lkml
---
fs/namespace.c | 3 ++-
include/linux/mnt_namespace.h | 3 +++
2 files changed, 5 insertions(+), 1 deletion(-)
--- linux-2.6.19.orig/fs/namespace.c
+++ linux-2.6.19/fs/namespace.c
@@ -37,7 +37,8 @@ static int event;
static struct list_head *mount_hashtable __read_mostly;
static int hash_mask __read_mostly, hash_bits __read_mostly;
static struct kmem_cache *mnt_cache __read_mostly;
-static struct rw_semaphore namespace_sem;
+struct rw_semaphore namespace_sem;
+EXPORT_SYMBOL_GPL(namespace_sem);
/* /sys/fs */
decl_subsys(fs, NULL, NULL);
--- linux-2.6.19.orig/include/linux/mnt_namespace.h
+++ linux-2.6.19/include/linux/mnt_namespace.h
@@ -6,6 +6,9 @@
#include <linux/sched.h>
#include <linux/nsproxy.h>
+/* exported for AppArmor (SubDomain) */
+extern struct rw_semaphore namespace_sem;
+
struct mnt_namespace {
atomic_t count;
struct vfsmount * root;

View File

@@ -1,22 +0,0 @@
Index: linux-2.6.18/security/Makefile
===================================================================
--- linux-2.6.18.orig/security/Makefile
+++ linux-2.6.18/security/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_KEYS) += keys/
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
+obj-$(CONFIG_SECURITY_APPARMOR) += commoncap.o apparmor/
# if we don't select a security model, use the default capabilities
ifneq ($(CONFIG_SECURITY),y)
--- linux-2.6.17.orig/security/Kconfig
+++ linux-2.6.17/security/Kconfig
@@ -106,6 +106,7 @@ config SECURITY_SECLVL
If you are unsure how to answer this question, answer N.
source security/selinux/Kconfig
+source security/apparmor/Kconfig
endmenu

View File

@@ -1,3 +0,0 @@
apparmor_audit.patch
apparmor_namespacesem.patch
apparmor_security.patch

View File

@@ -1,29 +0,0 @@
For each kernel release there is a directory named using the kernel version #
The files managed by svn in a release directory are:
patches/ - directory of kernel patches without the apparmor module in the
series. These are used to build the apparmor.patch and
apparmor-fullseries.patch
postapply/ - directory of patches to the module code in head of svn enabling
it to build for a given kernel.
current/ - patches against current branch
nextgen/ - patches against nextgen branch
when a release tar ball is built it will be automatically generated using
the tip of module and module-nextgen and the release dir in the tar ball
will be populated with:
kernel version #
current/ - contains the patches for the current branch of apparmor
apparmor-Kversion#-svnversion#-fullseries.patch - single kernel patcch
patches/ - quilt series to patch kernel including apparmor patch
nextgen - contains the patches for the nextgen branch of apparmor
apparmor-Kversion#-svnversion#-fullseries.patch - single kernel patch
patches/ - quilt series to patch kernel including apparmor patch
the fullseries kernel patches are equivalent to the quilt series flattened
into a single patch

View File

@@ -1,12 +0,0 @@
Contents of apparmor kernel patch release
current/ - contains the patches for the current branch of apparmor
apparmor-Kversion#-svnversion#-fullseries.patch - single kernel patcch
patches/ - quilt series to patch kernel including apparmor patch
nextgen - contains the patches for the nextgen branch of apparmor
apparmor-Kversion#-svnversion#-fullseries.patch - single kernel patch
patches/ - quilt series to patch kernel including apparmor patch
the fullseries kernel patches are equivalent to the quilt series flattened
into a single patch

View File

@@ -1,11 +0,0 @@
AppArmor kernel patches
This directory should contain 2 patches that do the same thing. The patches
directory contains a quilt series of distinct patches necessary to apply
apparmor to the kernel.
The fullseries patch is the quilt series combined into a single patch.
The apparmor patches name convention is
apparmor-${KERNEL_VERSION}-v${APPARMOR_MODULE_REPO_VERSION}.patch

View File

@@ -1,144 +0,0 @@
From: John Johansen <jjohansen@suse.de>
Subject: Fix __d_path to allow for old and new behavior bnc#380763
Fix __d_path so that it can be told whether or not to connect
disconnect path to the root. This is easier and more efficient
than trying to reconnect these paths for d_path and get_cwd
after the fact.
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/dcache.c | 57 ++++++++++++++++++-------------------------------
fs/namespace.c | 2 -
include/linux/dcache.h | 2 -
3 files changed, 24 insertions(+), 37 deletions(-)
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1772,6 +1772,7 @@ shouldnt_be_hashed:
* @buffer: buffer to return value in
* @buflen: buffer length
* @fail_deleted: what to return for deleted files
+ * @disconnect: don't return a path starting with / when disconnected
*
* Convert a dentry into an ASCII path name. If the entry has been deleted,
* then if @fail_deleted is true, ERR_PTR(-ENOENT) is returned. Otherwise,
@@ -1784,9 +1785,10 @@ shouldnt_be_hashed:
*/
char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
struct dentry *root, struct vfsmount *rootmnt,
- char *buffer, int buflen, int fail_deleted)
+ char *buffer, int buflen, int fail_deleted, int disconnect)
{
- int namelen, is_slash, vfsmount_locked = 0;
+ int namelen, vfsmount_locked = 0;
+ const unsigned char *name;
if (buflen < 2)
return ERR_PTR(-ENAMETOOLONG);
@@ -1847,27 +1849,26 @@ global_root:
* unconnected dentry, or the file is on a pseudo filesystem.
*/
namelen = dentry->d_name.len;
- is_slash = (namelen == 1 && *dentry->d_name.name == '/');
- if (is_slash || (dentry->d_sb->s_flags & MS_NOUSER)) {
- /*
- * Make sure we won't return a pathname starting with '/'.
- *
- * Historically, we also glue together the root dentry and
- * remaining name for pseudo filesystems like pipefs, which
- * have the MS_NOUSER flag set. This results in pathnames
- * like "pipe:[439336]".
- */
- if (*buffer == '/') {
- buffer++;
- buflen++;
- }
- if (is_slash)
- goto out;
+ name = dentry->d_name.name;
+
+ /*
+ * If this is a root dentry, then overwrite the slash. This
+ * will also DTRT with pseudo filesystems which have root
+ * dentries named "foo:".
+ */
+ if (IS_ROOT(dentry)) {
+ buffer++;
+ buflen++;
+ }
+ if (disconnect && *name == '/') {
+ /* Make sure we won't return a pathname starting with '/' */
+ name++;
+ namelen--;
}
if (buflen < namelen)
goto Elong;
buffer -= namelen;
- memcpy(buffer, dentry->d_name.name, namelen);
+ memcpy(buffer, name, namelen);
goto out;
Elong:
@@ -1875,18 +1876,6 @@ Elong:
goto out;
}
-static char *__connect_d_path(char *path, char *buffer)
-{
- if (!IS_ERR(path) && *path != '/') {
- /* Pretend that disconnected paths are hanging off the root. */
- if (path == buffer)
- path = ERR_PTR(-ENAMETOOLONG);
- else
- *--path = '/';
- }
- return path;
-}
-
/* write full pathname into buffer and return start of pathname */
char *d_path(struct dentry *dentry, struct vfsmount *vfsmnt, char *buf,
int buflen)
@@ -1909,8 +1898,7 @@ char *d_path(struct dentry *dentry, stru
rootmnt = mntget(current->fs->rootmnt);
root = dget(current->fs->root);
read_unlock(&current->fs->lock);
- res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen, 0);
- res = __connect_d_path(res, buf);
+ res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen, 0, 0);
dput(root);
mntput(rootmnt);
return res;
@@ -1972,8 +1960,7 @@ asmlinkage long sys_getcwd(char __user *
root = dget(current->fs->root);
read_unlock(&current->fs->lock);
- cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE, 1);
- cwd = __connect_d_path(cwd, page);
+ cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE, 1, 0);
error = PTR_ERR(cwd);
if (IS_ERR(cwd))
goto out;
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1901,7 +1901,7 @@ char *d_namespace_path(struct dentry *de
mntput(rootmnt);
if (nsrootmnt)
root = dget(nsrootmnt->mnt_root);
- res = __d_path(dentry, vfsmnt, root, nsrootmnt, buf, buflen, 1);
+ res = __d_path(dentry, vfsmnt, root, nsrootmnt, buf, buflen, 1, 1);
dput(root);
mntput(nsrootmnt);
/* Prevent empty path for lazily unmounted filesystems. */
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -301,7 +301,7 @@ extern int d_validate(struct dentry *, s
extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...);
extern char *__d_path(struct dentry *, struct vfsmount *, struct dentry *,
- struct vfsmount *, char *, int, int);
+ struct vfsmount *, char *, int, int, int);
extern char * d_path(struct dentry *, struct vfsmount *, char *, int);
/* Allocation counts.. */

View File

@@ -1,745 +0,0 @@
---
security/apparmor/apparmor.h | 14 -
security/apparmor/apparmorfs.c | 2
security/apparmor/inline.h | 14 -
security/apparmor/main.c | 490 +++++++++++++++++++----------------
security/apparmor/match.c | 9
security/apparmor/module_interface.c | 10
6 files changed, 309 insertions(+), 230 deletions(-)
--- a/security/apparmor/apparmor.h
+++ b/security/apparmor/apparmor.h
@@ -44,8 +44,7 @@
AA_EXEC_MOD_2 | AA_EXEC_MOD_3 | \
AA_EXEC_MOD_4)
-#define AA_EXEC_TYPE (MAY_EXEC | AA_EXEC_UNSAFE | \
- AA_EXEC_MODIFIERS)
+#define AA_EXEC_TYPE (AA_EXEC_UNSAFE | AA_EXEC_MODIFIERS)
#define AA_EXEC_UNCONFINED AA_EXEC_MOD_0
#define AA_EXEC_INHERIT AA_EXEC_MOD_1
@@ -85,6 +84,10 @@
AA_AUDIT_FIELD)
#define AA_VALID_PERM_MASK (AA_FILE_PERMS | AA_SHARED_PERMS)
+
+/* audit bits for the second accept field */
+#define AUDIT_FILE_MASK 0x1fc07f
+#define AUDIT_QUIET_MASK(mask) ((mask >> 7) & AUDIT_FILE_MASK)
#define AA_VALID_PERM2_MASK 0x0fffffff
#define AA_SECURE_EXEC_NEEDED 1
@@ -179,6 +182,9 @@ struct aa_profile {
int isstale;
kernel_cap_t capabilities;
+ kernel_cap_t audit_caps;
+ kernel_cap_t quiet_caps;
+
struct kref count;
struct list_head task_contexts;
spinlock_t lock;
@@ -226,7 +232,7 @@ struct aa_audit {
const char *name;
const char *name2;
const char *name3;
- int request_mask, denied_mask;
+ int request_mask, denied_mask, audit_mask;
struct iattr *iattr;
pid_t task, parent;
int error_code;
@@ -331,7 +337,7 @@ extern struct aa_dfa *aa_match_alloc(voi
extern void aa_match_free(struct aa_dfa *dfa);
extern int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size);
extern int verify_dfa(struct aa_dfa *dfa);
-extern unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str);
+extern unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str, int *);
extern unsigned int aa_dfa_next_state(struct aa_dfa *dfa, unsigned int start,
const char *str);
extern unsigned int aa_match_state(struct aa_dfa *dfa, unsigned int start,
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -89,7 +89,7 @@ static struct file_operations apparmorfs
static ssize_t aa_matching_read(struct file *file, char __user *buf,
size_t size, loff_t *ppos)
{
- const char *matching = "pattern=aadfa perms=rwxamlk/ user::other";
+ const char *matching = "pattern=aadfa audit perms=rwxamlk/ user::other";
return simple_read_from_buffer(buf, size, ppos, matching,
strlen(matching));
--- a/security/apparmor/inline.h
+++ b/security/apparmor/inline.h
@@ -232,9 +232,19 @@ static inline void unlock_both_profiles(
}
}
-static inline unsigned int aa_match(struct aa_dfa *dfa, const char *pathname)
+static inline unsigned int aa_match(struct aa_dfa *dfa, const char *pathname,
+ int *audit_mask)
{
- return dfa ? aa_dfa_match(dfa, pathname) : 0;
+ if (dfa)
+ return aa_dfa_match(dfa, pathname, audit_mask);
+ if (audit_mask)
+ *audit_mask = 0;
+ return 0;
+}
+
+static inline int dfa_audit_mask(struct aa_dfa *dfa, unsigned int state)
+{
+ return ACCEPT_TABLE2(dfa)[state];
}
#endif /* __INLINE_H__ */
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -36,204 +36,6 @@ static int aa_inode_mode(struct inode *i
return AA_OTHER_SHIFT;
}
-/**
- * aa_file_denied - check for @mask access on a file
- * @profile: profile to check against
- * @name: pathname of file
- * @mask: permission mask requested for file
- *
- * Return %0 on success, or else the permissions in @mask that the
- * profile denies.
- */
-static int aa_file_denied(struct aa_profile *profile, const char *name,
- int mask)
-{
- return (mask & ~aa_match(profile->file_rules, name));
-}
-
-/**
- * aa_link_denied - check for permission to link a file
- * @profile: profile to check against
- * @link: pathname of link being created
- * @target: pathname of target to be linked to
- * @target_mode: UGO shift for target inode
- * @request_mask: the permissions subset valid only if link succeeds
- * Return %0 on success, or else the permissions that the profile denies.
- */
-static int aa_link_denied(struct aa_profile *profile, const char *link,
- const char *target, int target_mode,
- int *request_mask)
-{
- unsigned int state;
- int l_mode, t_mode, denied_mask = 0;
- int link_mask = AA_MAY_LINK << target_mode;
-
- *request_mask = link_mask;
-
- l_mode = aa_match_state(profile->file_rules, DFA_START, link, &state);
- if (l_mode & link_mask) {
- int mode;
- /* test to see if target can be paired with link */
- state = aa_dfa_null_transition(profile->file_rules, state);
- mode = aa_match_state(profile->file_rules, state, target,
- NULL);
-
- if (!(mode & link_mask))
- denied_mask |= link_mask;
- /* return if link subset test is not required */
- if (!(mode & (AA_LINK_SUBSET_TEST << target_mode)))
- return denied_mask;
- }
-
- /* Do link perm subset test
- * If a subset test is required a permission subset test of the
- * perms for the link are done against the user::other of the
- * target's 'r', 'w', 'x', 'a', 'k', and 'm' permissions.
- *
- * If the link has 'x', an exact match of all the execute flags
- * must match.
- */
- denied_mask |= ~l_mode & link_mask;
-
- t_mode = aa_match(profile->file_rules, target);
-
- /* For actual subset test ignore valid-profile-transition flags,
- * and link bits
- */
- l_mode &= AA_FILE_PERMS & ~AA_LINK_BITS;
- t_mode &= AA_FILE_PERMS & ~AA_LINK_BITS;
-
- *request_mask = l_mode | link_mask;
-
- if (l_mode) {
- denied_mask |= l_mode & ~t_mode;
- if ((l_mode & AA_EXEC_BITS) &&
- (l_mode & ALL_AA_EXEC_TYPE) !=
- (t_mode & ALL_AA_EXEC_TYPE))
- denied_mask = (denied_mask & ~ALL_AA_EXEC_TYPE) |
- (l_mode & ALL_AA_EXEC_TYPE);
- }
-
- return denied_mask;
-}
-
-/**
- * aa_get_name - compute the pathname of a file
- * @dentry: dentry of the file
- * @mnt: vfsmount of the file
- * @buffer: buffer that aa_get_name() allocated
- * @check: AA_CHECK_DIR is set if the file is a directory
- *
- * Returns a pointer to the beginning of the pathname (which usually differs
- * from the beginning of the buffer), or an error code.
- *
- * We need @check to indicate whether the file is a directory or not because
- * the file may not yet exist, and so we cannot check the inode's file type.
- */
-static char *aa_get_name(struct dentry *dentry, struct vfsmount *mnt,
- char **buffer, int check)
-{
- char *name;
- int is_dir, size = 256;
-
- is_dir = (check & AA_CHECK_DIR) ? 1 : 0;
-
- for (;;) {
- char *buf = kmalloc(size, GFP_KERNEL);
- if (!buf)
- return ERR_PTR(-ENOMEM);
-
- name = d_namespace_path(dentry, mnt, buf, size - is_dir);
- if (!IS_ERR(name)) {
- if (name[0] != '/') {
- /*
- * This dentry is not connected to the
- * namespace root -- reject access.
- */
- kfree(buf);
- return ERR_PTR(-ENOENT);
- }
- if (is_dir && name[1] != '\0') {
- /*
- * Append "/" to the pathname. The root
- * directory is a special case; it already
- * ends in slash.
- */
- buf[size - 2] = '/';
- buf[size - 1] = '\0';
- }
-
- *buffer = buf;
- return name;
- }
- if (PTR_ERR(name) != -ENAMETOOLONG)
- return name;
-
- kfree(buf);
- size <<= 1;
- if (size > apparmor_path_max)
- return ERR_PTR(-ENAMETOOLONG);
- }
-}
-
-static inline void aa_put_name_buffer(char *buffer)
-{
- kfree(buffer);
-}
-
-/**
- * aa_perm_dentry - check if @profile allows @mask for a file
- * @profile: profile to check against
- * @dentry: dentry of the file
- * @mnt: vfsmount o the file
- * @sa: audit context
- * @mask: requested profile permissions
- * @check: kind of check to perform
- *
- * Returns 0 upon success, or else an error code.
- *
- * @check indicates the file type, and whether the file was accessed through
- * an open file descriptor (AA_CHECK_FD) or not.
- */
-static int aa_perm_dentry(struct aa_profile *profile, struct dentry *dentry,
- struct vfsmount *mnt, struct aa_audit *sa, int check)
-{
- int error;
- char *buffer = NULL;
-
- sa->name = aa_get_name(dentry, mnt, &buffer, check);
- sa->request_mask <<= aa_inode_mode(dentry->d_inode);
- if (IS_ERR(sa->name)) {
- /*
- * deleted files are given a pass on permission checks when
- * accessed through a file descriptor.
- */
- if (PTR_ERR(sa->name) == -ENOENT && (check & AA_CHECK_FD))
- sa->denied_mask = 0;
- else {
- sa->denied_mask = sa->request_mask;
- sa->error_code = PTR_ERR(sa->name);
- if (sa->error_code == -ENOENT)
- sa->info = "Failed name resolution - object not a valid entry";
- else if (sa->error_code == -ENAMETOOLONG)
- sa->info = "Failed name resolution - name too long";
- else
- sa->info = "Failed name resolution";
- }
- sa->name = NULL;
- } else
- sa->denied_mask = aa_file_denied(profile, sa->name,
- sa->request_mask);
-
- if (!sa->denied_mask)
- sa->error_code = 0;
-
- error = aa_audit(profile, sa);
- aa_put_name_buffer(buffer);
-
- return error;
-}
-
int alloc_default_namespace(void)
{
struct aa_namespace *ns;
@@ -471,20 +273,259 @@ int aa_audit(struct aa_profile *profile,
int type = AUDIT_APPARMOR_DENIED;
struct audit_context *audit_cxt;
- if (likely(!sa->error_code)) {
- if (likely(!PROFILE_AUDIT(profile)))
- /* nothing to log */
- return 0;
- else
- type = AUDIT_APPARMOR_AUDIT;
- } else if (PROFILE_COMPLAIN(profile)) {
+ if (likely(!sa->error_code))
+ type = AUDIT_APPARMOR_AUDIT;
+ else if (PROFILE_COMPLAIN(profile))
type = AUDIT_APPARMOR_ALLOWED;
- }
audit_cxt = apparmor_logsyscall ? current->audit_context : NULL;
return aa_audit_base(profile, sa, audit_cxt, type);
}
+static int aa_audit_file(struct aa_profile *profile, struct aa_audit *sa)
+{
+ if (likely(!sa->error_code)) {
+ int mask = sa->audit_mask & AUDIT_FILE_MASK;
+
+ if (unlikely(PROFILE_AUDIT(profile)))
+ mask |= AUDIT_FILE_MASK;
+
+ if (likely(!(sa->request_mask & mask)))
+ return 0;
+
+ /* mask off perms that are not being force audited */
+ sa->request_mask &= mask | ALL_AA_EXEC_TYPE;
+ } else {
+ int mask = AUDIT_QUIET_MASK(sa->audit_mask);
+
+ if (!(sa->denied_mask & ~mask))
+ return sa->error_code;
+
+ /* mask off perms whose denial is being silenced */
+ sa->denied_mask &= (~mask) | ALL_AA_EXEC_TYPE;
+ }
+
+ return aa_audit(profile, sa);
+}
+
+static int aa_audit_caps(struct aa_profile *profile, struct aa_audit *sa,
+ int cap)
+{
+ if (likely(!sa->error_code)) {
+ if (likely(!PROFILE_AUDIT(profile) &&
+ !cap_raised(profile->audit_caps, cap)))
+ return 0;
+ }
+
+ /* quieting of capabilities is handled the caps_logged cache */
+ return aa_audit(profile, sa);
+}
+
+/**
+ * aa_file_denied - check for @mask access on a file
+ * @profile: profile to check against
+ * @name: pathname of file
+ * @mask: permission mask requested for file
+ * @audit_mask: return audit mask for the match
+ *
+ * Return %0 on success, or else the permissions in @mask that the
+ * profile denies.
+ */
+static int aa_file_denied(struct aa_profile *profile, const char *name,
+ int mask, int *audit_mask)
+{
+ return (mask & ~aa_match(profile->file_rules, name, audit_mask));
+}
+
+/**
+ * aa_link_denied - check for permission to link a file
+ * @profile: profile to check against
+ * @link: pathname of link being created
+ * @target: pathname of target to be linked to
+ * @target_mode: UGO shift for target inode
+ * @request_mask: the permissions subset valid only if link succeeds
+ * @audit_mask: return the audit_mask for the link permission
+ * Return %0 on success, or else the permissions that the profile denies.
+ */
+static int aa_link_denied(struct aa_profile *profile, const char *link,
+ const char *target, int target_mode,
+ int *request_mask, int *audit_mask)
+{
+ unsigned int state;
+ int l_mode, t_mode, denied_mask = 0;
+ int link_mask = AA_MAY_LINK << target_mode;
+
+ *request_mask = link_mask;
+
+ l_mode = aa_match_state(profile->file_rules, DFA_START, link, &state);
+
+ if (l_mode & link_mask) {
+ int mode;
+ /* test to see if target can be paired with link */
+ state = aa_dfa_null_transition(profile->file_rules, state);
+ mode = aa_match_state(profile->file_rules, state, target,
+ &state);
+
+ if (!(mode & link_mask))
+ denied_mask |= link_mask;
+
+ *audit_mask = dfa_audit_mask(profile->file_rules, state);
+
+ /* return if link subset test is not required */
+ if (!(mode & (AA_LINK_SUBSET_TEST << target_mode)))
+ return denied_mask;
+ }
+
+ /* Do link perm subset test
+ * If a subset test is required a permission subset test of the
+ * perms for the link are done against the user::other of the
+ * target's 'r', 'w', 'x', 'a', 'k', and 'm' permissions.
+ *
+ * If the link has 'x', an exact match of all the execute flags
+ * must match.
+ */
+ denied_mask |= ~l_mode & link_mask;
+
+ t_mode = aa_match(profile->file_rules, target, NULL);
+
+ /* For actual subset test ignore valid-profile-transition flags,
+ * and link bits
+ */
+ l_mode &= AA_FILE_PERMS & ~AA_LINK_BITS;
+ t_mode &= AA_FILE_PERMS & ~AA_LINK_BITS;
+
+ *request_mask = l_mode | link_mask;
+
+ if (l_mode) {
+ denied_mask |= l_mode & ~t_mode;
+ if ((l_mode & AA_EXEC_BITS) &&
+ (l_mode & ALL_AA_EXEC_TYPE) !=
+ (t_mode & ALL_AA_EXEC_TYPE))
+ denied_mask = (denied_mask & ~ALL_AA_EXEC_TYPE) |
+ (l_mode & (ALL_AA_EXEC_TYPE | AA_EXEC_BITS));
+ }
+
+ return denied_mask;
+}
+
+/**
+ * aa_get_name - compute the pathname of a file
+ * @dentry: dentry of the file
+ * @mnt: vfsmount of the file
+ * @buffer: buffer that aa_get_name() allocated
+ * @check: AA_CHECK_DIR is set if the file is a directory
+ *
+ * Returns a pointer to the beginning of the pathname (which usually differs
+ * from the beginning of the buffer), or an error code.
+ *
+ * We need @check to indicate whether the file is a directory or not because
+ * the file may not yet exist, and so we cannot check the inode's file type.
+ */
+static char *aa_get_name(struct dentry *dentry, struct vfsmount *mnt,
+ char **buffer, int check)
+{
+ char *name;
+ int is_dir, size = 256;
+
+ is_dir = (check & AA_CHECK_DIR) ? 1 : 0;
+
+ for (;;) {
+ char *buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ name = d_namespace_path(dentry, mnt, buf, size - is_dir);
+ if (!IS_ERR(name)) {
+ if (name[0] != '/') {
+ /*
+ * This dentry is not connected to the
+ * namespace root -- reject access.
+ */
+ kfree(buf);
+ return ERR_PTR(-ENOENT);
+ }
+ if (is_dir && name[1] != '\0') {
+ /*
+ * Append "/" to the pathname. The root
+ * directory is a special case; it already
+ * ends in slash.
+ */
+ buf[size - 2] = '/';
+ buf[size - 1] = '\0';
+ }
+
+ *buffer = buf;
+ return name;
+ }
+ if (PTR_ERR(name) != -ENAMETOOLONG)
+ return name;
+
+ kfree(buf);
+ size <<= 1;
+ if (size > apparmor_path_max)
+ return ERR_PTR(-ENAMETOOLONG);
+ }
+}
+
+static inline void aa_put_name_buffer(char *buffer)
+{
+ kfree(buffer);
+}
+
+/**
+ * aa_perm_dentry - check if @profile allows @mask for a file
+ * @profile: profile to check against
+ * @dentry: dentry of the file
+ * @mnt: vfsmount o the file
+ * @sa: audit context
+ * @mask: requested profile permissions
+ * @check: kind of check to perform
+ *
+ * Returns 0 upon success, or else an error code.
+ *
+ * @check indicates the file type, and whether the file was accessed through
+ * an open file descriptor (AA_CHECK_FD) or not.
+ */
+static int aa_perm_dentry(struct aa_profile *profile, struct dentry *dentry,
+ struct vfsmount *mnt, struct aa_audit *sa, int check)
+{
+ int error;
+ char *buffer = NULL;
+
+ sa->name = aa_get_name(dentry, mnt, &buffer, check);
+ sa->request_mask <<= aa_inode_mode(dentry->d_inode);
+ if (IS_ERR(sa->name)) {
+ /*
+ * deleted files are given a pass on permission checks when
+ * accessed through a file descriptor.
+ */
+ if (PTR_ERR(sa->name) == -ENOENT && (check & AA_CHECK_FD))
+ sa->denied_mask = 0;
+ else {
+ sa->denied_mask = sa->request_mask;
+ sa->error_code = PTR_ERR(sa->name);
+ if (sa->error_code == -ENOENT)
+ sa->info = "Failed name resolution - object not a valid entry";
+ else if (sa->error_code == -ENAMETOOLONG)
+ sa->info = "Failed name resolution - name too long";
+ else
+ sa->info = "Failed name resolution";
+ }
+ sa->name = NULL;
+ } else
+ sa->denied_mask = aa_file_denied(profile, sa->name,
+ sa->request_mask,
+ &sa->audit_mask);
+
+ if (!sa->denied_mask)
+ sa->error_code = 0;
+
+ error = aa_audit_file(profile, sa);
+ aa_put_name_buffer(buffer);
+
+ return error;
+}
+
/**
* aa_attr - check if attribute change is allowed
* @profile: profile to check against
@@ -621,10 +662,11 @@ int aa_perm_path(struct aa_profile *prof
else
sa.request_mask = mask << AA_OTHER_SHIFT;
- sa.denied_mask = aa_file_denied(profile, name, sa.request_mask) ;
+ sa.denied_mask = aa_file_denied(profile, name, sa.request_mask,
+ &sa.audit_mask) ;
sa.error_code = sa.denied_mask ? -EACCES : 0;
- return aa_audit(profile, &sa);
+ return aa_audit_file(profile, &sa);
}
/**
@@ -660,7 +702,7 @@ int aa_capability(struct aa_task_context
sa.name = capability_names[cap];
sa.error_code = error;
- error = aa_audit(cxt->profile, &sa);
+ error = aa_audit_caps(cxt->profile, &sa, cap);
return error;
}
@@ -709,11 +751,12 @@ int aa_link(struct aa_profile *profile,
if (sa.name && sa.name2) {
sa.denied_mask = aa_link_denied(profile, sa.name, sa.name2,
aa_inode_mode(target->d_inode),
- &sa.request_mask);
+ &sa.request_mask,
+ &sa.audit_mask);
sa.error_code = sa.denied_mask ? -EACCES : 0;
}
- error = aa_audit(profile, &sa);
+ error = aa_audit_file(profile, &sa);
aa_put_name_buffer(buffer);
aa_put_name_buffer(buffer2);
@@ -802,8 +845,8 @@ aa_register_find(struct aa_profile *prof
new_profile =
aa_dup_profile(profile->ns->null_complain_profile);
} else {
- aa_audit_reject(profile, sa);
- return ERR_PTR(-EACCES); /* was -EPERM */
+ sa->error_code = -EACCES;
+ return ERR_PTR(aa_audit_file(profile, sa));
}
} else {
/* Only way we can get into this code is if task
@@ -863,7 +906,8 @@ repeat:
/* Confined task, determine what mode inherit, unconfined or
* mandatory to load new profile
*/
- exec_mode = aa_match(profile->file_rules, filename);
+ exec_mode = aa_match(profile->file_rules, filename,
+ &sa.audit_mask);
if (exec_mode & sa.request_mask) {
switch ((exec_mode >> shift) & AA_EXEC_MODIFIERS) {
@@ -906,6 +950,9 @@ repeat:
break;
}
+ } else if (sa.request_mask & AUDIT_QUIET_MASK(sa.audit_mask)) {
+ /* quiet failed exit */
+ new_profile = ERR_PTR(-EACCES);
} else if (complain) {
/* There was no entry in calling profile
* describing mode to execute image in.
@@ -916,8 +963,8 @@ repeat:
exec_mode |= AA_EXEC_UNSAFE << shift;
} else {
sa.denied_mask = sa.request_mask;
- aa_audit_reject(profile, &sa);
- new_profile = ERR_PTR(-EPERM);
+ sa.error_code = -EACCES;
+ new_profile = ERR_PTR(aa_audit_file(profile, &sa));
}
} else {
/* Unconfined task, load profile if it exists */
@@ -973,6 +1020,7 @@ repeat:
sa.info = "set profile";
aa_audit_hint(new_profile, &sa);
}
+
cleanup:
aa_put_name_buffer(buffer);
if (IS_ERR(new_profile))
@@ -1149,7 +1197,7 @@ repeat:
if (PROFILE_COMPLAIN(profile) ||
(ns == profile->ns &&
- (aa_match(profile->file_rules, name) & AA_CHANGE_PROFILE)))
+ (aa_match(profile->file_rules, name, NULL) & AA_CHANGE_PROFILE)))
error = do_change_profile(profile, ns, name, 0, 0, &sa);
else {
/* check for a rule with a namespace prepended */
@@ -1356,9 +1404,11 @@ void aa_change_task_context(struct task_
call_rcu(&old_cxt->rcu, free_aa_task_context_rcu_callback);
}
if (new_cxt) {
- /* clear the caps_logged cache, so that new profile/hat has
- * chance to emit its own set of cap messages */
- new_cxt->caps_logged = CAP_EMPTY_SET;
+ /* set the caps_logged cache to the quiet_caps mask
+ * this has the effect of quieting caps that are not
+ * supposed to be logged
+ */
+ new_cxt->caps_logged = profile->quiet_caps;
new_cxt->cookie = cookie;
new_cxt->task = task;
new_cxt->profile = aa_dup_profile(profile);
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -14,6 +14,7 @@
#include <linux/errno.h>
#include "apparmor.h"
#include "match.h"
+#include "inline.h"
static struct table_header *unpack_table(void *blob, size_t bsize)
{
@@ -295,13 +296,17 @@ unsigned int aa_dfa_null_transition(stru
* aa_dfa_match - find accept perm for @str in @dfa
* @dfa: the dfa to match @str against
* @str: the string to match against the dfa
+ * @audit_mask: the audit_mask for the final state
*
* aa_dfa_match will match @str and return the accept perms for the
* final state.
*/
-unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str)
+unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str, int *audit_mask)
{
- return ACCEPT_TABLE(dfa)[aa_dfa_next_state(dfa, DFA_START, str)];
+ int state = aa_dfa_next_state(dfa, DFA_START, str);
+ if (audit_mask)
+ *audit_mask = dfa_audit_mask(dfa, state);
+ return ACCEPT_TABLE(dfa)[state];
}
/**
--- a/security/apparmor/module_interface.c
+++ b/security/apparmor/module_interface.c
@@ -310,6 +310,10 @@ static struct aa_profile *aa_unpack_prof
if (!aa_is_u32(e, &(profile->capabilities), NULL))
goto fail;
+ if (!aa_is_u32(e, &(profile->audit_caps), NULL))
+ goto fail;
+ if (!aa_is_u32(e, &(profile->quiet_caps), NULL))
+ goto fail;
/* get file rules */
profile->file_rules = aa_unpack_dfa(e);
@@ -317,6 +321,10 @@ static struct aa_profile *aa_unpack_prof
error = PTR_ERR(profile->file_rules);
profile->file_rules = NULL;
goto fail;
+ if (!aa_is_u16(e, &profile->audit_network[i], NULL))
+ goto fail;
+ if (!aa_is_u16(e, &profile->quiet_network[i], NULL))
+ goto fail;
}
if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
@@ -360,7 +368,7 @@ static int aa_verify_header(struct aa_ex
}
/* check that the interface version is currently supported */
- if (e->version != 3) {
+ if (e->version != 4) {
struct aa_audit sa;
memset(&sa, 0, sizeof(sa));
sa.operation = operation;

View File

@@ -1,72 +0,0 @@
From: Tony Jones <tonyj@suse.de>
Subject: Export audit subsystem for use by modules
Update kenel audit range comments to show AppArmor's registered range of
1500-1599. This range used to be reserved for LSPP but LSPP uses the
SE Linux range and the range was given to AppArmor.
Adds necessary export symbols for audit subsystem routines.
Changes audit_log_vformat to be externally visible (analagous to vprintf)
Patch is not in mainline -- pending AppArmor code submission to lkml
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
include/linux/audit.h | 12 +++++++++++-
kernel/audit.c | 6 ++++--
2 files changed, 15 insertions(+), 3 deletions(-)
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -33,7 +33,7 @@
* 1200 - 1299 messages internal to the audit daemon
* 1300 - 1399 audit event messages
* 1400 - 1499 SE Linux use
- * 1500 - 1599 kernel LSPP events
+ * 1500 - 1599 AppArmor use
* 1600 - 1699 kernel crypto events
* 1700 - 1799 kernel anomaly records
* 1800 - 1999 future kernel use (maybe integrity labels and related events)
@@ -116,6 +116,13 @@
#define AUDIT_MAC_IPSEC_DELSPD 1414 /* Not used */
#define AUDIT_MAC_IPSEC_EVENT 1415 /* Audit an IPSec event */
+#define AUDIT_APPARMOR_AUDIT 1501 /* AppArmor audited grants */
+#define AUDIT_APPARMOR_ALLOWED 1502 /* Allowed Access for learning */
+#define AUDIT_APPARMOR_DENIED 1503
+#define AUDIT_APPARMOR_HINT 1504 /* Process Tracking information */
+#define AUDIT_APPARMOR_STATUS 1505 /* Changes in config */
+#define AUDIT_APPARMOR_ERROR 1506 /* Internal AppArmor Errors */
+
#define AUDIT_FIRST_KERN_ANOM_MSG 1700
#define AUDIT_LAST_KERN_ANOM_MSG 1799
#define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */
@@ -513,6 +520,9 @@ extern void audit_log(struct audit_
__attribute__((format(printf,4,5)));
extern struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type);
+extern void audit_log_vformat(struct audit_buffer *ab,
+ const char *fmt, va_list args)
+ __attribute__((format(printf,2,0)));
extern void audit_log_format(struct audit_buffer *ab,
const char *fmt, ...)
__attribute__((format(printf,2,3)));
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1215,8 +1215,7 @@ static inline int audit_expand(struct au
* will be called a second time. Currently, we assume that a printk
* can't format message larger than 1024 bytes, so we don't either.
*/
-static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
- va_list args)
+void audit_log_vformat(struct audit_buffer *ab, const char *fmt, va_list args)
{
int len, avail;
struct sk_buff *skb;
@@ -1471,3 +1470,6 @@ EXPORT_SYMBOL(audit_log_start);
EXPORT_SYMBOL(audit_log_end);
EXPORT_SYMBOL(audit_log_format);
EXPORT_SYMBOL(audit_log);
+EXPORT_SYMBOL_GPL(audit_log_vformat);
+EXPORT_SYMBOL_GPL(audit_log_untrustedstring);
+EXPORT_SYMBOL_GPL(audit_log_d_path);

View File

@@ -1,61 +0,0 @@
---
security/apparmor/Kconfig | 17 +++++++++++++++++
security/apparmor/lsm.c | 16 ++++++++++++++++
2 files changed, 33 insertions(+)
--- a/security/apparmor/Kconfig
+++ b/security/apparmor/Kconfig
@@ -7,4 +7,21 @@ config SECURITY_APPARMOR
Required userspace tools (if they are not included in your
distribution) and further information may be found at
<http://forge.novell.com/modules/xfmod/project/?apparmor>
+
If you are unsure how to answer this question, answer N.
+
+config SECURITY_APPARMOR_BOOTPARAM_VALUE
+ int "AppArmor boot parameter default value"
+ depends on SECURITY_APPARMOR
+ range 0 1
+ default 1
+ help
+ This option sets the default value for the kernel parameter
+ 'apparmor', which allows AppArmor to be enabled or disabled
+ at boot. If this option is set to 0 (zero), the AppArmor
+ kernel parameter will default to 0, disabling AppArmor at
+ bootup. If this option is set to 1 (one), the AppArmor
+ kernel parameter will default to 1, enabling AppArmor at
+ bootup.
+
+ If you are unsure how to answer this question, answer 1.
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -23,6 +23,17 @@
#include "apparmor.h"
#include "inline.h"
+/* Boot time disable flag */
+int apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE;
+
+static int __init apparmor_enabled_setup(char *str)
+{
+ apparmor_enabled = simple_strtol(str, NULL, 0);
+ return 1;
+}
+__setup("apparmor=", apparmor_enabled_setup);
+
+
static int param_set_aabool(const char *val, struct kernel_param *kp);
static int param_get_aabool(char *buffer, struct kernel_param *kp);
#define param_check_aabool(name, p) __param_check(name, p, int)
@@ -882,6 +893,11 @@ static int __init apparmor_init(void)
{
int error;
+ if (!apparmor_enabled) {
+ info_message("AppArmor disabled by boottime parameter\n");
+ return 0;
+ }
+
if ((error = create_apparmorfs())) {
AA_ERROR("Unable to activate AppArmor filesystem\n");
goto createfs_out;

View File

@@ -1,50 +0,0 @@
---
security/apparmor/Kconfig | 2 +-
security/apparmor/lsm.c | 26 --------------------------
2 files changed, 1 insertion(+), 27 deletions(-)
--- a/security/apparmor/Kconfig
+++ b/security/apparmor/Kconfig
@@ -1,5 +1,5 @@
config SECURITY_APPARMOR
- tristate "AppArmor support"
+ bool "AppArmor support"
depends on SECURITY
select AUDIT
help
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -931,33 +931,7 @@ createfs_out:
}
-static void __exit apparmor_exit(void)
-{
- /* Remove and release all the profiles on the profile list. */
- mutex_lock(&aa_interface_lock);
- aa_profile_ns_list_release();
-
- /* FIXME: cleanup profiles references on files */
- free_default_namespace();
-
- /*
- * Delay for an rcu cycle to make sure that all active task
- * context readers have finished, and all profiles have been
- * freed by their rcu callbacks.
- */
- synchronize_rcu();
-
- destroy_apparmorfs();
- mutex_unlock(&aa_interface_lock);
-
- if (unregister_security(&apparmor_ops))
- info_message("Unable to properly unregister AppArmor");
-
- info_message("AppArmor protection removed");
-}
-
module_init(apparmor_init);
-module_exit(apparmor_exit);
MODULE_DESCRIPTION("AppArmor process confinement");
MODULE_AUTHOR("Novell/Immunix, http://bugs.opensuse.org");

View File

@@ -1,16 +0,0 @@
---
security/apparmor/lsm.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -366,8 +366,7 @@ static inline int aa_mask_permissions(in
static int apparmor_inode_create(struct inode *dir, struct dentry *dentry,
struct vfsmount *mnt, int mask)
{
- /* FIXME: may move to MAY_APPEND later */
- return aa_permission("inode_create", dir, dentry, mnt, MAY_WRITE, 0);
+ return aa_permission("inode_create", dir, dentry, mnt, MAY_APPEND, 0);
}
static int apparmor_inode_link(struct dentry *old_dentry,

View File

@@ -1,83 +0,0 @@
---
security/apparmor/main.c | 39 ++++++++++++++++++++++++++++-----------
1 file changed, 28 insertions(+), 11 deletions(-)
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -216,6 +216,12 @@ static int aa_perm_dentry(struct aa_prof
else {
sa->denied_mask = sa->request_mask;
sa->error_code = PTR_ERR(sa->name);
+ if (sa->error_code == -ENOENT)
+ sa->info = "Failed name resolution - object not a valid entry";
+ else if (sa->error_code == -ENAMETOOLONG)
+ sa->info = "Failed name resolution - name too long";
+ else
+ sa->info = "Failed name resolution";
}
sa->name = NULL;
} else
@@ -371,8 +377,11 @@ static int aa_audit_base(struct aa_profi
if (sa->operation)
audit_log_format(ab, "operation=\"%s\"", sa->operation);
- if (sa->info)
+ if (sa->info) {
audit_log_format(ab, " info=\"%s\"", sa->info);
+ if (sa->error_code)
+ audit_log_format(ab, " error=%d", sa->error_code);
+ }
if (sa->request_mask)
aa_audit_file_mask(ab, "requested_mask", sa->request_mask);
@@ -918,23 +927,29 @@ int aa_register(struct linux_binprm *bpr
AA_DEBUG("%s\n", __FUNCTION__);
- filename = aa_get_name(filp->f_dentry, filp->f_vfsmnt, &buffer, 0);
- if (IS_ERR(filename)) {
- AA_ERROR("%s: Failed to get filename", __FUNCTION__);
- return -ENOENT;
- }
+ profile = aa_get_profile(current);
shift = aa_inode_mode(filp->f_dentry->d_inode);
- exec_mode = AA_EXEC_UNSAFE << shift;
-
memset(&sa, 0, sizeof(sa));
sa.operation = "exec";
sa.gfp_mask = GFP_KERNEL;
- sa.name = filename;
sa.request_mask = MAY_EXEC << shift;
+ filename = aa_get_name(filp->f_dentry, filp->f_vfsmnt, &buffer, 0);
+ if (IS_ERR(filename)) {
+ if (profile) {
+ sa.info = "Failed name resolution - exec failed";
+ sa.error_code = PTR_ERR(filename);
+ aa_audit_reject(profile, &sa);
+ return sa.error_code;
+ } else
+ return 0;
+ }
+ sa.name = filename;
+
+ exec_mode = AA_EXEC_UNSAFE << shift;
+
repeat:
- profile = aa_get_profile(current);
if (profile) {
complain = PROFILE_COMPLAIN(profile);
@@ -1011,8 +1026,10 @@ repeat:
if (IS_ERR(old_profile)) {
aa_put_profile(new_profile);
aa_put_profile(profile);
- if (PTR_ERR(old_profile) == -ESTALE)
+ if (PTR_ERR(old_profile) == -ESTALE) {
+ profile = aa_get_profile(current);
goto repeat;
+ }
if (PTR_ERR(old_profile) == -EPERM) {
sa.denied_mask = sa.request_mask;
sa.info = "unable to set profile due to ptrace";

View File

@@ -1,47 +0,0 @@
---
security/apparmor/apparmorfs.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -99,6 +99,22 @@ static struct file_operations apparmorfs
.read = aa_matching_read,
};
+/* apparmor/features */
+static ssize_t aa_features_read(struct file *file, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ const char *features = "file=3.0 capability=1.0 network=1.0 "
+ "change_hat=1.3 change_profile=1.0 "
+ "aanamespaces=1.0";
+
+ return simple_read_from_buffer(buf, size, ppos, features,
+ strlen(features));
+}
+
+static struct file_operations apparmorfs_features_fops = {
+ .read = aa_features_read,
+};
+
/* apparmor/.load */
static ssize_t aa_profile_load(struct file *f, const char __user *buf,
size_t size, loff_t *pos)
@@ -204,6 +220,7 @@ void destroy_apparmorfs(void)
aafs_remove(".replace");
aafs_remove(".load");
aafs_remove("matching");
+ aafs_remove("features");
aafs_remove("profiles");
securityfs_remove(apparmor_dentry);
apparmor_dentry = NULL;
@@ -232,6 +249,9 @@ int create_apparmorfs(void)
error = aafs_create("matching", 0444, &apparmorfs_matching_fops);
if (error)
goto error;
+ error = aafs_create("features", 0444, &apparmorfs_features_fops);
+ if (error)
+ goto error;
error = aafs_create(".load", 0640, &apparmorfs_profile_load);
if (error)
goto error;

View File

@@ -1,36 +0,0 @@
---
security/apparmor/apparmorfs.c | 2 +-
security/apparmor/main.c | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -89,7 +89,7 @@ static struct file_operations apparmorfs
static ssize_t aa_matching_read(struct file *file, char __user *buf,
size_t size, loff_t *ppos)
{
- const char *matching = "pattern=aadfa perms=rwxamlz user:other";
+ const char *matching = "pattern=aadfa perms=rwxamlk/ user::other";
return simple_read_from_buffer(buf, size, ppos, matching,
strlen(matching));
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -99,7 +99,7 @@ static int aa_link_denied(struct aa_prof
/* Link always requires 'l' on the link for both parts of the pair.
* If a subset test is required a permission subset test of the
* perms for the link are done against the user:group:other of the
- * target's 'r', 'w', 'x', 'a', 'z', and 'm' permissions.
+ * target's 'r', 'w', 'x', 'a', 'k', and 'm' permissions.
*
* If the link has 'x', an exact match of all the execute flags
* ('i', 'u', 'p'). safe exec is treated as a subset of unsafe exec
@@ -388,7 +388,7 @@ static int aa_audit_base(struct aa_profi
audit_log_format(ab, " info=\"%s\"", sa->info);
if (sa->request_mask)
- aa_audit_file_mask(ab, "request_mask", sa->request_mask);
+ aa_audit_file_mask(ab, "requested_mask", sa->request_mask);
if (sa->denied_mask)
aa_audit_file_mask(ab, "denied_mask", sa->denied_mask);

View File

@@ -1,14 +0,0 @@
---
security/apparmor/lsm.c | 1 +
1 file changed, 1 insertion(+)
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -249,6 +249,7 @@ static int apparmor_sysctl(struct ctl_ta
}
out:
+ aa_put_profile(profile);
return error;
}

View File

@@ -1,30 +0,0 @@
From: John Johansen <jjohansen@suse.de>
Subject: Add AppArmor LSM to security/Makefile
Signed-off-by: John Johansen <jjohansen@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
security/Kconfig | 1 +
security/Makefile | 1 +
2 files changed, 2 insertions(+)
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -104,6 +104,7 @@ config SECURITY_ROOTPLUG
If you are unsure how to answer this question, answer N.
source security/selinux/Kconfig
+source security/apparmor/Kconfig
endmenu
--- a/security/Makefile
+++ b/security/Makefile
@@ -14,5 +14,6 @@ endif
obj-$(CONFIG_SECURITY) += security.o dummy.o inode.o
# Must precede capability.o in order to stack properly.
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
+obj-$(CONFIG_SECURITY_APPARMOR) += commoncap.o apparmor/
obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o
obj-$(CONFIG_SECURITY_ROOTPLUG) += commoncap.o root_plug.o

View File

@@ -1,162 +0,0 @@
Change link to use pairs to provide tighter control of how links
can be made. The permission subset test is controled by an
extra flag of the link perm, this allows for compatibility with
old style links which have a broad pair, but also allows for
pairs to not require the subset test when needed.
The pairs are stored as two matches in the dfa seperated by the
null transition. Both matches must be made and have the link
perm set. If the second match has the AA_LINK_SUBSET_TEST perm
set, then the subset test is done.
The link keyword can be used or just the link mask
User side these pairs are expressed as follows
link [link_mask] linkname -> [subset_mask] targetname,
[link_mask] linkname -> [subset_mask] targetname,
link /linkname -> /targetname, #link to targetname, link is perm subset
if user:group:other link specification is desired then a user:group:other
mask containing only the link perm in the appropriate positions can be
specified
eg.
link l:: /linkname -> /targetname, # links to targetname if owned by
or
l:: /linkname -> /targetname,
Both the linkname and the target support full AppArmor globbing.
link l:: /** -> /**, # allow any link to target owned by user
to override the default subset test
l:: /linkname -> px /targetname,
Traditional AA style links are still supported and are mapped by the
parser into the newer link pair for the kernel, with the LINK_SUBSET_TEST
bits set.
/linkname rwl,
is mapped to
link /linkname -> /**,
/linkname rw,
---
security/apparmor/apparmor.h | 4 +++
security/apparmor/main.c | 45 ++++++++++++++++++++++++++++++++-----------
2 files changed, 38 insertions(+), 11 deletions(-)
--- a/security/apparmor/apparmor.h
+++ b/security/apparmor/apparmor.h
@@ -34,6 +34,7 @@
AA_MAY_LOCK | AA_EXEC_MMAP | \
AA_EXEC_UNSAFE | AA_EXEC_MOD_0 | \
AA_EXEC_MOD_1)
+#define AA_LINK_SUBSET_TEST 0x0020
#define AA_EXEC_UNCONFINED 0
#define AA_EXEC_INHERIT AA_EXEC_MOD_0
@@ -59,6 +60,9 @@
#define AA_USER_EXEC_MODS (AA_EXEC_MODIFIERS << AA_USER_SHIFT)
#define AA_OTHER_EXEC_MODS (AA_EXEC_MODIFIERS << AA_OTHER_SHIFT)
+#define AA_USER_EXEC_UNSAFE (AA_EXEC_UNSAFE << AA_USER_SHIFT)
+#define AA_OTHER_EXEC_UNSAFE (AA_EXEC_UNSAFE << AA_OTHER_SHIFT)
+
#define AA_EXEC_BITS (AA_USER_EXEC | AA_OTHER_EXEC)
#define AA_ALL_EXEC_MODS (AA_USER_EXEC_MODS | \
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -67,10 +67,27 @@ static int aa_link_denied(struct aa_prof
const char *target, int target_mode,
int *request_mask)
{
- int l_mode, t_mode, denied_mask;
+ unsigned int state;
+ int l_mode, t_mode, denied_mask = 0;
int link_mask = AA_MAY_LINK << target_mode;
- l_mode = aa_match(profile->file_rules, link);
+ *request_mask = link_mask;
+
+ l_mode = aa_match_state(profile->file_rules, DFA_START, link, &state);
+ if (l_mode & link_mask) {
+ int mode;
+ /* test to see if target can be paired with link */
+ state = aa_dfa_null_transition(profile->file_rules, state);
+ mode = aa_match_state(profile->file_rules, state, target,
+ NULL);
+
+ if (!(mode & link_mask))
+ denied_mask |= link_mask;
+ if (!(mode & (AA_LINK_SUBSET_TEST << target_mode)))
+ return denied_mask;
+ }
+
+ /* do link perm subset test */
t_mode = aa_match(profile->file_rules, target);
/* Ignore valid-profile-transition flags. */
@@ -79,23 +96,30 @@ static int aa_link_denied(struct aa_prof
*request_mask = l_mode | link_mask;
- /* Link always requires 'l' on the link, a subset for user:other
- * of the target's 'r', 'w', 'x', 'a', 'z', and 'm' permissions on
- * the link, and if the link has 'x', an exact match of all the
- * execute flags ('i', 'u', 'U', 'p', 'P').
+ /* Link always requires 'l' on the link for both parts of the pair.
+ * If a subset test is required a permission subset test of the
+ * perms for the link are done against the user:group:other of the
+ * target's 'r', 'w', 'x', 'a', 'z', and 'm' permissions.
+ *
+ * If the link has 'x', an exact match of all the execute flags
+ * ('i', 'u', 'p'). safe exec is treated as a subset of unsafe exec
*/
#define SUBSET_PERMS (AA_FILE_PERMS & ~AA_LINK_BITS)
- denied_mask = ~l_mode & link_mask;
+ denied_mask |= ~l_mode & link_mask;
if (l_mode & SUBSET_PERMS) {
denied_mask |= (l_mode & SUBSET_PERMS) & ~t_mode;
if (denied_mask & AA_EXEC_BITS)
denied_mask |= l_mode & AA_ALL_EXEC_MODS;
else if (l_mode & AA_EXEC_BITS) {
+ if (!(l_mode & AA_USER_EXEC_UNSAFE))
+ l_mode |= t_mode & AA_USER_EXEC_UNSAFE;
if (l_mode & AA_USER_EXEC &&
(l_mode & AA_USER_EXEC_MODS) !=
(t_mode & AA_USER_EXEC_MODS))
denied_mask |= AA_USER_EXEC |
(l_mode & AA_USER_EXEC_MODS);
+ if (!(l_mode & AA_OTHER_EXEC_UNSAFE))
+ l_mode |= t_mode & AA_OTHER_EXEC_UNSAFE;
if (l_mode & AA_OTHER_EXEC &&
(l_mode & AA_OTHER_EXEC_MODS) !=
(t_mode & AA_OTHER_EXEC_MODS))
@@ -703,15 +727,15 @@ int aa_link(struct aa_profile *profile,
struct dentry *link, struct vfsmount *link_mnt,
struct dentry *target, struct vfsmount *target_mnt)
{
- int error, check = 0;
+ int error;
struct aa_audit sa;
char *buffer = NULL, *buffer2 = NULL;
memset(&sa, 0, sizeof(sa));
sa.operation = "inode_link";
sa.gfp_mask = GFP_KERNEL;
- sa.name = aa_get_name(link, link_mnt, &buffer, check);
- sa.name2 = aa_get_name(target, target_mnt, &buffer2, check);
+ sa.name = aa_get_name(link, link_mnt, &buffer, 0);
+ sa.name2 = aa_get_name(target, target_mnt, &buffer2, 0);
if (IS_ERR(sa.name)) {
sa.error_code = PTR_ERR(sa.name);
@@ -723,7 +747,6 @@ int aa_link(struct aa_profile *profile,
}
if (sa.name && sa.name2) {
- sa.request_mask = AA_MAY_LINK;
sa.denied_mask = aa_link_denied(profile, sa.name, sa.name2,
aa_inode_mode(target->d_inode),
&sa.request_mask);

View File

@@ -1,904 +0,0 @@
From: John Johansen <jjohansen@suse.de>
Subject: AppArmor: Module and LSM hooks
Module parameters, LSM hooks, initialization and teardown.
Signed-off-by: John Johansen <jjohansen@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
security/apparmor/lsm.c | 889 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 889 insertions(+)
--- /dev/null
+++ b/security/apparmor/lsm.c
@@ -0,0 +1,889 @@
+/*
+ * Copyright (C) 1998-2007 Novell/SUSE
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * AppArmor LSM interface
+ */
+
+#include <linux/security.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/ctype.h>
+#include <linux/sysctl.h>
+#include <linux/audit.h>
+
+#include "apparmor.h"
+#include "inline.h"
+
+/* Flag indicating whether initialization completed */
+int apparmor_initialized = 0;
+
+static int param_set_aabool(const char *val, struct kernel_param *kp);
+static int param_get_aabool(char *buffer, struct kernel_param *kp);
+#define param_check_aabool(name, p) __param_check(name, p, int)
+
+static int param_set_aauint(const char *val, struct kernel_param *kp);
+static int param_get_aauint(char *buffer, struct kernel_param *kp);
+#define param_check_aauint(name, p) __param_check(name, p, int)
+
+/* Flag values, also controllable via /sys/module/apparmor/parameters
+ * We define special types as we want to do additional mediation.
+ *
+ * Complain mode -- in complain mode access failures result in auditing only
+ * and task is allowed access. audit events are processed by userspace to
+ * generate policy. Default is 'enforce' (0).
+ * Value is also togglable per profile and referenced when global value is
+ * enforce.
+ */
+int apparmor_complain = 0;
+module_param_named(complain, apparmor_complain, aabool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(apparmor_complain, "Toggle AppArmor complain mode");
+
+/* Debug mode */
+int apparmor_debug = 0;
+module_param_named(debug, apparmor_debug, aabool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(apparmor_debug, "Toggle AppArmor debug mode");
+
+/* Audit mode */
+int apparmor_audit = 0;
+module_param_named(audit, apparmor_audit, aabool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(apparmor_audit, "Toggle AppArmor audit mode");
+
+/* Syscall logging mode */
+int apparmor_logsyscall = 0;
+module_param_named(logsyscall, apparmor_logsyscall, aabool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(apparmor_logsyscall, "Toggle AppArmor logsyscall mode");
+
+/* Maximum pathname length before accesses will start getting rejected */
+unsigned int apparmor_path_max = 2 * PATH_MAX;
+module_param_named(path_max, apparmor_path_max, aauint, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(apparmor_path_max, "Maximum pathname length allowed");
+
+/* Boot time disable flag */
+#ifdef CONFIG_SECURITY_APPARMOR_DISABLE
+#define AA_ENABLED_PERMS 0600
+#else
+#define AA_ENABLED_PERMS 0400
+#endif
+static int param_set_aa_enabled(const char *val, struct kernel_param *kp);
+unsigned int apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE;
+module_param_call(enabled, param_set_aa_enabled, param_get_aauint,
+ &apparmor_enabled, AA_ENABLED_PERMS);
+MODULE_PARM_DESC(apparmor_enabled, "Enable/Disable Apparmor on boot");
+
+static int __init apparmor_enabled_setup(char *str)
+{
+ apparmor_enabled = simple_strtol(str, NULL, 0);
+ return 1;
+}
+__setup("apparmor=", apparmor_enabled_setup);
+
+static int param_set_aabool(const char *val, struct kernel_param *kp)
+{
+ if (aa_task_context(current))
+ return -EPERM;
+ return param_set_bool(val, kp);
+}
+
+static int param_get_aabool(char *buffer, struct kernel_param *kp)
+{
+ if (aa_task_context(current))
+ return -EPERM;
+ return param_get_bool(buffer, kp);
+}
+
+static int param_set_aauint(const char *val, struct kernel_param *kp)
+{
+ if (aa_task_context(current))
+ return -EPERM;
+ return param_set_uint(val, kp);
+}
+
+static int param_get_aauint(char *buffer, struct kernel_param *kp)
+{
+ if (aa_task_context(current))
+ return -EPERM;
+ return param_get_uint(buffer, kp);
+}
+
+/* allow run time disabling of apparmor */
+static int param_set_aa_enabled(const char *val, struct kernel_param *kp)
+{
+ char *endp;
+ unsigned long l;
+
+ if (!apparmor_initialized) {
+ apparmor_enabled = 0;
+ return 0;
+ }
+
+ if (aa_task_context(current))
+ return -EPERM;
+
+ if (!apparmor_enabled)
+ return -EINVAL;
+
+ if (!val)
+ return -EINVAL;
+
+ l = simple_strtoul(val, &endp, 0);
+ if (endp == val || l != 0)
+ return -EINVAL;
+
+ apparmor_enabled = 0;
+ apparmor_disable();
+ return 0;
+}
+
+static int aa_reject_syscall(struct task_struct *task, gfp_t flags,
+ const char *name)
+{
+ struct aa_profile *profile = aa_get_profile(task);
+ int error = 0;
+
+ if (profile) {
+ error = aa_audit_syscallreject(profile, flags, name);
+ aa_put_profile(profile);
+ }
+
+ return error;
+}
+
+static int apparmor_ptrace(struct task_struct *parent,
+ struct task_struct *child)
+{
+ struct aa_task_context *cxt;
+ int error = 0;
+
+ /*
+ * parent can ptrace child when
+ * - parent is unconfined
+ * - parent & child are in the same namespace &&
+ * - parent is in complain mode
+ * - parent and child are confined by the same profile
+ * - parent profile has CAP_SYS_PTRACE
+ */
+
+ rcu_read_lock();
+ cxt = aa_task_context(parent);
+ if (cxt) {
+ if (parent->nsproxy != child->nsproxy) {
+ struct aa_audit sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = "ptrace";
+ sa.gfp_mask = GFP_ATOMIC;
+ sa.parent = parent->pid;
+ sa.task = child->pid;
+ sa.info = "different namespaces";
+ aa_audit_reject(cxt->profile, &sa);
+ error = -EPERM;
+ } else {
+ struct aa_task_context *child_cxt =
+ aa_task_context(child);
+
+ error = aa_may_ptrace(cxt, child_cxt ?
+ child_cxt->profile : NULL);
+ if (PROFILE_COMPLAIN(cxt->profile)) {
+ struct aa_audit sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = "ptrace";
+ sa.gfp_mask = GFP_ATOMIC;
+ sa.parent = parent->pid;
+ sa.task = child->pid;
+ aa_audit_hint(cxt->profile, &sa);
+ }
+ }
+ }
+ rcu_read_unlock();
+
+ return error;
+}
+
+static int apparmor_capable(struct task_struct *task, int cap)
+{
+ int error;
+ struct aa_task_context *cxt;
+
+ /* cap_capable returns 0 on success, else -EPERM */
+ error = cap_capable(task, cap);
+
+ rcu_read_lock();
+ cxt = aa_task_context(task);
+ if (cxt && (!error || cap_raised(cxt->profile->set_caps, cap)))
+ error = aa_capability(cxt, cap);
+ rcu_read_unlock();
+
+ return error;
+}
+
+static int apparmor_sysctl(struct ctl_table *table, int op)
+{
+ struct aa_profile *profile = aa_get_profile(current);
+ int error = 0;
+
+ if (profile) {
+ char *buffer, *name;
+ int mask;
+
+ mask = 0;
+ if (op & 4)
+ mask |= MAY_READ;
+ if (op & 2)
+ mask |= MAY_WRITE;
+
+ error = -ENOMEM;
+ buffer = (char*)__get_free_page(GFP_KERNEL);
+ if (!buffer)
+ goto out;
+ name = sysctl_pathname(table, buffer, PAGE_SIZE);
+ if (name && name - buffer >= 5) {
+ name -= 5;
+ memcpy(name, "/proc", 5);
+ error = aa_perm_path(profile, "sysctl", name, mask, 0);
+ }
+ free_page((unsigned long)buffer);
+ }
+
+out:
+ aa_put_profile(profile);
+ return error;
+}
+
+static int apparmor_bprm_set_security(struct linux_binprm *bprm)
+{
+ /* handle capability bits with setuid, etc */
+ cap_bprm_set_security(bprm);
+ /* already set based on script name */
+ if (bprm->sh_bang)
+ return 0;
+ return aa_register(bprm);
+}
+
+static int apparmor_bprm_secureexec(struct linux_binprm *bprm)
+{
+ int ret = cap_bprm_secureexec(bprm);
+
+ if (!ret && (unsigned long)bprm->security & AA_SECURE_EXEC_NEEDED) {
+ AA_DEBUG("%s: secureexec required for %s\n",
+ __FUNCTION__, bprm->filename);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int apparmor_sb_mount(char *dev_name, struct nameidata *nd, char *type,
+ unsigned long flags, void *data)
+{
+ return aa_reject_syscall(current, GFP_KERNEL, "mount");
+}
+
+static int apparmor_umount(struct vfsmount *mnt, int flags)
+{
+ return aa_reject_syscall(current, GFP_KERNEL, "umount");
+}
+
+static int apparmor_inode_mkdir(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, int mask)
+{
+ struct aa_profile *profile;
+ int error = 0;
+
+ if (!mnt || !mediated_filesystem(dir))
+ goto out;
+
+ profile = aa_get_profile(current);
+
+ if (profile)
+ error = aa_perm_dir(profile, "inode_mkdir", dentry, mnt,
+ MAY_WRITE);
+
+ aa_put_profile(profile);
+
+out:
+ return error;
+}
+
+static int apparmor_inode_rmdir(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt)
+{
+ struct aa_profile *profile;
+ int error = 0;
+
+ if (!mnt || !mediated_filesystem(dir))
+ goto out;
+
+ profile = aa_get_profile(current);
+
+ if (profile)
+ error = aa_perm_dir(profile, "inode_rmdir", dentry, mnt,
+ MAY_WRITE);
+
+ aa_put_profile(profile);
+
+out:
+ return error;
+}
+
+static int aa_permission(const char *operation, struct inode *inode,
+ struct dentry *dentry, struct vfsmount *mnt,
+ int mask, int check)
+{
+ int error = 0;
+
+ if (mnt && mediated_filesystem(inode)) {
+ struct aa_profile *profile;
+
+ profile = aa_get_profile(current);
+ if (profile)
+ error = aa_perm(profile, operation, dentry, mnt, mask,
+ check);
+ aa_put_profile(profile);
+ }
+ return error;
+}
+
+static inline int aa_mask_permissions(int mask)
+{
+ if (mask & MAY_APPEND)
+ mask &= (MAY_READ | MAY_APPEND | MAY_EXEC);
+ else
+ mask &= (MAY_READ | MAY_WRITE | MAY_EXEC);
+ return mask;
+}
+
+static int apparmor_inode_create(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, int mask)
+{
+ return aa_permission("inode_create", dir, dentry, mnt, MAY_APPEND, 0);
+}
+
+static int apparmor_inode_link(struct dentry *old_dentry,
+ struct vfsmount *old_mnt, struct inode *dir,
+ struct dentry *new_dentry,
+ struct vfsmount *new_mnt)
+{
+ int error = 0;
+ struct aa_profile *profile;
+
+ if (!old_mnt || !new_mnt || !mediated_filesystem(dir))
+ goto out;
+
+ profile = aa_get_profile(current);
+
+ if (profile)
+ error = aa_link(profile, new_dentry, new_mnt,
+ old_dentry, old_mnt);
+
+ aa_put_profile(profile);
+
+out:
+ return error;
+}
+
+static int apparmor_inode_unlink(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt)
+{
+ int check = 0;
+
+ if (S_ISDIR(dentry->d_inode->i_mode))
+ check |= AA_CHECK_DIR;
+ return aa_permission("inode_unlink", dir, dentry, mnt, MAY_WRITE,
+ check);
+}
+
+static int apparmor_inode_symlink(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, const char *old_name)
+{
+ return aa_permission("inode_symlink", dir, dentry, mnt, MAY_WRITE, 0);
+}
+
+static int apparmor_inode_mknod(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, int mode, dev_t dev)
+{
+ return aa_permission("inode_mknod", dir, dentry, mnt, MAY_WRITE, 0);
+}
+
+static int apparmor_inode_rename(struct inode *old_dir,
+ struct dentry *old_dentry,
+ struct vfsmount *old_mnt,
+ struct inode *new_dir,
+ struct dentry *new_dentry,
+ struct vfsmount *new_mnt)
+{
+ struct aa_profile *profile;
+ int error = 0;
+
+ if ((!old_mnt && !new_mnt) || !mediated_filesystem(old_dir))
+ goto out;
+
+ profile = aa_get_profile(current);
+
+ if (profile) {
+ struct inode *inode = old_dentry->d_inode;
+ int check = 0;
+
+ if (inode && S_ISDIR(inode->i_mode))
+ check |= AA_CHECK_DIR;
+ if (old_mnt)
+ error = aa_perm(profile, "inode_rename", old_dentry,
+ old_mnt, MAY_READ | MAY_WRITE, check);
+
+ if (!error && new_mnt) {
+ error = aa_perm(profile, "inode_rename", new_dentry,
+ new_mnt, MAY_WRITE, check);
+ }
+ }
+
+ aa_put_profile(profile);
+
+out:
+ return error;
+}
+
+static int apparmor_inode_permission(struct inode *inode, int mask,
+ struct nameidata *nd)
+{
+ int check = 0;
+
+ if (!nd || nd->flags & (LOOKUP_PARENT | LOOKUP_CONTINUE))
+ return 0;
+ mask = aa_mask_permissions(mask);
+ if (S_ISDIR(inode->i_mode)) {
+ check |= AA_CHECK_DIR;
+ /* allow traverse accesses to directories */
+ mask &= ~MAY_EXEC;
+ }
+ return aa_permission("inode_permission", inode, nd->dentry, nd->mnt,
+ mask, check);
+}
+
+static int apparmor_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
+ struct iattr *iattr)
+{
+ int error = 0;
+
+ if (!mnt)
+ goto out;
+
+ if (mediated_filesystem(dentry->d_inode)) {
+ struct aa_profile *profile;
+
+ profile = aa_get_profile(current);
+ /*
+ * Mediate any attempt to change attributes of a file
+ * (chmod, chown, chgrp, etc)
+ */
+ if (profile)
+ error = aa_attr(profile, dentry, mnt, iattr);
+
+ aa_put_profile(profile);
+ }
+
+out:
+ return error;
+}
+
+static int aa_xattr_permission(struct dentry *dentry, struct vfsmount *mnt,
+ const char *operation, int mask,
+ struct file *file)
+{
+ int error = 0;
+
+ if (mnt && mediated_filesystem(dentry->d_inode)) {
+ struct aa_profile *profile = aa_get_profile(current);
+ int check = file ? AA_CHECK_FD : 0;
+
+ if (profile)
+ error = aa_perm_xattr(profile, operation, dentry, mnt,
+ mask, check);
+ aa_put_profile(profile);
+ }
+
+ return error;
+}
+
+static int apparmor_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+ char *name, void *value, size_t size,
+ int flags, struct file *file)
+{
+ return aa_xattr_permission(dentry, mnt, "xattr set", MAY_WRITE, file);
+}
+
+static int apparmor_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
+ char *name, struct file *file)
+{
+ return aa_xattr_permission(dentry, mnt, "xattr get", MAY_READ, file);
+}
+
+static int apparmor_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt,
+ struct file *file)
+{
+ return aa_xattr_permission(dentry, mnt, "xattr list", MAY_READ, file);
+}
+
+static int apparmor_inode_removexattr(struct dentry *dentry,
+ struct vfsmount *mnt, char *name,
+ struct file *file)
+{
+ return aa_xattr_permission(dentry, mnt, "xattr remove", MAY_WRITE,
+ file);
+}
+
+static int aa_file_permission(const char *op, struct file *file, int mask)
+{
+ struct aa_profile *profile;
+ struct aa_profile *file_profile = (struct aa_profile*)file->f_security;
+ int error = 0;
+
+ if (!file_profile)
+ goto out;
+
+ /*
+ * If this file was opened under a different profile, we
+ * revalidate the access against the current profile.
+ */
+ profile = aa_get_profile(current);
+ if (profile && (file_profile != profile || mask & AA_MAY_LOCK)) {
+ struct dentry *dentry = file->f_dentry;
+ struct vfsmount *mnt = file->f_vfsmnt;
+ struct inode *inode = dentry->d_inode;
+ int check = AA_CHECK_FD;
+
+ /*
+ * FIXME: We should remember which profiles we revalidated
+ * against.
+ */
+ if (S_ISDIR(inode->i_mode))
+ check |= AA_CHECK_DIR;
+ error = aa_permission(op, inode, dentry, mnt, mask, check);
+ }
+ aa_put_profile(profile);
+
+out:
+ return error;
+}
+
+static int apparmor_file_permission(struct file *file, int mask)
+{
+ return aa_file_permission("file_permission", file,
+ aa_mask_permissions(mask));
+}
+
+static inline int apparmor_file_lock (struct file *file, unsigned int cmd)
+{
+ int mask = AA_MAY_LOCK;
+ if (cmd == F_WRLCK)
+ mask |= MAY_WRITE;
+ return aa_file_permission("file_lock", file, mask);
+}
+
+static int apparmor_file_alloc_security(struct file *file)
+{
+ struct aa_profile *profile;
+
+ profile = aa_get_profile(current);
+ if (profile)
+ file->f_security = profile;
+
+ return 0;
+}
+
+static void apparmor_file_free_security(struct file *file)
+{
+ struct aa_profile *file_profile = (struct aa_profile*)file->f_security;
+
+ aa_put_profile(file_profile);
+}
+
+static inline int aa_mmap(struct file *file, const char *operation,
+ unsigned long prot, unsigned long flags)
+{
+ struct dentry *dentry;
+ int mask = 0;
+
+ if (!file || !file->f_security)
+ return 0;
+
+ if (prot & PROT_READ)
+ mask |= MAY_READ;
+ /* Private mappings don't require write perms since they don't
+ * write back to the files */
+ if ((prot & PROT_WRITE) && !(flags & MAP_PRIVATE))
+ mask |= MAY_WRITE;
+ if (prot & PROT_EXEC)
+ mask |= AA_EXEC_MMAP;
+
+ dentry = file->f_dentry;
+ return aa_permission(operation, dentry->d_inode, dentry,
+ file->f_vfsmnt, mask, AA_CHECK_FD);
+}
+
+static int apparmor_file_mmap(struct file *file, unsigned long reqprot,
+ unsigned long prot, unsigned long flags,
+ unsigned long addr, unsigned long addr_only)
+{
+ if ((addr < mmap_min_addr) && !capable(CAP_SYS_RAWIO)) {
+ struct aa_profile *profile = aa_get_profile(current);
+ if (profile)
+ /* future control check here */
+ return -EACCES;
+ else
+ return -EACCES;
+ aa_put_profile(profile);
+ }
+
+ return aa_mmap(file, "file_mmap", prot, flags);
+}
+
+static int apparmor_file_mprotect(struct vm_area_struct *vma,
+ unsigned long reqprot, unsigned long prot)
+{
+ return aa_mmap(vma->vm_file, "file_mprotect", prot,
+ !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0);
+}
+
+static int apparmor_task_alloc_security(struct task_struct *task)
+{
+ return aa_clone(task);
+}
+
+/*
+ * Called from IRQ context from RCU callback.
+ */
+static void apparmor_task_free_security(struct task_struct *task)
+{
+ aa_release(task);
+}
+
+static int apparmor_getprocattr(struct task_struct *task, char *name,
+ char **value)
+{
+ unsigned len;
+ int error;
+ struct aa_profile *profile;
+
+ /* AppArmor only supports the "current" process attribute */
+ if (strcmp(name, "current") != 0)
+ return -EINVAL;
+
+ /* must be task querying itself or admin */
+ if (current != task && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ profile = aa_get_profile(task);
+ error = aa_getprocattr(profile, value, &len);
+ aa_put_profile(profile);
+ if (!error)
+ error = len;
+
+ return error;
+}
+
+static int apparmor_setprocattr(struct task_struct *task, char *name,
+ void *value, size_t size)
+{
+ char *command, *args;
+ int error;
+
+ if (strcmp(name, "current") != 0 || size == 0 || size >= PAGE_SIZE)
+ return -EINVAL;
+ args = value;
+ args[size] = '\0';
+ args = strstrip(args);
+ command = strsep(&args, " ");
+ if (!args)
+ return -EINVAL;
+ while (isspace(*args))
+ args++;
+ if (!*args)
+ return -EINVAL;
+
+ if (strcmp(command, "changehat") == 0) {
+ if (current != task)
+ return -EACCES;
+ error = aa_setprocattr_changehat(args);
+ } else if (strcmp(command, "changeprofile") == 0) {
+ if (current != task)
+ return -EACCES;
+ error = aa_setprocattr_changeprofile(args);
+ } else if (strcmp(command, "setprofile") == 0) {
+ struct aa_profile *profile;
+
+ /* Only an unconfined process with admin capabilities
+ * may change the profile of another task.
+ */
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ profile = aa_get_profile(current);
+ if (profile) {
+ struct aa_audit sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = "profile_set";
+ sa.gfp_mask = GFP_KERNEL;
+ sa.task = task->pid;
+ sa.info = "from confined process";
+ aa_audit_reject(profile, &sa);
+ aa_put_profile(profile);
+ return -EACCES;
+ }
+ error = aa_setprocattr_setprofile(task, args);
+ } else {
+ struct aa_audit sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = "setprocattr";
+ sa.gfp_mask = GFP_KERNEL;
+ sa.info = "invalid command";
+ sa.name = command;
+ sa.task = task->pid;
+ aa_audit_reject(NULL, &sa);
+ return -EINVAL;
+ }
+
+ if (!error)
+ error = size;
+ return error;
+}
+
+struct security_operations apparmor_ops = {
+ .ptrace = apparmor_ptrace,
+ .capget = cap_capget,
+ .capset_check = cap_capset_check,
+ .capset_set = cap_capset_set,
+ .sysctl = apparmor_sysctl,
+ .capable = apparmor_capable,
+ .syslog = cap_syslog,
+
+ .netlink_send = cap_netlink_send,
+ .netlink_recv = cap_netlink_recv,
+
+ .bprm_apply_creds = cap_bprm_apply_creds,
+ .bprm_set_security = apparmor_bprm_set_security,
+ .bprm_secureexec = apparmor_bprm_secureexec,
+
+ .sb_mount = apparmor_sb_mount,
+ .sb_umount = apparmor_umount,
+
+ .inode_mkdir = apparmor_inode_mkdir,
+ .inode_rmdir = apparmor_inode_rmdir,
+ .inode_create = apparmor_inode_create,
+ .inode_link = apparmor_inode_link,
+ .inode_unlink = apparmor_inode_unlink,
+ .inode_symlink = apparmor_inode_symlink,
+ .inode_mknod = apparmor_inode_mknod,
+ .inode_rename = apparmor_inode_rename,
+ .inode_permission = apparmor_inode_permission,
+ .inode_setattr = apparmor_inode_setattr,
+ .inode_setxattr = apparmor_inode_setxattr,
+ .inode_getxattr = apparmor_inode_getxattr,
+ .inode_listxattr = apparmor_inode_listxattr,
+ .inode_removexattr = apparmor_inode_removexattr,
+ .file_permission = apparmor_file_permission,
+ .file_alloc_security = apparmor_file_alloc_security,
+ .file_free_security = apparmor_file_free_security,
+ .file_mmap = apparmor_file_mmap,
+ .file_mprotect = apparmor_file_mprotect,
+ .file_lock = apparmor_file_lock,
+
+ .task_alloc_security = apparmor_task_alloc_security,
+ .task_free_security = apparmor_task_free_security,
+ .task_post_setuid = cap_task_post_setuid,
+ .task_reparent_to_init = cap_task_reparent_to_init,
+
+ .getprocattr = apparmor_getprocattr,
+ .setprocattr = apparmor_setprocattr,
+};
+
+void info_message(const char *str)
+{
+ struct aa_audit sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.gfp_mask = GFP_KERNEL;
+ sa.info = str;
+ printk(KERN_INFO "AppArmor: %s\n", str);
+ if (audit_enabled)
+ aa_audit_message(NULL, &sa, AUDIT_APPARMOR_STATUS);
+}
+
+static int __init apparmor_init(void)
+{
+ int error;
+
+ if (!apparmor_enabled) {
+ info_message("AppArmor disabled by boottime parameter\n");
+ return 0;
+ }
+
+ if ((error = create_apparmorfs())) {
+ AA_ERROR("Unable to activate AppArmor filesystem\n");
+ goto createfs_out;
+ }
+
+ if ((error = alloc_default_namespace())){
+ AA_ERROR("Unable to allocate default profile namespace\n");
+ goto alloc_out;
+ }
+
+ if ((error = register_security(&apparmor_ops))) {
+ AA_ERROR("Unable to register AppArmor\n");
+ goto register_security_out;
+ }
+
+ /* Report that AppArmor successfully initialized */
+ apparmor_initialized = 1;
+ if (apparmor_complain)
+ info_message("AppArmor initialized: complainmode enabled");
+ else
+ info_message("AppArmor initialized");
+
+ return error;
+
+register_security_out:
+ free_default_namespace();
+
+alloc_out:
+ destroy_apparmorfs();
+
+createfs_out:
+ return error;
+
+}
+
+security_initcall(apparmor_init);
+
+void apparmor_disable(void)
+{
+ /* Remove and release all the profiles on the profile list. */
+ mutex_lock(&aa_interface_lock);
+ aa_profile_ns_list_release();
+
+ /* FIXME: cleanup profiles references on files */
+ free_default_namespace();
+
+ /*
+ * Delay for an rcu cycle to make sure that all active task
+ * context readers have finished, and all profiles have been
+ * freed by their rcu callbacks.
+ */
+ synchronize_rcu();
+
+ destroy_apparmorfs();
+ mutex_unlock(&aa_interface_lock);
+
+ apparmor_initialized = 0;
+
+ info_message("AppArmor protection removed");
+}
+
+MODULE_DESCRIPTION("AppArmor process confinement");
+MODULE_AUTHOR("Novell/Immunix, http://bugs.opensuse.org");
+MODULE_LICENSE("GPL");

View File

@@ -1,59 +0,0 @@
---
security/apparmor/main.c | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -172,15 +172,18 @@ escape:
static char *aa_get_name(struct dentry *dentry, struct vfsmount *mnt,
char **buffer, int check)
{
- char *name;
+ char *buf = NULL, *name;
int is_dir, size = 256;
is_dir = (check & AA_CHECK_DIR) ? 1 : 0;
for (;;) {
- char *buf = kmalloc(size, GFP_KERNEL);
- if (!buf)
- return ERR_PTR(-ENOMEM);
+ char *b = krealloc(buf, size, GFP_KERNEL);
+
+ name = ERR_PTR(-ENOMEM);
+ if (!b)
+ break;
+ buf = b;
name = d_namespace_path(dentry, mnt, buf, size - is_dir);
@@ -195,8 +198,8 @@ static char *aa_get_name(struct dentry *
* This dentry is not connected to the
* namespace root -- reject access.
*/
- kfree(buf);
- return ERR_PTR(-ENOENT);
+ name = ERR_PTR(-ENOENT);
+ break;
}
if (is_dir && name[1] != '\0') {
/*
@@ -212,13 +215,15 @@ static char *aa_get_name(struct dentry *
return name;
}
if (PTR_ERR(name) != -ENAMETOOLONG)
- return name;
+ break;
- kfree(buf);
size <<= 1;
+ name = ERR_PTR(-ENAMETOOLONG);
if (size > apparmor_path_max)
- return ERR_PTR(-ENAMETOOLONG);
+ break;
}
+ kfree(buf);
+ return name;
}
static inline void aa_put_name_buffer(char *buffer)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,408 +0,0 @@
From: John Johansen <jjohansen@suse.de>
Subject: AppArmor: Simplified network controls for AppArmor
Simple network control determining which network families a confined
application has access to.
Signed-off-by: John Johansen <jjohansen@suse.de>
---
security/apparmor/Makefile | 7 +
security/apparmor/apparmor.h | 9 ++
security/apparmor/lsm.c | 129 ++++++++++++++++++++++++++++++++++-
security/apparmor/main.c | 107 ++++++++++++++++++++++++++++-
security/apparmor/module_interface.c | 26 ++++++-
5 files changed, 271 insertions(+), 7 deletions(-)
--- a/security/apparmor/Makefile
+++ b/security/apparmor/Makefile
@@ -8,6 +8,11 @@ apparmor-y := main.o list.o procattr.o l
quiet_cmd_make-caps = GEN $@
cmd_make-caps = 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 > $@
-$(obj)/main.o : $(obj)/capability_names.h
+quiet_cmd_make-af = GEN $@
+cmd_make-af = 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 > $@
+
+$(obj)/main.o : $(obj)/capability_names.h $(obj)/af_names.h
$(obj)/capability_names.h : $(srctree)/include/linux/capability.h
$(call cmd,make-caps)
+$(obj)/af_names.h : $(srctree)/include/linux/socket.h
+ $(call cmd,make-af)
--- a/security/apparmor/apparmor.h
+++ b/security/apparmor/apparmor.h
@@ -16,6 +16,8 @@
#include <linux/fs.h>
#include <linux/binfmts.h>
#include <linux/rcupdate.h>
+#include <linux/socket.h>
+#include <net/sock.h>
/*
* We use MAY_READ, MAY_WRITE, MAY_EXEC, MAY_APPEND and the following flags
@@ -208,6 +210,9 @@ struct aa_profile {
struct list_head task_contexts;
spinlock_t lock;
unsigned long int_flags;
+ u16 network_families[AF_MAX];
+ u16 audit_network[AF_MAX];
+ u16 quiet_network[AF_MAX];
};
extern struct list_head profile_ns_list;
@@ -254,6 +259,7 @@ struct aa_audit {
int request_mask, denied_mask, audit_mask;
struct iattr *iattr;
pid_t task, parent;
+ int family, type, protocol;
int error_code;
};
@@ -315,6 +321,9 @@ extern void aa_change_task_context(struc
struct aa_profile *previous_profile);
extern int aa_may_ptrace(struct aa_task_context *cxt,
struct aa_profile *tracee);
+extern int aa_net_perm(struct aa_profile *profile, char *operation,
+ int family, int type, int protocol);
+extern int aa_revalidate_sk(struct sock *sk, char *operation);
/* lsm.c */
extern int apparmor_initialized;
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -18,6 +18,7 @@
#include <linux/ctype.h>
#include <linux/sysctl.h>
#include <linux/audit.h>
+#include <net/sock.h>
#include "apparmor.h"
#include "inline.h"
@@ -663,6 +664,117 @@ static void apparmor_task_free_security(
aa_release(task);
}
+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_get_profile(current);
+ if (profile)
+ error = aa_net_perm(profile, "socket_create", family,
+ type, protocol);
+ aa_put_profile(profile);
+
+ return error;
+}
+
+static int apparmor_socket_post_create(struct socket *sock, int family,
+ int type, int protocol, int kern)
+{
+ struct sock *sk = sock->sk;
+
+ if (kern)
+ return 0;
+
+ return aa_revalidate_sk(sk, "socket_post_create");
+}
+
+static int apparmor_socket_bind(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_bind");
+}
+
+static int apparmor_socket_connect(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_connect");
+}
+
+static int apparmor_socket_listen(struct socket *sock, int backlog)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_listen");
+}
+
+static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_accept");
+}
+
+static int apparmor_socket_sendmsg(struct socket *sock,
+ struct msghdr *msg, int size)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_sendmsg");
+}
+
+static int apparmor_socket_recvmsg(struct socket *sock,
+ struct msghdr *msg, int size, int flags)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_recvmsg");
+}
+
+static int apparmor_socket_getsockname(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_getsockname");
+}
+
+static int apparmor_socket_getpeername(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_getpeername");
+}
+
+static int apparmor_socket_getsockopt(struct socket *sock, int level,
+ int optname)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_getsockopt");
+}
+
+static int apparmor_socket_setsockopt(struct socket *sock, int level,
+ int optname)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_setsockopt");
+}
+
+static int apparmor_socket_shutdown(struct socket *sock, int how)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_shutdown");
+}
+
static int apparmor_getprocattr(struct task_struct *task, char *name,
char **value)
{
@@ -763,9 +875,6 @@ struct security_operations apparmor_ops
.capable = apparmor_capable,
.syslog = cap_syslog,
- .netlink_send = cap_netlink_send,
- .netlink_recv = cap_netlink_recv,
-
.bprm_apply_creds = cap_bprm_apply_creds,
.bprm_set_security = apparmor_bprm_set_security,
.bprm_secureexec = apparmor_bprm_secureexec,
@@ -801,6 +910,20 @@ struct security_operations apparmor_ops
.getprocattr = apparmor_getprocattr,
.setprocattr = apparmor_setprocattr,
+
+ .socket_create = apparmor_socket_create,
+ .socket_post_create = apparmor_socket_post_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,
};
void info_message(const char *str)
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -14,6 +14,9 @@
#include <linux/audit.h>
#include <linux/mount.h>
#include <linux/ptrace.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <net/sock.h>
#include "apparmor.h"
@@ -116,6 +119,24 @@ static void aa_audit_file_mask(struct au
audit_log_format(ab, " %s=\"%s::%s\"", name, user, other);
}
+static const char *address_families[] = {
+#include "af_names.h"
+};
+
+static const char *sock_types[] = {
+ "unknown(0)",
+ "stream",
+ "dgram",
+ "raw",
+ "rdm",
+ "seqpacket",
+ "dccp",
+ "unknown(7)",
+ "unknown(8)",
+ "unknown(9)",
+ "packet",
+};
+
/**
* aa_audit - Log an audit event to the audit subsystem
* @profile: profile to check against
@@ -187,7 +208,25 @@ static int aa_audit_base(struct aa_profi
audit_log_untrustedstring(ab, sa->name2);
}
- audit_log_format(ab, " pid=%d", current->pid);
+ if (sa->family || sa->type) {
+ if (address_families[sa->family])
+ audit_log_format(ab, " family=\"%s\"",
+ address_families[sa->family]);
+ else
+ audit_log_format(ab, " family=\"unknown(%d)\"",
+ sa->family);
+
+ if (sock_types[sa->type])
+ audit_log_format(ab, " sock_type=\"%s\"",
+ sock_types[sa->type]);
+ else
+ audit_log_format(ab, " sock_type=\"unknown(%d)\"",
+ sa->type);
+
+ audit_log_format(ab, " protocol=%d", sa->protocol);
+ }
+
+ audit_log_format(ab, " pid=%d", current->pid);
if (profile) {
audit_log_format(ab, " profile=");
@@ -768,6 +807,72 @@ int aa_link(struct aa_profile *profile,
return error;
}
+int aa_net_perm(struct aa_profile *profile, char *operation,
+ int family, int type, int protocol)
+{
+ struct aa_audit sa;
+ int error = 0;
+ u16 family_mask, audit_mask, quiet_mask;
+
+ if ((family < 0) || (family >= AF_MAX))
+ return -EINVAL;
+
+ if ((type < 0) || (type >= SOCK_MAX))
+ return -EINVAL;
+
+ /* unix domain and netlink sockets are handled by ipc */
+ if (family == AF_UNIX || family == AF_NETLINK)
+ return 0;
+
+ family_mask = profile->network_families[family];
+ audit_mask = profile->audit_network[family];
+ quiet_mask = profile->quiet_network[family];
+
+ error = (family_mask & (1 << type)) ? 0 : -EACCES;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = operation;
+ sa.gfp_mask = GFP_KERNEL;
+ sa.family = family;
+ sa.type = type;
+ sa.protocol = protocol;
+ sa.error_code = error;
+
+ if (likely(!error)) {
+ if (!PROFILE_AUDIT(profile) && !(family_mask & audit_mask))
+ return 0;
+ } else if (!((1 << type) & ~quiet_mask)) {
+ return error;
+ }
+
+ error = aa_audit(profile, &sa);
+
+ return error;
+}
+
+int aa_revalidate_sk(struct sock *sk, char *operation)
+{
+ struct aa_profile *profile;
+ int error = 0;
+
+ /* this is some debugging code to flush out the network hooks that
+ that are called in interrupt context */
+ if (in_interrupt()) {
+ printk("AppArmor Debug: Hook being called from interrupt context\n");
+ dump_stack();
+ return 0;
+ }
+
+ profile = aa_get_profile(current);
+ if (profile)
+ error = aa_net_perm(profile, operation,
+ sk->sk_family, sk->sk_type,
+ sk->sk_protocol);
+ aa_put_profile(profile);
+
+ return error;
+}
+
/*******************************
* Global task related functions
*******************************/
--- a/security/apparmor/module_interface.c
+++ b/security/apparmor/module_interface.c
@@ -320,8 +320,8 @@ static struct aa_profile *aa_unpack_prof
struct aa_audit *sa)
{
struct aa_profile *profile = NULL;
-
- int error = -EPROTO;
+ size_t size = 0;
+ int i, error = -EPROTO;
profile = alloc_aa_profile();
if (!profile)
@@ -354,6 +354,28 @@ static struct aa_profile *aa_unpack_prof
if (!aa_is_u32(e, &(profile->set_caps), NULL))
goto fail;
+ size = aa_is_array(e, "net_allowed_af");
+ if (size) {
+ if (size > AF_MAX)
+ goto fail;
+
+ for (i = 0; i < size; i++) {
+ if (!aa_is_u16(e, &profile->network_families[i], NULL))
+ goto fail;
+ if (!aa_is_u16(e, &profile->audit_network[i], NULL))
+ goto fail;
+ if (!aa_is_u16(e, &profile->quiet_network[i], NULL))
+ goto fail;
+ }
+ if (!aa_is_nameX(e, AA_ARRAYEND, NULL))
+ goto fail;
+ /* allow unix domain and netlink sockets they are handled
+ * by IPC
+ */
+ }
+ profile->network_families[AF_UNIX] = 0xffff;
+ profile->network_families[AF_NETLINK] = 0xffff;
+
/* get file rules */
profile->file_rules = aa_unpack_dfa(e);
if (IS_ERR(profile->file_rules)) {

View File

@@ -1,461 +0,0 @@
From: John Johansen <jjohansen@suse.de>
Subject: AppArmor: per profile controls for system rlimits
Provide contol of rlimits on a per profile basis. Each profile provides
a per limit contol and corresponding hard limit value, such that when a
profile becomes attached to a task it sets the tasks limits to be <= to
the profiles specified limits. Note: the profile limit value will not
raise a tasks limit if it is already less than the profile mandates.
In addition to setting a tasks limits, the ability to set limits on
a confined task are controlled. AppArmor only controls the raising
of a tasks limits Tasks with CAP_SYS_RESOURCE can have their hard limits
raised up to the value specified by the profile. AppArmor does not
prevent a task for lowering its hard limits, nor does it provide
additional control on soft limits.
AppArmor only controls the limits specified in a profile so that
any limit not specified is free to be modified subject to standard
linux limitations.
---
security/apparmor/apparmor.h | 23 ++++++
security/apparmor/apparmorfs.c | 2
security/apparmor/lsm.c | 16 ++++
security/apparmor/main.c | 132 +++++++++++++++++++++++++++++++----
security/apparmor/module_interface.c | 56 ++++++++++++++
5 files changed, 215 insertions(+), 14 deletions(-)
--- a/security/apparmor/apparmor.h
+++ b/security/apparmor/apparmor.h
@@ -16,6 +16,7 @@
#include <linux/fs.h>
#include <linux/binfmts.h>
#include <linux/rcupdate.h>
+#include <linux/resource.h>
#include <linux/socket.h>
#include <net/sock.h>
@@ -136,6 +137,18 @@ extern unsigned int apparmor_path_max;
#define AA_ERROR(fmt, args...) printk(KERN_ERR "AppArmor: " fmt, ##args)
+/* struct aa_rlimit - rlimits settings for the profile
+ * @mask: which hard limits to set
+ * @limits: rlimit values that override task limits
+ *
+ * AppArmor rlimits are used to set confined task rlimits. Only the
+ * limits specified in @mask will be controlled by apparmor.
+ */
+struct aa_rlimit {
+ unsigned int mask;
+ struct rlimit limits[RLIM_NLIMITS];
+};
+
struct aa_profile;
/* struct aa_namespace - namespace for a set of profiles
@@ -170,6 +183,8 @@ struct aa_namespace {
* @audit_caps: caps that are to be audited
* @quiet_caps: caps that should not be audited
* @capabilities: capabilities granted by the process
+ * @rlimits: rlimits for the profile
+ * @task_count: how many tasks the profile is attached to
* @count: reference count of the profile
* @task_contexts: list of tasks confined by profile
* @lock: lock for the task_contexts list
@@ -206,6 +221,9 @@ struct aa_profile {
kernel_cap_t audit_caps;
kernel_cap_t quiet_caps;
+ struct aa_rlimit rlimits;
+ unsigned int task_count;
+
struct kref count;
struct list_head task_contexts;
spinlock_t lock;
@@ -257,6 +275,7 @@ struct aa_audit {
const char *name2;
const char *name3;
int request_mask, denied_mask, audit_mask;
+ int rlimit;
struct iattr *iattr;
pid_t task, parent;
int family, type, protocol;
@@ -324,6 +343,10 @@ extern int aa_may_ptrace(struct aa_task_
extern int aa_net_perm(struct aa_profile *profile, char *operation,
int family, int type, int protocol);
extern int aa_revalidate_sk(struct sock *sk, char *operation);
+extern int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource,
+ struct rlimit *new_rlim);
+extern void aa_set_rlimits(struct task_struct *task, struct aa_profile *profile);
+
/* lsm.c */
extern int apparmor_initialized;
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -105,7 +105,7 @@ static ssize_t aa_features_read(struct f
{
const char *features = "file=3.0 capability=2.0 network=1.0 "
"change_hat=1.4 change_profile=1.0 "
- "aanamespaces=1.0";
+ "aanamespaces=1.0 rlimit=1.0";
return simple_read_from_buffer(buf, size, ppos, features,
strlen(features));
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -866,6 +866,21 @@ static int apparmor_setprocattr(struct t
return error;
}
+static int apparmor_task_setrlimit(unsigned int resource,
+ struct rlimit *new_rlim)
+{
+ struct aa_profile *profile;
+ int error = 0;
+
+ profile = aa_get_profile(current);
+ if (profile) {
+ error = aa_task_setrlimit(profile, resource, new_rlim);
+ }
+ aa_put_profile(profile);
+
+ return error;
+}
+
struct security_operations apparmor_ops = {
.ptrace = apparmor_ptrace,
.capget = cap_capget,
@@ -907,6 +922,7 @@ struct security_operations apparmor_ops
.task_free_security = apparmor_task_free_security,
.task_post_setuid = cap_task_post_setuid,
.task_reparent_to_init = cap_task_reparent_to_init,
+ .task_setrlimit = apparmor_task_setrlimit,
.getprocattr = apparmor_getprocattr,
.setprocattr = apparmor_setprocattr,
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -177,6 +177,9 @@ static int aa_audit_base(struct aa_profi
if (sa->request_mask)
audit_log_format(ab, " fsuid=%d", current->fsuid);
+ if (sa->rlimit)
+ audit_log_format(ab, " rlimit=%d", sa->rlimit - 1);
+
if (sa->iattr) {
struct iattr *iattr = sa->iattr;
@@ -872,6 +875,79 @@ int aa_revalidate_sk(struct sock *sk, ch
return error;
}
+/**
+ * aa_task_setrlimit - test permission to set an rlimit
+ * @profile - profile confining the task
+ * @resource - the resource being set
+ * @new_rlim - the new resource limit
+ *
+ * Control raising the processes hard limit.
+ */
+int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource,
+ struct rlimit *new_rlim)
+{
+ struct aa_audit sa;
+ int error = 0;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = "setrlimit";
+ sa.gfp_mask = GFP_KERNEL;
+ sa.rlimit = resource + 1;
+
+ if (profile->rlimits.mask & (1 << resource) &&
+ new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max) {
+ sa.error_code = -EACCES;
+
+ error = aa_audit(profile, &sa);
+ }
+
+ return error;
+}
+
+static int aa_rlimit_nproc(struct aa_profile *profile) {
+ if (profile && (profile->rlimits.mask & (1 << RLIMIT_NPROC)) &&
+ profile->task_count >= profile->rlimits.limits[RLIMIT_NPROC].rlim_max)
+ return -EAGAIN;
+ return 0;
+}
+
+void aa_set_rlimits(struct task_struct *task, struct aa_profile *profile)
+{
+ int i, mask;
+
+ if (!profile)
+ return;
+
+ if (!profile->rlimits.mask)
+ return;
+
+ task_lock(task->group_leader);
+ mask = 1;
+ for (i = 0; i < RLIM_NLIMITS; i++, mask <<= 1) {
+ struct rlimit new_rlim, *old_rlim;
+
+ /* check to see if NPROC which is per profile and handled
+ * in clone/exec or whether this is a limit to be set
+ * can't set cpu limit either right now
+ */
+ if (i == RLIMIT_NPROC || i == RLIMIT_CPU)
+ continue;
+
+ old_rlim = task->signal->rlim + i;
+ new_rlim = *old_rlim;
+
+ if (mask & profile->rlimits.mask &&
+ profile->rlimits.limits[i].rlim_max < new_rlim.rlim_max) {
+ new_rlim.rlim_max = profile->rlimits.limits[i].rlim_max;
+ /* soft limit should not exceed hard limit */
+ if (new_rlim.rlim_cur > new_rlim.rlim_max)
+ new_rlim.rlim_cur = new_rlim.rlim_max;
+ }
+
+ *old_rlim = new_rlim;
+ }
+ task_unlock(task->group_leader);
+}
/*******************************
* Global task related functions
@@ -885,6 +961,7 @@ int aa_revalidate_sk(struct sock *sk, ch
*/
int aa_clone(struct task_struct *child)
{
+ struct aa_audit sa;
struct aa_task_context *cxt, *child_cxt;
struct aa_profile *profile;
@@ -894,6 +971,11 @@ int aa_clone(struct task_struct *child)
if (!child_cxt)
return -ENOMEM;
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = "clone";
+ sa.task = child->pid;
+ sa.gfp_mask = GFP_KERNEL;
+
repeat:
profile = aa_get_profile(current);
if (profile) {
@@ -910,18 +992,22 @@ repeat:
goto repeat;
}
+ if (aa_rlimit_nproc(profile)) {
+ sa.info = "rlimit nproc limit exceeded";
+ unlock_profile(profile);
+ aa_audit_reject(profile, &sa);
+ aa_put_profile(profile);
+ return -EAGAIN;
+ }
+
/* No need to grab the child's task lock here. */
aa_change_task_context(child, child_cxt, profile,
cxt->cookie, cxt->previous_profile);
+
unlock_profile(profile);
if (APPARMOR_COMPLAIN(child_cxt) &&
profile == profile->ns->null_complain_profile) {
- struct aa_audit sa;
- memset(&sa, 0, sizeof(sa));
- sa.operation = "clone";
- sa.gfp_mask = GFP_KERNEL;
- sa.task = child->pid;
aa_audit_hint(profile, &sa);
}
aa_put_profile(profile);
@@ -1156,6 +1242,10 @@ repeat:
sa.task = current->parent->pid;
aa_audit_reject(profile, &sa);
}
+ if (PTR_ERR(old_profile) == -EAGAIN) {
+ sa.info = "rlimit nproc limit exceeded";
+ aa_audit_reject(profile, &sa);
+ }
new_profile = old_profile;
goto cleanup;
}
@@ -1296,6 +1386,12 @@ static int do_change_profile(struct aa_p
goto out;
}
+ if ((error = aa_rlimit_nproc(new_profile))) {
+ sa->info = "rlimit nproc limit exceeded";
+ aa_audit_reject(cxt->profile, sa);
+ goto out;
+ }
+
if (new_profile == ns->null_complain_profile)
aa_audit_hint(cxt->profile, sa);
@@ -1482,17 +1578,18 @@ struct aa_profile *__aa_replace_profile(
cxt = lock_task_and_profiles(task, profile);
if (unlikely(profile && profile->isstale)) {
- task_unlock(task);
- unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
- aa_free_task_context(new_cxt);
- return ERR_PTR(-ESTALE);
+ old_profile = ERR_PTR(-ESTALE);
+ goto error;
}
if ((current->ptrace & PT_PTRACED) && aa_may_ptrace(cxt, profile)) {
- task_unlock(task);
- unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
- aa_free_task_context(new_cxt);
- return ERR_PTR(-EPERM);
+ old_profile = ERR_PTR(-EPERM);
+ goto error;
+ }
+
+ if (aa_rlimit_nproc(profile)) {
+ old_profile = ERR_PTR(-EAGAIN);
+ goto error;
}
if (cxt)
@@ -1500,8 +1597,15 @@ struct aa_profile *__aa_replace_profile(
aa_change_task_context(task, new_cxt, profile, 0, NULL);
task_unlock(task);
+ aa_set_rlimits(task, profile);
unlock_both_profiles(profile, old_profile);
return old_profile;
+
+error:
+ task_unlock(task);
+ unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
+ aa_free_task_context(new_cxt);
+ return old_profile;
}
/**
@@ -1566,6 +1670,7 @@ void aa_change_task_context(struct task_
if (old_cxt) {
list_del_init(&old_cxt->list);
+ old_cxt->profile->task_count--;
call_rcu(&old_cxt->rcu, free_aa_task_context_rcu_callback);
}
if (new_cxt) {
@@ -1577,6 +1682,7 @@ void aa_change_task_context(struct task_
new_cxt->cookie = cookie;
new_cxt->task = task;
new_cxt->profile = aa_dup_profile(profile);
+ profile->task_count++;
new_cxt->previous_profile = aa_dup_profile(previous_profile);
list_move(&new_cxt->list, &profile->task_contexts);
}
--- a/security/apparmor/module_interface.c
+++ b/security/apparmor/module_interface.c
@@ -177,6 +177,22 @@ fail:
return 0;
}
+static int aa_is_u64(struct aa_ext *e, u64 *data, const char *name)
+{
+ void *pos = e->pos;
+ if (aa_is_nameX(e, AA_U64, name)) {
+ if (!aa_inbounds(e, sizeof(u64)))
+ goto fail;
+ if (data)
+ *data = le64_to_cpu(get_unaligned((u64 *)e->pos));
+ e->pos += sizeof(u64);
+ return 1;
+ }
+fail:
+ e->pos = pos;
+ return 0;
+}
+
static size_t aa_is_array(struct aa_ext *e, const char *name)
{
void *pos = e->pos;
@@ -311,6 +327,39 @@ fail:
return 0;
}
+int aa_unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
+{
+ void *pos = e->pos;
+
+ /* rlimits are optional */
+ if (aa_is_nameX(e, AA_STRUCT, "rlimits")) {
+ int i, size;
+ u32 tmp = 0;
+ if (!aa_is_u32(e, &tmp, NULL))
+ goto fail;
+ profile->rlimits.mask = tmp;
+
+ size = aa_is_array(e, NULL);
+ if (size != RLIM_NLIMITS)
+ goto fail;
+ for (i = 0; i < size; i++) {
+ u64 tmp = 0;
+ if (!aa_is_u64(e, &tmp, NULL))
+ goto fail;
+ profile->rlimits.limits[i].rlim_max = tmp;
+ }
+ if (!aa_is_nameX(e, AA_ARRAYEND, NULL))
+ goto fail;
+ if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
+ goto fail;
+ }
+ return 1;
+
+fail:
+ e->pos = pos;
+ return 0;
+}
+
/**
* aa_unpack_profile - unpack a serialized profile
* @e: serialized data extent information
@@ -354,6 +403,9 @@ static struct aa_profile *aa_unpack_prof
if (!aa_is_u32(e, &(profile->set_caps), NULL))
goto fail;
+ if (!aa_unpack_rlimits(e, profile))
+ goto fail;
+
size = aa_is_array(e, "net_allowed_af");
if (size) {
if (size > AF_MAX)
@@ -613,6 +665,8 @@ ssize_t aa_replace_profile(void *udata,
sa.operation = "profile_load";
goto out;
}
+ /* do not fail replacement based off of profile's NPROC rlimit */
+
/*
* Replacement needs to allocate a new aa_task_context for each
* task confined by old_profile. To do this the profile locks
@@ -633,6 +687,7 @@ ssize_t aa_replace_profile(void *udata,
task_lock(task);
task_replace(task, new_cxt, new_profile);
task_unlock(task);
+ aa_set_rlimits(task, new_profile);
new_cxt = NULL;
}
unlock_both_profiles(old_profile, new_profile);
@@ -655,6 +710,7 @@ out:
*
* remove a profile from the profile list and all aa_task_context references
* to said profile.
+ * NOTE: removing confinement does not restore rlimits to preconfinemnet values
*/
ssize_t aa_remove_profile(char *name, size_t size)
{

View File

@@ -1,73 +0,0 @@
---
security/apparmor/apparmor.h | 1 +
security/apparmor/match.c | 9 +++++++--
security/apparmor/match.h | 2 ++
3 files changed, 10 insertions(+), 2 deletions(-)
--- a/security/apparmor/apparmor.h
+++ b/security/apparmor/apparmor.h
@@ -87,6 +87,7 @@
AA_AUDIT_FIELD)
#define AA_VALID_PERM_MASK (AA_FILE_PERMS | AA_SHARED_PERMS)
+#define AA_VALID_PERM2_MASK 0x0fffffff
#define AA_SECURE_EXEC_NEEDED 1
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -83,6 +83,7 @@ int unpack_dfa(struct aa_dfa *dfa, void
switch(table->td_id) {
case YYTD_ID_ACCEPT:
+ case YYTD_ID_ACCEPT2:
case YYTD_ID_BASE:
dfa->tables[table->td_id - 1] = table;
if (table->td_flags != YYTD_DATA32)
@@ -134,7 +135,8 @@ int verify_dfa(struct aa_dfa *dfa)
int error = -EPROTO;
/* check that required tables exist */
- if (!(dfa->tables[YYTD_ID_ACCEPT -1 ] &&
+ if (!(dfa->tables[YYTD_ID_ACCEPT - 1] &&
+ dfa->tables[YYTD_ID_ACCEPT2 - 1] &&
dfa->tables[YYTD_ID_DEF - 1] &&
dfa->tables[YYTD_ID_BASE - 1] &&
dfa->tables[YYTD_ID_NXT - 1] &&
@@ -144,7 +146,8 @@ int verify_dfa(struct aa_dfa *dfa)
/* accept.size == default.size == base.size */
state_count = dfa->tables[YYTD_ID_BASE - 1]->td_lolen;
if (!(state_count == dfa->tables[YYTD_ID_DEF - 1]->td_lolen &&
- state_count == dfa->tables[YYTD_ID_ACCEPT - 1]->td_lolen))
+ state_count == dfa->tables[YYTD_ID_ACCEPT - 1]->td_lolen &&
+ state_count == dfa->tables[YYTD_ID_ACCEPT2 - 1]->td_lolen))
goto out;
/* next.size == chk.size */
@@ -177,6 +180,8 @@ int verify_dfa(struct aa_dfa *dfa)
if (mode & ~AA_VALID_PERM_MASK)
goto out;
+ if (ACCEPT_TABLE2(dfa)[i] & ~AA_VALID_PERM2_MASK)
+ goto out;
/* if any exec modifier is set MAY_EXEC must be set */
if ((mode & AA_USER_EXEC_TYPE) && !(mode & AA_USER_EXEC))
--- a/security/apparmor/match.h
+++ b/security/apparmor/match.h
@@ -39,6 +39,7 @@ struct table_set_header {
#define YYTD_ID_DEF 4
#define YYTD_ID_EC 5
#define YYTD_ID_META 6
+#define YYTD_ID_ACCEPT2 7
#define YYTD_ID_NXT 8
@@ -60,6 +61,7 @@ struct table_header {
#define CHECK_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_CHK - 1]->td_data))
#define EQUIV_TABLE(DFA) ((u8 *)((DFA)->tables[YYTD_ID_EC - 1]->td_data))
#define ACCEPT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT - 1]->td_data))
+#define ACCEPT_TABLE2(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT2 -1]->td_data))
struct aa_dfa {
struct table_header *tables[YYTD_ID_NXT];

View File

@@ -1,141 +0,0 @@
---
Documentation/lsm/AppArmor-Security-Goal.txt | 134 +++++++++++++++++++++++++++
1 file changed, 134 insertions(+)
--- /dev/null
+++ b/Documentation/lsm/AppArmor-Security-Goal.txt
@@ -0,0 +1,134 @@
+AppArmor Security Goal
+Crispin Cowan, PhD
+MercenaryLinux.com
+
+This document specifies the security goal that AppArmor is intended to
+achieve, so that users can evaluate whether AppArmor will meet their
+needs, and kernel developers can evaluate whether AppArmor is living up
+to its claims. This document is *not* a general purpose explanation of
+how AppArmor works, nor is it an explanation for why one might want to
+use AppArmor rather than some other system.
+
+AppArmor is intended to limit system damage from attackers exploiting
+vulnerabilities in applications that the system hosts. The threat is
+that an attacker can cause a vulnerable application to do something
+unexpected and undesirable. AppArmor addresses this threat by confining
+the application to access only the resources it needs to access to
+execute properly, effectively imposing "least privilege" execution on
+the application.
+
+Applications interact with the rest of the system via resources
+including files, interprocess communication, networking, capabilities,
+and execution of other applications. The purpose of least privilege is
+to bound the damage that a malicious user or code can do by removing
+access to resources that the application does not need for its intended
+function. This is true for all access control systems, including AppArmor.
+
+The "attacker" is someone trying to gain the privileges of a process for
+themselves. For instance, a policy for a web server might grant read
+only access to most web documents, preventing an attacker who can
+corrupt the web server from defacing the web pages. A web server has
+access to the web server's local file system, and a network attacker
+trying to hack the web server does not have such file access. An e-mail
+attacker attempting to infect the recipient of the e-mail does not have
+access to the files that the victim user's mail client does. By limiting
+the scope of access for an application, AppArmor can limit the damage an
+attacker can do by exploiting vulnerabilities in applications.
+
+An "application" is one or more related processes performing a function,
+e.g. the gang of processes that constitute an Apache web server, or a
+Postfix mail server. AppArmor *only* confines processes that the
+AppArmor policy says it should confine, and other processes are
+permitted to do anything that DAC permits. This is sometimes known as a
+targeted security policy.
+
+AppArmor does not provide a "default" policy that applies to all
+processes. So to defend an entire host, you have to piece-wise confine
+each process that is exposed to potential attack. For instance, to
+defend a system against network attack, place AppArmor profiles around
+every application that accesses the network. This limits the damage a
+network attacker can do to the file system to only those files granted
+by the profiles for the network-available applications. Similarly, to
+defend a system against attack from the console, place AppArmor profiles
+around every application that accessed the keyboard and mouse. The
+system is "defended" in that the worst the attacker can do to corrupt
+the system is limited to the transitive closure of what the confined
+processes are allowed to access.
+
+AppArmor currently mediates access to files, ability to use POSIX.1e
+Capabilities, and coarse-grained control on network access. This is
+sufficient to prevent a confined process from *directly* corrupting the
+file system. It is not sufficient to prevent a confined process from
+*indirectly* corrupting the system by influencing some other process to
+do the dirty deed. But to do so requires a complicit process that can be
+manipulated through another channel such as IPC. A "complicit" process
+is either a malicious process the attacker somehow got control of, or is
+a process that is actively listening to IPC of some kind and can be
+corrupted via IPC.
+
+The only IPC that AppArmor mediates is access to named sockets, FIFOs,
+etc. that appear in the file system name space, a side effect of
+AppArmor's file access mediation. Future versions of AppArmor will
+mediate more resources, including finer grained network access controls,
+and controls on various forms of IPC.
+
+AppArmor specifies the programs to be confined and the resources they
+can access in a syntax similar to how users are accustomed to accessing
+those resources. So file access controls are specified using absolute
+paths with respect to the name space the process is in. POSIX.1e
+capabilities are specified by name. Network access controls currently
+are specified by simply naming the protocol that can be used e.g. tcp,
+udp, and in the future will be more general, resembling firewall rules.
+
+Thus the AppArmor security goal should be considered piecewise from the
+point of view of a single confined process: that process should only be
+able to access the resources specified in its profile:
+
+ * can only access files that are reachable in its name space by path
+ names matching its profile, and only with the permitted modes:
+ read, append, write, memory map, execute, and link
+ * can only use the POSIX.1e capabilities listed in the profile
+ * can only perform the network operations listed in the profile
+
+Security issues that AppArmor explicitly does *not* address:
+
+ * Processes that are not confined by AppArmor are not restricted in
+ any way by AppArmor. If an unconfined process is considered an
+ unacceptable threat, then confine additional applications until
+ adequate security is achieved.
+ * A process that is not permitted to directly access a resource can
+ influence some other process that does have access to the resource
+ may do so, if the "influence" is a permitted action.
+ * A confined process may only access a file if it has at least one
+ of the files aliases specified in its profile. If a file alias is
+ not specified in the profile then it can not be accessed by that
+ path. The creation of aliases needs to be tightly controlled in
+ confined applications, hard links creation should be limited to
+ provide adequate security.
+ * A confined process can operate on a file descriptor passed to it
+ by an unconfined process, even if it manipulates a file not in the
+ confined process's profile. To block this attack, confine the
+ process that passed the file descriptor.
+ * Process activities not currently mediated by AppArmor are
+ permitted, e.g. confined processes can perform any IPC that DAC
+ permits, other than signals as mediated by CAP_KILL.
+ * Manipulating AppArmor policy requires being both root privileged
+ and not being confined by AppArmor, thus there is explicitly no
+ capability for non-privileged users to change AppArmor policy.
+ * AppArmor confines processes if they are children of a confined
+ process, or if the name of the exec'd child matches the name of an
+ AppArmor profile. Another process could copy a program to a
+ different path name and then execute it without confinement, but
+ the other process would have to have permission to do so in the
+ first place. To prevent this, confine the other process and
+ additional applications until adequate security is achieved.
+ * Mount and namespace manipulations can be used to arbitrarily
+ change the pathnames that files appear at, and thus completely
+ bypass AppArmor policy. To prevent this, processes confined by
+ AppArmor are currently not permitted to call mount or manipulate
+ name spaces at all. A future development may provide more granular
+ controls on mount and namespace manipulations.
+ * AppArmor does not slice bread, cure cancer, bring world peace, or
+ provide perfect security. This list may be expanded :-)
+
+

View File

@@ -1,18 +0,0 @@
---
security/apparmor/main.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -159,8 +159,10 @@ static int aa_audit_base(struct aa_profi
return type == AUDIT_APPARMOR_ALLOWED ? 0 : -ENOMEM;
}
+ audit_log_format(ab, " type=%d", type);
+
if (sa->operation)
- audit_log_format(ab, "operation=\"%s\"", sa->operation);
+ audit_log_format(ab, " operation=\"%s\"", sa->operation);
if (sa->info) {
audit_log_format(ab, " info=\"%s\"", sa->info);

View File

@@ -1,16 +0,0 @@
---
security/apparmor/main.c | 3 +++
1 file changed, 3 insertions(+)
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -405,6 +405,9 @@ static int aa_audit_base(struct aa_profi
if (sa->denied_mask)
aa_audit_file_mask(ab, "denied_mask", sa->denied_mask);
+ if (sa->request_mask)
+ audit_log_format(ab, " fsuid=%d", current->fsuid);
+
if (sa->iattr) {
struct iattr *iattr = sa->iattr;

View File

@@ -1,87 +0,0 @@
---
security/apparmor/apparmor.h | 10 ++++++++++
security/apparmor/apparmorfs.c | 4 ++--
security/apparmor/lsm.c | 15 ++++++---------
security/apparmor/module_interface.c | 2 ++
4 files changed, 20 insertions(+), 11 deletions(-)
--- a/security/apparmor/apparmor.h
+++ b/security/apparmor/apparmor.h
@@ -158,8 +158,17 @@ struct aa_namespace {
* @file_rules: dfa containing the profiles file rules
* @flags: flags controlling profile behavior
* @isstale: flag indicating if profile is stale
+ * @set_caps: capabilities that are being set
+ * @capabilities: capabilities mask
+ * @audit_caps: caps that are to be audited
+ * @quiet_caps: caps that should not be audited
* @capabilities: capabilities granted by the process
* @count: reference count of the profile
+ * @task_contexts: list of tasks confined by profile
+ * @lock: lock for the task_contexts list
+ * @network_families: basic network permissions
+ * @audit_network: which network permissions to force audit
+ * @quiet_network: which network permissions to quiet rejects
*
* The AppArmor profile contains the basic confinement data. Each profile
* has a name, and all nonstale profile are in a profile namespace.
@@ -183,6 +192,7 @@ struct aa_profile {
} flags;
int isstale;
+ kernel_cap_t set_caps;
kernel_cap_t capabilities;
kernel_cap_t audit_caps;
kernel_cap_t quiet_caps;
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -103,8 +103,8 @@ static struct file_operations apparmorfs
static ssize_t aa_features_read(struct file *file, char __user *buf,
size_t size, loff_t *ppos)
{
- const char *features = "file=3.0 capability=1.0 network=1.0 "
- "change_hat=1.3 change_profile=1.0 "
+ const char *features = "file=3.0 capability=2.0 network=1.0 "
+ "change_hat=1.4 change_profile=1.0 "
"aanamespaces=1.0";
return simple_read_from_buffer(buf, size, ppos, features,
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -210,19 +210,16 @@ static int apparmor_ptrace(struct task_s
static int apparmor_capable(struct task_struct *task, int cap)
{
int error;
+ struct aa_task_context *cxt;
/* cap_capable returns 0 on success, else -EPERM */
error = cap_capable(task, cap);
- if (!error) {
- struct aa_task_context *cxt;
-
- rcu_read_lock();
- cxt = aa_task_context(task);
- if (cxt)
- error = aa_capability(cxt, cap);
- rcu_read_unlock();
- }
+ rcu_read_lock();
+ cxt = aa_task_context(task);
+ if (cxt && (!error || cap_raised(cxt->profile->set_caps, cap)))
+ error = aa_capability(cxt, cap);
+ rcu_read_unlock();
return error;
}
--- a/security/apparmor/module_interface.c
+++ b/security/apparmor/module_interface.c
@@ -316,6 +316,8 @@ static struct aa_profile *aa_unpack_prof
goto fail;
if (!aa_is_u32(e, &(profile->quiet_caps), NULL))
goto fail;
+ if (!aa_is_u32(e, &(profile->set_caps), NULL))
+ goto fail;
size = aa_is_array(e, "net_allowed_af");
if (size) {

View File

@@ -1,95 +0,0 @@
This patch removes magic cookies from the aa_change_profile interface, making
it a true one-way transition.
It also fixes a refcounting bug with previous_profile by virtue of removing
the broken code entirely.
Signed-off-by: Kenny Graunke <kgraunke@novell.com>
Signed-off-by: Seth Arnold <seth.arnold@suse.de>
---
security/apparmor/apparmor.h | 2 +-
security/apparmor/main.c | 21 +++++++--------------
security/apparmor/procattr.c | 9 +--------
3 files changed, 9 insertions(+), 23 deletions(-)
--- a/security/apparmor/apparmor.h
+++ b/security/apparmor/apparmor.h
@@ -211,7 +211,7 @@ extern int aa_clone(struct task_struct *
extern int aa_register(struct linux_binprm *bprm);
extern void aa_release(struct task_struct *task);
extern int aa_change_hat(const char *id, u64 hat_magic);
-extern int aa_change_profile(const char *name, u64 cookie);
+extern int aa_change_profile(const char *name);
extern struct aa_profile *__aa_find_profile(const char *name,
struct list_head *list);
extern struct aa_profile *__aa_replace_profile(struct task_struct *task,
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -1110,20 +1110,18 @@ out:
}
/**
- * aa_change_profile - change profile to/from previous stored profile
+ * aa_change_profile - perform a one-way profile transition
* @name: name of profile to change to
- * @cookie: magic value to validate the profile change
*
- * Change to new profile @name, and store the @cookie in the current task
- * context. If the new @name is %NULL and the @cookie matches that
- * stored in the current task context, return to the previous profile.
+ * Change to new profile @name. Unlike with hats, there is no way
+ * to change back.
*
* Returns %0 on success, error otherwise.
*/
-int aa_change_profile(const char *name, u64 cookie)
+int aa_change_profile(const char *name)
{
struct aa_task_context *cxt;
- struct aa_profile *profile, *previous_profile;
+ struct aa_profile *profile;
struct aa_audit sa;
int error = 0;
@@ -1139,7 +1137,6 @@ repeat:
return -EPERM;
}
profile = aa_dup_profile(cxt->profile);
- previous_profile = aa_dup_profile(cxt->previous_profile);
task_unlock(current);
if (name) {
@@ -1150,13 +1147,9 @@ repeat:
aa_put_profile(profile);
return -EACCES;
}
- error = do_change_profile(profile, name, cookie, 0, &sa);
- } else if (previous_profile)
- error = do_change_profile(profile, previous_profile->name,
- cookie, 1, &sa);
- /* else ignore restores when there is no saved profile */
+ error = do_change_profile(profile, name, 0, 0, &sa);
+ }
- aa_put_profile(previous_profile);
aa_put_profile(profile);
if (error == -ESTALE)
goto repeat;
--- a/security/apparmor/procattr.c
+++ b/security/apparmor/procattr.c
@@ -87,14 +87,7 @@ int aa_setprocattr_changehat(char *args)
int aa_setprocattr_changeprofile(char *args)
{
- char *name;
- u64 cookie;
-
- name = split_token_from_name("change_profile", args, &cookie);
- if (IS_ERR(name))
- return PTR_ERR(name);
-
- return aa_change_profile(name, cookie);
+ return aa_change_profile(args);
}
int aa_setprocattr_setprofile(struct task_struct *task, char *args)

View File

@@ -1,86 +0,0 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Add d_namespace_path() to compute namespace relative pathnames
In AppArmor, we are interested in pathnames relative to the namespace root.
This is the same as d_path() except for the root where the search ends. Add
a function for computing the namespace-relative path.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/dcache.c | 6 +++---
fs/namespace.c | 27 +++++++++++++++++++++++++++
include/linux/dcache.h | 2 ++
include/linux/mount.h | 2 ++
4 files changed, 34 insertions(+), 3 deletions(-)
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1782,9 +1782,9 @@ shouldnt_be_hashed:
*
* Returns the buffer or an error code.
*/
-static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
- struct dentry *root, struct vfsmount *rootmnt,
- char *buffer, int buflen, int fail_deleted)
+char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
+ struct dentry *root, struct vfsmount *rootmnt,
+ char *buffer, int buflen, int fail_deleted)
{
int namelen, is_slash, vfsmount_locked = 0;
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1883,3 +1883,30 @@ void __put_mnt_ns(struct mnt_namespace *
release_mounts(&umount_list);
kfree(ns);
}
+
+char *d_namespace_path(struct dentry *dentry, struct vfsmount *vfsmnt,
+ char *buf, int buflen)
+{
+ struct vfsmount *rootmnt, *nsrootmnt = NULL;
+ struct dentry *root = NULL;
+ char *res;
+
+ read_lock(&current->fs->lock);
+ rootmnt = mntget(current->fs->rootmnt);
+ read_unlock(&current->fs->lock);
+ spin_lock(&vfsmount_lock);
+ if (rootmnt->mnt_ns)
+ nsrootmnt = mntget(rootmnt->mnt_ns->root);
+ spin_unlock(&vfsmount_lock);
+ mntput(rootmnt);
+ if (nsrootmnt)
+ root = dget(nsrootmnt->mnt_root);
+ res = __d_path(dentry, vfsmnt, root, nsrootmnt, buf, buflen, 1);
+ dput(root);
+ mntput(nsrootmnt);
+ /* Prevent empty path for lazily unmounted filesystems. */
+ if (!IS_ERR(res) && *res == '\0')
+ *--res = '.';
+ return res;
+}
+EXPORT_SYMBOL(d_namespace_path);
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -300,6 +300,8 @@ extern int d_validate(struct dentry *, s
*/
extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...);
+extern char *__d_path(struct dentry *, struct vfsmount *, struct dentry *,
+ struct vfsmount *, char *, int, int);
extern char * d_path(struct dentry *, struct vfsmount *, char *, int);
/* Allocation counts.. */
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -103,5 +103,7 @@ extern void shrink_submounts(struct vfsm
extern spinlock_t vfsmount_lock;
extern dev_t name_to_dev_t(char *name);
+extern char *d_namespace_path(struct dentry *, struct vfsmount *, char *, int);
+
#endif
#endif /* _LINUX_MOUNT_H */

View File

@@ -1,47 +0,0 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Switch to vfs_permission() in do_path_lookup()
Switch from file_permission() to vfs_permission() in do_path_lookup():
this avoids calling permission() with a NULL nameidata here.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/namei.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1147,25 +1147,24 @@ static int fastcall do_path_lookup(int d
nd->dentry = dget(fs->pwd);
read_unlock(&fs->lock);
} else {
- struct dentry *dentry;
-
file = fget_light(dfd, &fput_needed);
retval = -EBADF;
if (!file)
goto out_fail;
- dentry = file->f_path.dentry;
+ nd->dentry = file->f_path.dentry;
+ nd->mnt = file->f_path.mnt;
retval = -ENOTDIR;
- if (!S_ISDIR(dentry->d_inode->i_mode))
+ if (!S_ISDIR(nd->dentry->d_inode->i_mode))
goto fput_fail;
- retval = file_permission(file, MAY_EXEC);
+ retval = vfs_permission(nd, MAY_EXEC);
if (retval)
goto fput_fail;
- nd->mnt = mntget(file->f_path.mnt);
- nd->dentry = dget(dentry);
+ mntget(nd->mnt);
+ dget(nd->dentry);
fput_light(file, fput_needed);
}

View File

@@ -1,42 +0,0 @@
From: Andreas Gruenbacher <agruen@use.de>
Subject: Check for NULL nameidata in ecryptfs_d_revalidate
Some filesystems like nfsd and ecryptfs use lookup_one_len() on other
filesystems. This causes d_revalidate() calls with nd == NULL through
__lookup_hash() and cached_lookup(), so we need to check for NULL
nameidata.
Signed-off-by: Andreas Gruenbacher <agruen@use.de>
Cc: Mike Halcrow <mhalcrow@us.ibm.com>
Cc: Phillip Hellewell <phillip@hellewell.homeip.net>
---
fs/ecryptfs/dentry.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
--- a/fs/ecryptfs/dentry.c
+++ b/fs/ecryptfs/dentry.c
@@ -51,13 +51,17 @@ static int ecryptfs_d_revalidate(struct
if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate)
goto out;
- dentry_save = nd->dentry;
- vfsmount_save = nd->mnt;
- nd->dentry = lower_dentry;
- nd->mnt = lower_mnt;
+ if (nd) {
+ dentry_save = nd->dentry;
+ vfsmount_save = nd->mnt;
+ nd->dentry = lower_dentry;
+ nd->mnt = lower_mnt;
+ }
rc = lower_dentry->d_op->d_revalidate(lower_dentry, nd);
- nd->dentry = dentry_save;
- nd->mnt = vfsmount_save;
+ if (nd) {
+ nd->dentry = dentry_save;
+ nd->mnt = vfsmount_save;
+ }
if (dentry->d_inode) {
struct inode *lower_inode =
ecryptfs_inode_to_lower(dentry->d_inode);

View File

@@ -1,173 +0,0 @@
Allow the dfa to return its state, which will allow for pair matches.
---
security/apparmor/apparmor.h | 7 ++-
security/apparmor/inline.h | 2
security/apparmor/match.c | 99 ++++++++++++++++++++++++++++++++++++++-----
security/apparmor/match.h | 2
4 files changed, 99 insertions(+), 11 deletions(-)
--- a/security/apparmor/apparmor.h
+++ b/security/apparmor/apparmor.h
@@ -45,7 +45,6 @@
#define AA_SECURE_EXEC_NEEDED 1
-
/* Control parameters (0 or 1), settable thru module/boot flags or
* via /sys/kernel/security/apparmor/control */
extern int apparmor_complain;
@@ -260,5 +259,11 @@ extern void aa_match_free(struct aa_dfa
extern int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size);
extern int verify_dfa(struct aa_dfa *dfa);
extern unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str);
+extern unsigned int aa_dfa_next_state(struct aa_dfa *dfa, unsigned int start,
+ const char *str);
+extern unsigned int aa_match_state(struct aa_dfa *dfa, unsigned int start,
+ const char *str, unsigned int *final);
+extern unsigned int aa_dfa_null_transition(struct aa_dfa *dfa,
+ unsigned int start);
#endif /* __APPARMOR_H */
--- a/security/apparmor/inline.h
+++ b/security/apparmor/inline.h
@@ -12,6 +12,8 @@
#include <linux/sched.h>
+#include "match.h"
+
static inline int mediated_filesystem(struct inode *inode)
{
return !(inode->i_sb->s_flags & MS_NOUSER);
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -208,22 +208,26 @@ void aa_match_free(struct aa_dfa *dfa)
}
/**
- * aa_dfa_match - match @path against @dfa starting in @state
- * @dfa: the dfa to match @path against
- * @state: the state to start matching in
- * @path: the path to match against the dfa
+ * aa_dfa_next_state - traverse @dfa to find state @str stops at
+ * @dfa: the dfa to match @str against
+ * @start: the state of the dfa to start matching in
+ * @str: the string to match against the dfa
*
- * aa_dfa_match will match the full path length and return the state it
- * finished matching in. The final state is used to look up the accepting
- * label.
+ * aa_dfa_next_state will match @str against the dfa and return the state it
+ * finished matching in. The final state can be used to look up the accepting
+ * label, or as the start state of a continuing match.
*/
-unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str)
+unsigned int aa_dfa_next_state(struct aa_dfa *dfa, unsigned int start,
+ const char *str)
{
u16 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
u16 *next = NEXT_TABLE(dfa);
u16 *check = CHECK_TABLE(dfa);
- unsigned int state = 1, pos;
+ unsigned int state = start, pos;
+
+ if (state == 0)
+ return 0;
/* current state is <state>, matching character *str */
if (dfa->tables[YYTD_ID_EC - 1]) {
@@ -244,5 +248,80 @@ unsigned int aa_dfa_match(struct aa_dfa
state = def[state];
}
}
- return ACCEPT_TABLE(dfa)[state];
+ return state;
+}
+
+/**
+ * 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
+ *
+ * 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.
+ */
+unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, unsigned int start)
+{
+ u16 *def = DEFAULT_TABLE(dfa);
+ u32 *base = BASE_TABLE(dfa);
+ u16 *next = NEXT_TABLE(dfa);
+ u16 *check = CHECK_TABLE(dfa);
+ unsigned int state = start, pos;
+
+ /* current state is <state>, matching character *str */
+ if (dfa->tables[YYTD_ID_EC - 1]) {
+ u8 *equiv = EQUIV_TABLE(dfa);
+ pos = base[state] + equiv[0];
+ if (check[pos] == state)
+ state = next[pos];
+ else
+ state = def[state];
+ } else {
+ pos = base[state] + 0;
+ if (check[pos] == state)
+ state = next[pos];
+ else
+ state = def[state];
+ }
+
+ return state;
+}
+
+/**
+ * aa_dfa_match - find accept perm for @str in @dfa
+ * @dfa: the dfa to match @str against
+ * @str: the string to match against the dfa
+ *
+ * aa_dfa_match will match @str and return the accept perms for the
+ * final state.
+ */
+unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str)
+{
+ return ACCEPT_TABLE(dfa)[aa_dfa_next_state(dfa, DFA_START, str)];
}
+
+/**
+ * aa_match_state - find accept perm and state for @str in @dfa
+ * @dfa: the dfa to match @str against
+ * @start: the state to start the match from
+ * @str: the string to match against the dfa
+ * @final: the state that the match finished in
+ *
+ * aa_match_state will match @str and return the accept perms, and @final
+ * state, the match occured in.
+ */
+unsigned int aa_match_state(struct aa_dfa *dfa, unsigned int start,
+ const char *str, unsigned int *final)
+{
+ unsigned int state;
+ if (dfa) {
+ state = aa_dfa_next_state(dfa, start, str);
+ if (final)
+ *final = state;
+ return ACCEPT_TABLE(dfa)[state];
+ }
+ if (final)
+ *final = 0;
+ return 0;
+}
+
--- a/security/apparmor/match.h
+++ b/security/apparmor/match.h
@@ -12,6 +12,8 @@
#ifndef __MATCH_H
#define __MATCH_H
+#define DFA_START 1
+
/**
* The format used for transition tables is based on the GNU flex table
* file format (--tables-file option; see Table File Format in the flex

View File

@@ -1,72 +0,0 @@
---
security/apparmor/apparmor.h | 43 ++++++++++++++++++++++++++++---------------
1 file changed, 28 insertions(+), 15 deletions(-)
--- a/security/apparmor/apparmor.h
+++ b/security/apparmor/apparmor.h
@@ -26,25 +26,33 @@
#define AA_MAY_LINK 0x0010
#define AA_MAY_LOCK 0x0020
#define AA_EXEC_MMAP 0x0040
-#define AA_EXEC_UNSAFE 0x0080
-#define AA_EXEC_MOD_0 0x0100
-#define AA_EXEC_MOD_1 0x0200
+#define AA_MAY_MOUNT 0x0080 /* no direct audit mapping */
+#define AA_EXEC_UNSAFE 0x0100
+#define AA_EXEC_MOD_0 0x0200
+#define AA_EXEC_MOD_1 0x0400
+#define AA_EXEC_MOD_2 0x0800
+#define AA_EXEC_MOD_3 0x1000
+#define AA_EXEC_MOD_4 0x2000
+
#define AA_BASE_PERMS (MAY_READ | MAY_WRITE | MAY_EXEC | \
MAY_APPEND | AA_MAY_LINK | \
AA_MAY_LOCK | AA_EXEC_MMAP | \
- AA_EXEC_UNSAFE | AA_EXEC_MOD_0 | \
- AA_EXEC_MOD_1)
-#define AA_LINK_SUBSET_TEST 0x0020
-
-#define AA_EXEC_UNCONFINED 0
-#define AA_EXEC_INHERIT AA_EXEC_MOD_0
-#define AA_EXEC_PROFILE AA_EXEC_MOD_1
-#define AA_EXEC_PIX (AA_EXEC_MOD_0 | AA_EXEC_MOD_1)
-
-#define AA_EXEC_MODIFIERS (AA_EXEC_MOD_0 | AA_EXEC_MOD_1)
+ AA_MAY_MOUNT | AA_EXEC_UNSAFE | \
+ AA_EXEC_MOD_0 | AA_EXEC_MOD_1 | \
+ AA_EXEC_MOD_2 | AA_EXEC_MOD_3 | \
+ AA_EXEC_MOD_4)
+
+#define AA_EXEC_UNCONFINED AA_EXEC_MOD_0
+#define AA_EXEC_INHERIT AA_EXEC_MOD_1
+#define AA_EXEC_PROFILE (AA_EXEC_MOD_0 | AA_EXEC_MOD_1)
+#define AA_EXEC_PIX AA_EXEC_MOD_2
+
+#define AA_EXEC_MODIFIERS (AA_EXEC_MOD_0 | AA_EXEC_MOD_1 | \
+ AA_EXEC_MOD_2 | AA_EXEC_MOD_3 | \
+ AA_EXEC_MOD_4)
#define AA_USER_SHIFT 0
-#define AA_OTHER_SHIFT 10
+#define AA_OTHER_SHIFT 14
#define AA_USER_PERMS (AA_BASE_PERMS << AA_USER_SHIFT)
#define AA_OTHER_PERMS (AA_BASE_PERMS << AA_OTHER_SHIFT)
@@ -68,11 +76,16 @@
#define AA_ALL_EXEC_MODS (AA_USER_EXEC_MODS | \
AA_OTHER_EXEC_MODS)
+/* overloaded permissions for link pairs */
+#define AA_LINK_SUBSET_TEST 0x0020
+
/* shared permissions that are not duplicated in user::other */
+#define AA_AUDIT_FIELD 0x10000000
#define AA_CHANGE_HAT 0x20000000
#define AA_CHANGE_PROFILE 0x40000000
-#define AA_SHARED_PERMS (AA_CHANGE_HAT | AA_CHANGE_PROFILE)
+#define AA_SHARED_PERMS (AA_CHANGE_HAT | AA_CHANGE_PROFILE | \
+ AA_AUDIT_FIELD)
#define AA_VALID_PERM_MASK (AA_FILE_PERMS | AA_SHARED_PERMS)

View File

@@ -1,130 +0,0 @@
From: Miklos Szeredi <mszeredi@suse.cz>
Add a new file operation: f_op->fgetattr(), that is invoked by
fstat(). Fall back to i_op->getattr() if it is not defined.
We need this because fstat() semantics can in some cases be better
implemented if the filesystem has the open file available.
Let's take the following example: we have a network filesystem, with
the server implemented as an unprivileged userspace process running on
a UNIX system (this is basically what sshfs does).
We want the filesystem to follow the familiar UNIX file semantics as
closely as possible. If for example we have this sequence of events,
we still would like fstat to work correctly:
1) file X is opened on client
2) file X is renamed to Y on server
3) fstat() is performed on open file descriptor on client
This is only possible if the filesystem server acutally uses fstat()
on a file descriptor obtained when the file was opened. Which means,
the filesystem client needs a way to get this information from the
VFS.
Even if we assume, that the remote filesystem never changes, it is
difficult to implement open-unlink-fstat semantics correctly in the
client, without having this information.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
---
fs/fuse/file.c | 13 +++++++++++++
fs/stat.c | 29 ++++++++++++++++++++++++++++-
include/linux/fs.h | 1 +
3 files changed, 42 insertions(+), 1 deletion(-)
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -871,6 +871,17 @@ static int fuse_file_flock(struct file *
return err;
}
+static int fuse_file_fgetattr(struct file *file, struct kstat *stat)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ struct fuse_conn *fc = get_fuse_conn(inode);
+
+ if (!fuse_allow_task(fc, current))
+ return -EACCES;
+
+ return fuse_update_attributes(inode, stat, file, NULL);
+}
+
static sector_t fuse_bmap(struct address_space *mapping, sector_t block)
{
struct inode *inode = mapping->host;
@@ -920,6 +931,7 @@ static const struct file_operations fuse
.fsync = fuse_fsync,
.lock = fuse_file_lock,
.flock = fuse_file_flock,
+ .fgetattr = fuse_file_fgetattr,
.splice_read = generic_file_splice_read,
};
@@ -933,6 +945,7 @@ static const struct file_operations fuse
.fsync = fuse_fsync,
.lock = fuse_file_lock,
.flock = fuse_file_flock,
+ .fgetattr = fuse_file_fgetattr,
/* no mmap and splice_read */
};
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -55,6 +55,33 @@ int vfs_getattr(struct vfsmount *mnt, st
EXPORT_SYMBOL(vfs_getattr);
+/*
+ * Perform getattr on an open file
+ *
+ * Fall back to i_op->getattr (or generic_fillattr) if the filesystem
+ * doesn't define an f_op->fgetattr operation.
+ */
+static int vfs_fgetattr(struct file *file, struct kstat *stat)
+{
+ struct vfsmount *mnt = file->f_path.mnt;
+ struct dentry *dentry = file->f_path.dentry;
+ struct inode *inode = dentry->d_inode;
+ int retval;
+
+ retval = security_inode_getattr(mnt, dentry);
+ if (retval)
+ return retval;
+
+ if (file->f_op && file->f_op->fgetattr) {
+ return file->f_op->fgetattr(file, stat);
+ } else if (inode->i_op->getattr) {
+ return inode->i_op->getattr(mnt, dentry, stat);
+ } else {
+ generic_fillattr(inode, stat);
+ return 0;
+ }
+}
+
int vfs_stat_fd(int dfd, char __user *name, struct kstat *stat)
{
struct nameidata nd;
@@ -101,7 +128,7 @@ int vfs_fstat(unsigned int fd, struct ks
int error = -EBADF;
if (f) {
- error = vfs_getattr(f->f_path.mnt, f->f_path.dentry, stat);
+ error = vfs_fgetattr(f, stat);
fput(f);
}
return error;
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1188,6 +1188,7 @@ struct file_operations {
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
+ int (*fgetattr)(struct file *, struct kstat *);
};
struct inode_operations {

View File

@@ -1,83 +0,0 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Enable LSM hooks to distinguish operations on file descriptors from operations on pathnames
Struct iattr already contains ia_file since commit cc4e69de from
Miklos (which is related to commit befc649c). Use this to pass
struct file down the setattr hooks. This allows LSMs to distinguish
operations on file descriptors from operations on paths.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
Cc: Miklos Szeredi <mszeredi@suse.cz>
---
fs/nfsd/vfs.c | 12 +++++++-----
fs/open.c | 4 +++-
2 files changed, 10 insertions(+), 6 deletions(-)
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -413,7 +413,7 @@ static ssize_t nfsd_getxattr(struct dent
{
ssize_t buflen;
- buflen = vfs_getxattr(dentry, mnt, key, NULL, 0);
+ buflen = vfs_getxattr(dentry, mnt, key, NULL, 0, NULL);
if (buflen <= 0)
return buflen;
@@ -421,7 +421,7 @@ static ssize_t nfsd_getxattr(struct dent
if (!*buf)
return -ENOMEM;
- return vfs_getxattr(dentry, mnt, key, *buf, buflen);
+ return vfs_getxattr(dentry, mnt, key, *buf, buflen, NULL);
}
#endif
@@ -447,7 +447,7 @@ set_nfsv4_acl_one(struct dentry *dentry,
goto out;
}
- error = vfs_setxattr(dentry, mnt, key, buf, len, 0);
+ error = vfs_setxattr(dentry, mnt, key, buf, len, 0, NULL);
out:
kfree(buf);
return error;
@@ -2051,12 +2051,14 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i
mnt = fhp->fh_export->ex_mnt;
if (size)
- error = vfs_setxattr(fhp->fh_dentry, mnt, name, value, size,0);
+ error = vfs_setxattr(fhp->fh_dentry, mnt, name, value, size, 0,
+ NULL);
else {
if (!S_ISDIR(inode->i_mode) && type == ACL_TYPE_DEFAULT)
error = 0;
else {
- error = vfs_removexattr(fhp->fh_dentry, mnt, name);
+ error = vfs_removexattr(fhp->fh_dentry, mnt, name,
+ NULL);
if (error == -ENODATA)
error = 0;
}
--- a/fs/open.c
+++ b/fs/open.c
@@ -581,7 +581,7 @@ asmlinkage long sys_fchmod(unsigned int
if (mode == (mode_t) -1)
mode = inode->i_mode;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
- newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+ newattrs.ia_valid = ATTR_MODE | ATTR_CTIME | ATTR_FILE;
err = fnotify_change(dentry, file->f_path.mnt, &newattrs, file);
mutex_unlock(&inode->i_mutex);
@@ -661,6 +661,8 @@ static int chown_common(struct dentry *
if (!S_ISDIR(inode->i_mode))
newattrs.ia_valid |=
ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
+ if (file)
+ newattrs.ia_valid |= ATTR_FILE;
mutex_lock(&inode->i_mutex);
error = fnotify_change(dentry, mnt, &newattrs, file);
mutex_unlock(&inode->i_mutex);

View File

@@ -1,31 +0,0 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Fix file_permission()
We cannot easily switch from file_permission() to vfs_permission()
everywhere, so fix file_permission() to not use a NULL nameidata
for the remaining users.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/namei.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -313,7 +313,13 @@ int vfs_permission(struct nameidata *nd,
*/
int file_permission(struct file *file, int mask)
{
- return permission(file->f_path.dentry->d_inode, mask, NULL);
+ struct nameidata nd;
+
+ nd.dentry = file->f_path.dentry;
+ nd.mnt = file->f_path.mnt;
+ nd.flags = LOOKUP_ACCESS;
+
+ return permission(nd.dentry->d_inode, mask, &nd);
}
/*

View File

@@ -1,17 +0,0 @@
---
security/apparmor/main.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -1239,9 +1239,7 @@ repeat:
return -ENOENT;
}
- if (PROFILE_COMPLAIN(profile) ||
- (ns == profile->ns &&
- (aa_match(profile->file_rules, name) & AA_CHANGE_PROFILE)))
+ if (PROFILE_COMPLAIN(profile))
error = do_change_profile(profile, ns, name, 0, 0, &sa);
else {
/* check for a rule with a namespace prepended */

View File

@@ -1,68 +0,0 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Distinguish between connected and disconnected paths in d_path()
Change d_path() so that it will never return a path starting with '/' if
the path doesn't lead up to the chroot directory. Also ensure that the
path returned never is the empty string: this would only occur with a lazily unmounted file system; return "." in that case instead.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
fs/dcache.c | 18 ++++--------------
fs/namespace.c | 3 ---
2 files changed, 4 insertions(+), 17 deletions(-)
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1829,8 +1829,11 @@ global_root:
buffer++;
buflen++;
}
- if (is_slash)
+ if (is_slash) {
+ if (*buffer == '\0')
+ *--buffer = '.';
goto out;
+ }
}
if (buflen < namelen)
goto Elong;
@@ -1843,18 +1846,6 @@ Elong:
goto out;
}
-static char *__connect_d_path(char *path, char *buffer)
-{
- if (!IS_ERR(path) && *path != '/') {
- /* Pretend that disconnected paths are hanging off the root. */
- if (path == buffer)
- path = ERR_PTR(-ENAMETOOLONG);
- else
- *--path = '/';
- }
- return path;
-}
-
/* write full pathname into buffer and return start of pathname */
char *d_path(struct dentry *dentry, struct vfsmount *vfsmnt, char *buf,
int buflen)
@@ -1868,7 +1859,6 @@ char *d_path(struct dentry *dentry, stru
root = dget(current->fs->root);
read_unlock(&current->fs->lock);
res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen, 0);
- res = __connect_d_path(res, buf);
dput(root);
mntput(rootmnt);
return res;
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1917,9 +1917,6 @@ char *d_namespace_path(struct dentry *de
res = __d_path(dentry, vfsmnt, root, nsrootmnt, buf, buflen, 1);
dput(root);
mntput(nsrootmnt);
- /* Prevent empty path for lazily unmounted filesystems. */
- if (!IS_ERR(res) && *res == '\0')
- *--res = '.';
return res;
}
EXPORT_SYMBOL(d_namespace_path);

View File

@@ -1,134 +0,0 @@
---
security/apparmor/match.c | 74 +++++++++++++++++++++++++++++++---------------
1 file changed, 51 insertions(+), 23 deletions(-)
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -213,17 +213,23 @@ void aa_match_free(struct aa_dfa *dfa)
}
/**
- * aa_dfa_next_state - traverse @dfa to find state @str stops at
+ * aa_dfa_next_state_len - traverse @dfa to find state @str stops at
* @dfa: the dfa to match @str against
* @start: the state of the dfa to start matching in
- * @str: the string to match against the dfa
+ * @str: the string of bytes to match against the dfa
+ * @len: length of the string of bytes to match
*
* aa_dfa_next_state will match @str against the dfa and return the state it
* finished matching in. The final state can be used to look up the accepting
* label, or as the start state of a continuing match.
+ *
+ * aa_dfa_next_state could be implement using this function by doing
+ * return aa_dfa_next_state_len(dfa, start, str, strlen(str));
+ * but that would require traversing the string twice and be slightly
+ * slower.
*/
-unsigned int aa_dfa_next_state(struct aa_dfa *dfa, unsigned int start,
- const char *str)
+unsigned int aa_dfa_next_state_len(struct aa_dfa *dfa, unsigned int start,
+ const char *str, int len)
{
u16 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
@@ -237,7 +243,7 @@ unsigned int aa_dfa_next_state(struct aa
/* current state is <state>, matching character *str */
if (dfa->tables[YYTD_ID_EC - 1]) {
u8 *equiv = EQUIV_TABLE(dfa);
- while (*str) {
+ for (; len; len--) {
pos = base[state] + equiv[(u8)*str++];
if (check[pos] == state)
state = next[pos];
@@ -245,7 +251,7 @@ unsigned int aa_dfa_next_state(struct aa
state = def[state];
}
} else {
- while (*str) {
+ for (; len; len--) {
pos = base[state] + (u8)*str++;
if (check[pos] == state)
state = next[pos];
@@ -257,15 +263,17 @@ unsigned int aa_dfa_next_state(struct aa
}
/**
- * aa_dfa_null_transition - step to next state after null character
- * @dfa: the dfa to match against
+ * aa_dfa_next_state - traverse @dfa to find state @str stops at
+ * @dfa: the dfa to match @str against
* @start: the state of the dfa to start matching in
+ * @str: the null terminated string of bytes to match against the dfa
*
- * 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.
+ * aa_dfa_next_state will match @str against the dfa and return the state it
+ * finished matching in. The final state can be used to look up the accepting
+ * label, or as the start state of a continuing match.
*/
-unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, unsigned int start)
+unsigned int aa_dfa_next_state(struct aa_dfa *dfa, unsigned int start,
+ const char *str)
{
u16 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
@@ -273,26 +281,46 @@ unsigned int aa_dfa_null_transition(stru
u16 *check = CHECK_TABLE(dfa);
unsigned int state = start, pos;
+ if (state == 0)
+ return 0;
+
/* current state is <state>, matching character *str */
if (dfa->tables[YYTD_ID_EC - 1]) {
u8 *equiv = EQUIV_TABLE(dfa);
- pos = base[state] + equiv[0];
- if (check[pos] == state)
- state = next[pos];
- else
- state = def[state];
+ while (*str) {
+ pos = base[state] + equiv[(u8)*str++];
+ if (check[pos] == state)
+ state = next[pos];
+ else
+ state = def[state];
+ }
} else {
- pos = base[state] + 0;
- if (check[pos] == state)
- state = next[pos];
- else
- state = def[state];
+ while (*str) {
+ pos = base[state] + (u8)*str++;
+ if (check[pos] == state)
+ state = next[pos];
+ else
+ state = def[state];
+ }
}
-
return state;
}
/**
+ * 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
+ *
+ * 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.
+ */
+unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, unsigned int start)
+{
+ return aa_dfa_next_state_len(dfa, start, "", 1);
+}
+
+/**
* aa_dfa_match - find accept perm for @str in @dfa
* @dfa: the dfa to match @str against
* @str: the string to match against the dfa

View File

@@ -1,37 +0,0 @@
---
fs/fuse/dir.c | 2 +-
fs/fuse/file.c | 2 +-
fs/fuse/fuse_i.h | 3 +++
3 files changed, 5 insertions(+), 2 deletions(-)
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1167,7 +1167,7 @@ static int fuse_setattr(struct dentry *e
return fuse_do_setattr(entry, attr, NULL);
}
-static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
+int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
struct kstat *stat)
{
struct inode *inode = entry->d_inode;
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -855,7 +855,7 @@ static int fuse_file_fgetattr(struct fil
if (!fuse_allow_task(fc, current))
return -EACCES;
- return fuse_update_attributes(inode, stat, file, NULL);
+ return fuse_getattr(file->f_vfsmnt, file->f_dentry, stat);
}
static sector_t fuse_bmap(struct address_space *mapping, sector_t block)
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -597,3 +597,6 @@ int fuse_valid_type(int m);
int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task);
u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id);
+
+int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
+ struct kstat *stat);

View File

@@ -1,30 +0,0 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Make getcwd() only return valid paths
Make getcwd() fail with -ENOENT if the current working directory is
disconnected: the process is not asking for some previous name of that
directory but for the current name; returning a path meaningless in the
context of that process makes no sense.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
fs/dcache.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1910,10 +1910,12 @@ asmlinkage long sys_getcwd(char __user *
read_unlock(&current->fs->lock);
cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE, 1);
- cwd = __connect_d_path(cwd, page);
error = PTR_ERR(cwd);
if (IS_ERR(cwd))
goto out;
+ error = -ENOENT;
+ if (*cwd != '/')
+ goto out;
error = -ERANGE;
len = PAGE_SIZE + page - cwd;

View File

@@ -1,76 +0,0 @@
---
security/apparmor/main.c | 37 ++++++++++++++++++++-----------------
1 file changed, 20 insertions(+), 17 deletions(-)
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -68,7 +68,7 @@ static int aa_link_denied(struct aa_prof
int *request_mask)
{
unsigned int state;
- int l_mode, t_mode, denied_mask = 0;
+ int l_mode, t_mode, l_subset, denied_mask = 0;
int link_mask = AA_MAY_LINK << target_mode;
*request_mask = link_mask;
@@ -83,31 +83,35 @@ static int aa_link_denied(struct aa_prof
if (!(mode & link_mask))
denied_mask |= link_mask;
+ /* return if link subset test is not required */
if (!(mode & (AA_LINK_SUBSET_TEST << target_mode)))
return denied_mask;
}
- /* do link perm subset test */
- t_mode = aa_match(profile->file_rules, target);
-
- /* Ignore valid-profile-transition flags. */
- l_mode &= ~AA_SHARED_PERMS;
- t_mode &= ~AA_SHARED_PERMS;
-
- *request_mask = l_mode | link_mask;
-
- /* Link always requires 'l' on the link for both parts of the pair.
+ /* Do link perm subset test
* If a subset test is required a permission subset test of the
* perms for the link are done against the user:group:other of the
* target's 'r', 'w', 'x', 'a', 'k', and 'm' permissions.
*
* If the link has 'x', an exact match of all the execute flags
- * ('i', 'u', 'p'). safe exec is treated as a subset of unsafe exec
+ * must match.
*/
-#define SUBSET_PERMS (AA_FILE_PERMS & ~AA_LINK_BITS)
denied_mask |= ~l_mode & link_mask;
- if (l_mode & SUBSET_PERMS) {
- denied_mask |= (l_mode & SUBSET_PERMS) & ~t_mode;
+
+ t_mode = aa_match(profile->file_rules, target);
+
+
+ /* For actual subset test ignore valid-profile-transition flags,
+ * and link bits
+ */
+ l_mode &= ~(AA_SHARED_PERMS | AA_LINK_BITS);
+ t_mode &= ~(AA_SHARED_PERMS | AA_LINK_BITS);
+ l_subset = l_mode & AA_FILE_PERMS;
+
+ *request_mask = l_mode | link_mask;
+
+ if (l_subset) {
+ denied_mask |= (l_subset) & ~t_mode;
if (denied_mask & AA_EXEC_BITS)
denied_mask |= l_mode & AA_ALL_EXEC_MODS;
else if (l_mode & AA_EXEC_BITS) {
@@ -126,9 +130,8 @@ static int aa_link_denied(struct aa_prof
denied_mask |= AA_OTHER_EXEC |
(l_mode & AA_OTHER_EXEC_MODS);
}
- } else
+ } else if (t_mode & AA_FILE_PERMS)
denied_mask |= t_mode | link_mask;
-#undef SUBSET_PERMS
return denied_mask;
}

View File

@@ -1,22 +0,0 @@
fix bug where the error code and mask are not being set correctly
when pathname lookup fails.
---
security/apparmor/main.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -191,8 +191,10 @@ static int aa_perm_dentry(struct aa_prof
*/
if (PTR_ERR(sa->name) == -ENOENT && (check & AA_CHECK_FD))
sa->denied_mask = 0;
- else
- sa->denied_mask = PTR_ERR(sa->name);
+ else {
+ sa->denied_mask = sa->requested_mask;
+ sa->error_code = PTR_ERR(sa->name);
+ }
sa->name = NULL;
} else
sa->denied_mask = aa_file_denied(profile, sa->name,

View File

@@ -1,26 +0,0 @@
---
security/apparmor/main.c | 2 +-
security/apparmor/module_interface.c | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -1119,7 +1119,7 @@ aa_x_to_profile(struct aa_profile *profi
default:
/* all other indexes are named transitions */
index = AA_EXEC_INDEX(xmode);
- if (index - 4 > profile->exec_table_size) {
+ if (index - 4 >= profile->exec_table_size) {
sa->info = "invalid named transition - exec failed";
sa->error_code = -EACCES;
new_profile = ERR_PTR(-EACCES);
--- a/security/apparmor/module_interface.c
+++ b/security/apparmor/module_interface.c
@@ -319,6 +319,7 @@ static int aa_unpack_exec_table(struct a
goto fail;
if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
goto fail;
+ profile->exec_table_size = size;
}
return 1;

View File

@@ -1,38 +0,0 @@
---
security/apparmor/lsm.c | 18 ------------------
1 file changed, 18 deletions(-)
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -717,22 +717,6 @@ static int apparmor_socket_shutdown(stru
return aa_revalidate_sk(sk, "socket_shutdown");
}
-static int apparmor_socket_getpeersec_stream(struct socket *sock,
- char __user *optval, int __user *optlen, unsigned len)
-{
- struct sock *sk = sock->sk;
-
- return aa_revalidate_sk(sk, "socket_getpeersec_stream");
-}
-
-static int apparmor_socket_getpeersec_dgram(struct socket *sock,
- struct sk_buff *skb, u32 *secid)
-{
- struct sock *sk = sock->sk;
-
- return aa_revalidate_sk(sk, "socket_getpeersec_dgram");
-}
-
static int apparmor_getprocattr(struct task_struct *task, char *name,
char **value)
{
@@ -882,8 +866,6 @@ struct security_operations apparmor_ops
.socket_getsockopt = apparmor_socket_getsockopt,
.socket_setsockopt = apparmor_socket_setsockopt,
.socket_shutdown = apparmor_socket_shutdown,
- .socket_getpeersec_stream = apparmor_socket_getpeersec_stream,
- .socket_getpeersec_dgram = apparmor_socket_getpeersec_dgram,
};
static void info_message(const char *str)

View File

@@ -1,92 +0,0 @@
---
security/apparmor/list.c | 2 +-
security/apparmor/main.c | 20 +++++++++++---------
security/apparmor/procattr.c | 13 +++++++------
3 files changed, 19 insertions(+), 16 deletions(-)
--- a/security/apparmor/list.c
+++ b/security/apparmor/list.c
@@ -142,7 +142,7 @@ static int seq_show_profile(struct seq_f
seq_printf(f, "%s (%s)\n", profile->name,
PROFILE_COMPLAIN(profile) ? "complain" : "enforce");
else
- seq_printf(f, "%s:%s (%s)\n", profile->ns->name, profile->name,
+ seq_printf(f, ":%s:%s (%s)\n", profile->ns->name, profile->name,
PROFILE_COMPLAIN(profile) ? "complain" : "enforce");
return 0;
}
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -251,8 +251,10 @@ static int aa_audit_base(struct aa_profi
audit_log_format(ab, " profile=");
audit_log_untrustedstring(ab, profile->name);
- audit_log_format(ab, " namespace=");
- audit_log_untrustedstring(ab, profile->ns->name);
+ if (profile->ns != default_namespace) {
+ audit_log_format(ab, " namespace=");
+ audit_log_untrustedstring(ab, profile->ns->name);
+ }
}
audit_log_end(ab);
@@ -1364,15 +1366,15 @@ repeat:
if (hat_name) {
char *name, *profile_name;
- /* Not Yet. This perm check is currently done by searching
- for the hat profile. When hat style profile names
- become more generic then this will be needed.
- if (!(aa_match(profile->file_rules, hat_name) &
- AA_CHANGE_PROFILE)) {
- error = -EACCES;
+ if (!PROFILE_COMPLAIN(profile) &&
+ !(aa_match(profile->file_rules, hat_name, NULL)
+ & AA_CHANGE_HAT)) {
+ /* missing permission to change_hat is treated the
+ * same as a failed hat search */
+ error = -ENOENT;
goto out;
}
- */
+
if (previous_profile)
profile_name = previous_profile->name;
else
--- a/security/apparmor/procattr.c
+++ b/security/apparmor/procattr.c
@@ -24,15 +24,16 @@ int aa_getprocattr(struct aa_profile *pr
mode_len = strlen(mode_str);
name_len = strlen(profile->name);
if (profile->ns != default_namespace)
- ns_len = strlen(profile->ns->name) + 1;
+ ns_len = strlen(profile->ns->name) + 2;
*len = mode_len + ns_len + name_len + 1;
str = kmalloc(*len, GFP_ATOMIC);
if (!str)
return -ENOMEM;
if (ns_len) {
- memcpy(str, profile->ns->name, ns_len - 1);
- str += ns_len - 1;
+ *str++ = ':';
+ memcpy(str, profile->ns->name, ns_len - 2);
+ str += ns_len - 2;
*str++ = ':';
}
memcpy(str, profile->name, name_len);
@@ -96,11 +97,11 @@ int aa_setprocattr_changeprofile(char *a
{
char *name = args, *ns_name = NULL;
- if (name[0] != '/') {
- char *split = strchr(name, ':');
+ if (name[0] == ':') {
+ char *split = strchr(&name[1], ':');
if (split) {
*split = 0;
- ns_name = name;
+ ns_name = &name[1];
name = split + 1;
}
}

View File

@@ -1,18 +0,0 @@
The old style of casting in the rcu_dereference fails to compile with
the newer rcu_dereference macro.
---
security/apparmor/inline.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/security/apparmor/inline.h
+++ b/security/apparmor/inline.h
@@ -19,7 +19,7 @@ static inline int mediated_filesystem(st
static inline struct aa_task_context *aa_task_context(struct task_struct *task)
{
- return rcu_dereference((struct aa_task_context *)task->security);
+ return (struct aa_task_context *) rcu_dereference(task->security);
}
/**

View File

@@ -1,15 +0,0 @@
---
security/apparmor/main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -338,7 +338,7 @@ static void aa_audit_file_mask(struct au
aa_audit_file_sub_mask(ab, other,
(mask & AA_OTHER_PERMS) >> AA_OTHER_SHIFT);
- audit_log_format(ab, " %s=\"%s::%s\"", name, user, other);
+ audit_log_format(ab, " %s=\"%s:%s\"", name, user, other);
}
static const char *address_families[] = {

View File

@@ -1,44 +0,0 @@
From: John Johansen <jjohansen@suse.de>
Subject: Call lsm hook before unhashing dentry in vfs_rmdir()
If we unhash the dentry before calling the security_inode_rmdir hook,
we cannot compute the file's pathname in the hook anymore. AppArmor
needs to know the filename in order to decide whether a file may be
deleted, though.
Signed-off-by: John Johansen <jjohansen@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
fs/namei.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2097,6 +2097,10 @@ int vfs_rmdir(struct inode *dir, struct
if (!dir->i_op || !dir->i_op->rmdir)
return -EPERM;
+ error = security_inode_rmdir(dir, dentry, mnt);
+ if (error)
+ return error;
+
DQUOT_INIT(dir);
mutex_lock(&dentry->d_inode->i_mutex);
@@ -2104,12 +2108,9 @@ int vfs_rmdir(struct inode *dir, struct
if (d_mountpoint(dentry))
error = -EBUSY;
else {
- error = security_inode_rmdir(dir, dentry, mnt);
- if (!error) {
- error = dir->i_op->rmdir(dir, dentry);
- if (!error)
- dentry->d_inode->i_flags |= S_DEAD;
- }
+ error = dir->i_op->rmdir(dir, dentry);
+ if (!error)
+ dentry->d_inode->i_flags |= S_DEAD;
}
mutex_unlock(&dentry->d_inode->i_mutex);
if (!error) {

View File

@@ -1,278 +0,0 @@
---
security/Makefile | 2
security/foobar/Makefile | 1
security/foobar/foobar.c | 257 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 260 insertions(+)
--- a/security/Makefile
+++ b/security/Makefile
@@ -16,3 +16,5 @@ obj-$(CONFIG_SECURITY) += security.o d
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o
obj-$(CONFIG_SECURITY_ROOTPLUG) += commoncap.o root_plug.o
+
+subdir-$(CONFIG_SECURITY) += foobar
--- /dev/null
+++ b/security/foobar/Makefile
@@ -0,0 +1 @@
+obj-m += foobar.o
--- /dev/null
+++ b/security/foobar/foobar.c
@@ -0,0 +1,257 @@
+#include <linux/security.h>
+#include <linux/module.h>
+#include <linux/namei.h>
+
+#define log_path(dentry, mnt, fmt...) \
+ __log_path(__FUNCTION__, dentry, mnt, NULL, ##fmt)
+
+#define log_path_inode(inode, fmt...) \
+ __log_path(__FUNCTION__, NULL, NULL, inode, ##fmt)
+
+static int show_traversals;
+module_param(show_traversals, bool, 0644);
+
+static void __log_path(const char *op, struct dentry *dentry,
+ struct vfsmount *mnt, struct inode *inode,
+ char *fmt, ...)
+{
+ va_list ap;
+ char *page = NULL, *name = "<?>";
+
+ page = (char *)__get_free_page(GFP_KERNEL);
+ if (!page)
+ name = "<ENOMEM>";
+ else if (mnt) {
+ name = d_path(dentry, mnt, page + 1, PAGE_SIZE - 2);
+ if (IS_ERR(name)) {
+ snprintf(page, PAGE_SIZE, "<%ld>", PTR_ERR(name));
+ name = page;
+ } else {
+ page[PAGE_SIZE - 2] = '"';
+ page[PAGE_SIZE - 1] = '\0';
+ *--name = '"';
+ }
+ } else if (dentry) {
+ snprintf(page, PAGE_SIZE, "<%s>", dentry->d_name.name);
+ name = page;
+ } else if (inode) {
+ snprintf(page, PAGE_SIZE, "<%s:%ld>",
+ inode->i_sb->s_type->name,
+ inode->i_ino);
+ name = page;
+ }
+
+ if (fmt) {
+ char *buffer = (char *)__get_free_page(GFP_KERNEL);
+
+ if (buffer) {
+ int n;
+
+ n = snprintf(buffer, PAGE_SIZE, KERN_INFO "%s(%s, ",
+ op, name);
+ va_start(ap, fmt);
+ n += vsnprintf(buffer + n, PAGE_SIZE - n, fmt, ap);
+ va_end(ap);
+ snprintf(buffer + n, PAGE_SIZE - n, ")\n");
+ printk("%s", buffer);
+
+ free_page((unsigned long)buffer);
+ } else
+ printk("%s: Out of memory\n", __FUNCTION__);
+ } else
+ printk(KERN_INFO "%s(%s)\n", op, name);
+
+ free_page((unsigned long)page);
+}
+
+static int foobar_inode_mkdir(struct inode *inode, struct dentry *dentry,
+ struct vfsmount *mnt, int mask)
+{
+ log_path(dentry, mnt, NULL);
+
+ return 0;
+}
+
+static int foobar_inode_rmdir(struct inode *inode, struct dentry *dentry,
+ struct vfsmount *mnt)
+{
+ log_path(dentry, mnt, NULL);
+
+ return 0;
+}
+
+static int foobar_inode_create(struct inode *inode, struct dentry *dentry,
+ struct vfsmount *mnt, int mask)
+{
+ log_path(dentry, mnt, NULL);
+
+ return 0;
+}
+
+static int foobar_inode_link(struct dentry *old_dentry,
+ struct vfsmount *old_mnt,
+ struct inode *inode,
+ struct dentry *new_dentry,
+ struct vfsmount *new_mnt)
+{
+ log_path(old_dentry, old_mnt, "<old>");
+ log_path(new_dentry, new_mnt, "<new>");
+
+ return 0;
+}
+
+static int foobar_inode_unlink(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt)
+{
+ log_path(dentry, mnt, NULL);
+
+ return 0;
+}
+
+static int foobar_inode_mknod(struct inode *inode, struct dentry *dentry,
+ struct vfsmount *mnt, int mode, dev_t dev)
+{
+ log_path(dentry, mnt, NULL);
+
+ return 0;
+}
+
+static int foobar_inode_rename(struct inode *old_inode,
+ struct dentry *old_dentry,
+ struct vfsmount *old_mnt,
+ struct inode *new_inode,
+ struct dentry *new_dentry,
+ struct vfsmount *new_mnt)
+{
+ log_path(old_dentry, old_mnt, "<old>");
+ log_path(new_dentry, new_mnt, "<new>");
+
+ return 0;
+}
+
+static int foobar_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
+ struct iattr *iattr)
+{
+ log_path(dentry, mnt, NULL);
+
+ return 0;
+}
+
+static int foobar_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+ char *name, void *value, size_t size,
+ int flags, struct file *file)
+{
+ log_path(dentry, mnt, NULL);
+
+ return 0;
+}
+
+static int foobar_inode_getxattr(struct dentry *dentry,
+ struct vfsmount *mnt, char *name, struct file *file)
+{
+ log_path(dentry, mnt, NULL);
+
+ return 0;
+}
+
+static int foobar_inode_listxattr(struct dentry *dentry,
+ struct vfsmount *mnt, struct file *file)
+{
+ log_path(dentry, mnt, NULL);
+
+ return 0;
+}
+
+static int foobar_inode_removexattr(struct dentry *dentry,
+ struct vfsmount *mnt, char *name, struct file *file)
+{
+ log_path(dentry, mnt, NULL);
+
+ return 0;
+}
+
+static int foobar_inode_symlink(struct inode *dir,
+ struct dentry *dentry, struct vfsmount *mnt,
+ const char *old_name)
+{
+ log_path(dentry, mnt, NULL);
+
+ return 0;
+}
+
+static int foobar_inode_permission(struct inode *inode, int mask,
+ struct nameidata *nd)
+{
+ if (nd && (nd->flags & (LOOKUP_PARENT | LOOKUP_CONTINUE)) &&
+ !show_traversals)
+ return 0;
+
+ if (nd) {
+ char *parent = "", *sep="", *cont = "";
+
+ if (nd->flags & LOOKUP_PARENT)
+ parent = "LOOKUP_PARENT";
+ if (nd->flags & LOOKUP_CONTINUE)
+ cont = "LOOKUP_CONTINUE";
+ if ((nd->flags & (LOOKUP_PARENT | LOOKUP_CONTINUE)) ==
+ (LOOKUP_PARENT | LOOKUP_CONTINUE))
+ sep = ", ";
+ else if (!(nd->flags & (LOOKUP_PARENT | LOOKUP_CONTINUE)))
+ sep = "0";
+
+ log_path(nd->dentry, nd->mnt, "%c%c%c%c, %s%s%s",
+ mask & MAY_READ ? 'r' : '-',
+ mask & MAY_WRITE ? 'w' : '-',
+ mask & MAY_EXEC ? 'x' : '-',
+ mask & MAY_APPEND ? 'a' : '-',
+ parent, sep, cont);
+ } else {
+ log_path_inode(inode, "%c%c%c%c, 0",
+ mask & MAY_READ ? 'r' : '-',
+ mask & MAY_WRITE ? 'w' : '-',
+ mask & MAY_EXEC ? 'x' : '-',
+ mask & MAY_APPEND ? 'a' : '-');
+ }
+
+ return 0;
+}
+
+struct security_operations foobar_ops = {
+ .inode_create = foobar_inode_create,
+ .inode_link = foobar_inode_link,
+ .inode_unlink = foobar_inode_unlink,
+ .inode_mkdir = foobar_inode_mkdir,
+ .inode_rmdir = foobar_inode_rmdir,
+ .inode_mknod = foobar_inode_mknod,
+ .inode_rename = foobar_inode_rename,
+ .inode_setattr = foobar_inode_setattr,
+ .inode_setxattr = foobar_inode_setxattr,
+ .inode_getxattr = foobar_inode_getxattr,
+ .inode_listxattr = foobar_inode_listxattr,
+ .inode_removexattr = foobar_inode_removexattr,
+ .inode_symlink = foobar_inode_symlink,
+ .inode_permission = foobar_inode_permission,
+};
+
+static int __init foobar_init(void)
+{
+ int error;
+
+ error = register_security(&foobar_ops);
+ if (error)
+ printk(KERN_ERR "Unable to load foobar lsm\n");
+
+ return error;
+}
+
+static void __exit foobar_exit(void)
+{
+ if (unregister_security(&foobar_ops))
+ printk(KERN_ERR "Unable to properly unregister foobar lsm\n");
+}
+
+module_init(foobar_init);
+module_exit(foobar_exit);
+
+MODULE_DESCRIPTION("lsm module logging to syslog");
+MODULE_LICENSE("GPL");

View File

@@ -1,27 +0,0 @@
---
fs/open.c | 3 +++
include/linux/fs.h | 1 +
2 files changed, 4 insertions(+)
--- a/fs/open.c
+++ b/fs/open.c
@@ -207,6 +207,9 @@ int do_truncate(struct dentry *dentry, s
newattrs.ia_size = length;
newattrs.ia_valid = ATTR_SIZE | time_attrs;
+ if (filp)
+ newattrs.ia_valid |= ATTR_FILE;
+
/* Remove suid/sgid on truncate too */
newattrs.ia_valid |= should_remove_suid(dentry);
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -329,6 +329,7 @@ typedef void (dio_iodone_t)(struct kiocb
#define ATTR_ATTR_FLAG 1024
#define ATTR_KILL_SUID 2048
#define ATTR_KILL_SGID 4096
+#define ATTR_FILE 8192
#define ATTR_KILL_PRIV 16384
#define ATTR_OPEN 32768 /* Truncating from open(O_TRUNC) */

View File

@@ -1,392 +0,0 @@
Subject: VFS: new fsetattr() file operation
From: Miklos Szeredi <mszeredi@suse.cz>
Add a new file operation: f_op->fsetattr(), that is invoked by
ftruncate, fchmod, fchown and utimensat. Fall back to i_op->setattr()
if it is not defined.
For the reasons why we need this, see patch adding fgetattr().
ftruncate() already passed the open file to the filesystem via the
ia_file member of struct iattr. However it is cleaner to have a
separate file operation for this, so remove ia_file, ATTR_FILE and
convert existing users: fuse and AFS.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> ---
Signed-off-by: John Johansen <jjohansen@suse.de> ---
---
fs/afs/dir.c | 1 +
fs/afs/file.c | 1 +
fs/afs/inode.c | 19 +++++++++++++++----
fs/afs/internal.h | 1 +
fs/attr.c | 18 ++++++++++++++----
fs/fuse/dir.c | 20 +++++++++-----------
fs/fuse/file.c | 7 +++++++
fs/fuse/fuse_i.h | 4 ++++
fs/open.c | 20 ++++++++------------
fs/utimes.c | 2 +-
include/linux/fs.h | 10 ++--------
11 files changed, 63 insertions(+), 40 deletions(-)
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -45,6 +45,7 @@ const struct file_operations afs_dir_fil
.release = afs_release,
.readdir = afs_readdir,
.lock = afs_lock,
+ .fsetattr = afs_fsetattr,
};
const struct inode_operations afs_dir_inode_operations = {
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -36,6 +36,7 @@ const struct file_operations afs_file_op
.fsync = afs_fsync,
.lock = afs_lock,
.flock = afs_flock,
+ .fsetattr = afs_fsetattr,
};
const struct inode_operations afs_file_inode_operations = {
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -360,7 +360,8 @@ void afs_clear_inode(struct inode *inode
/*
* set the attributes of an inode
*/
-int afs_setattr(struct dentry *dentry, struct iattr *attr)
+static int afs_do_setattr(struct dentry *dentry, struct iattr *attr,
+ struct file *file)
{
struct afs_vnode *vnode = AFS_FS_I(dentry->d_inode);
struct key *key;
@@ -382,8 +383,8 @@ int afs_setattr(struct dentry *dentry, s
afs_writeback_all(vnode);
}
- if (attr->ia_valid & ATTR_FILE) {
- key = attr->ia_file->private_data;
+ if (file) {
+ key = file->private_data;
} else {
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key)) {
@@ -393,10 +394,20 @@ int afs_setattr(struct dentry *dentry, s
}
ret = afs_vnode_setattr(vnode, key, attr);
- if (!(attr->ia_valid & ATTR_FILE))
+ if (!file)
key_put(key);
error:
_leave(" = %d", ret);
return ret;
}
+
+int afs_setattr(struct dentry *dentry, struct iattr *attr)
+{
+ return afs_do_setattr(dentry, attr, NULL);
+}
+
+int afs_fsetattr(struct file *file, struct iattr *attr)
+{
+ return afs_do_setattr(file->f_path.dentry, attr, file);
+}
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -550,6 +550,7 @@ extern void afs_zap_data(struct afs_vnod
extern int afs_validate(struct afs_vnode *, struct key *);
extern int afs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
extern int afs_setattr(struct dentry *, struct iattr *);
+extern int afs_fsetattr(struct file *, struct iattr *);
extern void afs_clear_inode(struct inode *);
/*
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -100,8 +100,8 @@ int inode_setattr(struct inode * inode,
}
EXPORT_SYMBOL(inode_setattr);
-int notify_change(struct dentry *dentry, struct vfsmount *mnt,
- struct iattr *attr)
+int fnotify_change(struct dentry *dentry, struct vfsmount *mnt,
+ struct iattr *attr, struct file *file)
{
struct inode *inode = dentry->d_inode;
mode_t mode = inode->i_mode;
@@ -160,8 +160,12 @@ int notify_change(struct dentry *dentry,
if (inode->i_op && inode->i_op->setattr) {
error = security_inode_setattr(dentry, mnt, attr);
- if (!error)
- error = inode->i_op->setattr(dentry, attr);
+ if (!error) {
+ if (file && file->f_op && file->f_op->fsetattr)
+ error = file->f_op->fsetattr(file, attr);
+ else
+ error = inode->i_op->setattr(dentry, attr);
+ }
} else {
error = inode_change_ok(inode, attr);
if (!error)
@@ -184,4 +188,10 @@ int notify_change(struct dentry *dentry,
return error;
}
+int notify_change(struct dentry *dentry, struct vfsmount *mnt,
+ struct iattr *attr)
+{
+ return fnotify_change(dentry, mnt, attr, NULL);
+}
+
EXPORT_SYMBOL(notify_change);
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1063,21 +1063,22 @@ static int fuse_dir_fsync(struct file *f
return file ? fuse_fsync_common(file, de, datasync, 1) : 0;
}
-static bool update_mtime(unsigned ivalid)
+static bool update_mtime(unsigned ivalid, bool have_file)
{
/* Always update if mtime is explicitly set */
if (ivalid & ATTR_MTIME_SET)
return true;
/* If it's an open(O_TRUNC) or an ftruncate(), don't update */
- if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE)))
+ if ((ivalid & ATTR_SIZE) && ((ivalid & ATTR_OPEN) || have_file))
return false;
/* In all other cases update */
return true;
}
-static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg)
+static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg,
+ bool have_file)
{
unsigned ivalid = iattr->ia_valid;
@@ -1096,7 +1097,7 @@ static void iattr_to_fattr(struct iattr
if (!(ivalid & ATTR_ATIME_SET))
arg->valid |= FATTR_ATIME_NOW;
}
- if ((ivalid & ATTR_MTIME) && update_mtime(ivalid)) {
+ if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, have_file)) {
arg->valid |= FATTR_MTIME;
arg->mtime = iattr->ia_mtime.tv_sec;
arg->mtimensec = iattr->ia_mtime.tv_nsec;
@@ -1113,8 +1114,8 @@ static void iattr_to_fattr(struct iattr
* vmtruncate() doesn't allow for this case, so do the rlimit checking
* and the actual truncation by hand.
*/
-static int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
- struct file *file)
+int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
+ struct file *file)
{
struct inode *inode = entry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
@@ -1152,7 +1153,7 @@ static int fuse_do_setattr(struct dentry
memset(&inarg, 0, sizeof(inarg));
memset(&outarg, 0, sizeof(outarg));
- iattr_to_fattr(attr, &inarg);
+ iattr_to_fattr(attr, &inarg, file != NULL);
if (file) {
struct fuse_file *ff = file->private_data;
inarg.valid |= FATTR_FH;
@@ -1194,10 +1195,7 @@ static int fuse_do_setattr(struct dentry
static int fuse_setattr(struct dentry *entry, struct iattr *attr)
{
- if (attr->ia_valid & ATTR_FILE)
- return fuse_do_setattr(entry, attr, attr->ia_file);
- else
- return fuse_do_setattr(entry, attr, NULL);
+ return fuse_do_setattr(entry, attr, NULL);
}
static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -907,6 +907,11 @@ static sector_t fuse_bmap(struct address
return err ? 0 : outarg.block;
}
+static int fuse_fsetattr(struct file *file, struct iattr *attr)
+{
+ return fuse_do_setattr(file->f_path.dentry, attr, file);
+}
+
static const struct file_operations fuse_file_operations = {
.llseek = generic_file_llseek,
.read = do_sync_read,
@@ -920,6 +925,7 @@ static const struct file_operations fuse
.fsync = fuse_fsync,
.lock = fuse_file_lock,
.flock = fuse_file_flock,
+ .fsetattr = fuse_fsetattr,
.splice_read = generic_file_splice_read,
};
@@ -933,6 +939,7 @@ static const struct file_operations fuse
.fsync = fuse_fsync,
.lock = fuse_file_lock,
.flock = fuse_file_flock,
+ .fsetattr = fuse_fsetattr,
/* no mmap and splice_read */
};
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -505,6 +505,10 @@ void fuse_change_attributes(struct inode
*/
int fuse_dev_init(void);
+
+int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
+ struct file *file);
+
/**
* Cleanup the client device
*/
--- a/fs/open.c
+++ b/fs/open.c
@@ -206,16 +206,12 @@ int do_truncate(struct dentry *dentry, s
newattrs.ia_size = length;
newattrs.ia_valid = ATTR_SIZE | time_attrs;
- if (filp) {
- newattrs.ia_file = filp;
- newattrs.ia_valid |= ATTR_FILE;
- }
/* Remove suid/sgid on truncate too */
newattrs.ia_valid |= should_remove_suid(dentry);
mutex_lock(&dentry->d_inode->i_mutex);
- err = notify_change(dentry, mnt, &newattrs);
+ err = fnotify_change(dentry, mnt, &newattrs, filp);
mutex_unlock(&dentry->d_inode->i_mutex);
return err;
}
@@ -583,7 +579,7 @@ asmlinkage long sys_fchmod(unsigned int
mode = inode->i_mode;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
- err = notify_change(dentry, file->f_path.mnt, &newattrs);
+ err = fnotify_change(dentry, file->f_path.mnt, &newattrs, file);
mutex_unlock(&inode->i_mutex);
out_putf:
@@ -633,7 +629,7 @@ asmlinkage long sys_chmod(const char __u
}
static int chown_common(struct dentry * dentry, struct vfsmount *mnt,
- uid_t user, gid_t group)
+ uid_t user, gid_t group, struct file *file)
{
struct inode * inode;
int error;
@@ -663,7 +659,7 @@ static int chown_common(struct dentry *
newattrs.ia_valid |=
ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
mutex_lock(&inode->i_mutex);
- error = notify_change(dentry, mnt, &newattrs);
+ error = fnotify_change(dentry, mnt, &newattrs, file);
mutex_unlock(&inode->i_mutex);
out:
return error;
@@ -677,7 +673,7 @@ asmlinkage long sys_chown(const char __u
error = user_path_walk(filename, &nd);
if (error)
goto out;
- error = chown_common(nd.dentry, nd.mnt, user, group);
+ error = chown_common(nd.dentry, nd.mnt, user, group, NULL);
path_release(&nd);
out:
return error;
@@ -697,7 +693,7 @@ asmlinkage long sys_fchownat(int dfd, co
error = __user_walk_fd(dfd, filename, follow, &nd);
if (error)
goto out;
- error = chown_common(nd.dentry, nd.mnt, user, group);
+ error = chown_common(nd.dentry, nd.mnt, user, group, NULL);
path_release(&nd);
out:
return error;
@@ -711,7 +707,7 @@ asmlinkage long sys_lchown(const char __
error = user_path_walk_link(filename, &nd);
if (error)
goto out;
- error = chown_common(nd.dentry, nd.mnt, user, group);
+ error = chown_common(nd.dentry, nd.mnt, user, group, NULL);
path_release(&nd);
out:
return error;
@@ -730,7 +726,7 @@ asmlinkage long sys_fchown(unsigned int
dentry = file->f_path.dentry;
audit_inode(NULL, dentry);
- error = chown_common(dentry, file->f_path.mnt, user, group);
+ error = chown_common(dentry, file->f_path.mnt, user, group, file);
fput(file);
out:
return error;
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -132,7 +132,7 @@ long do_utimes(int dfd, char __user *fil
}
}
mutex_lock(&inode->i_mutex);
- error = notify_change(path.dentry, path.mnt, &newattrs);
+ error = fnotify_change(path.dentry, path.mnt, &newattrs, f);
mutex_unlock(&inode->i_mutex);
dput_and_out:
if (f)
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -329,7 +329,6 @@ typedef void (dio_iodone_t)(struct kiocb
#define ATTR_ATTR_FLAG 1024
#define ATTR_KILL_SUID 2048
#define ATTR_KILL_SGID 4096
-#define ATTR_FILE 8192
#define ATTR_KILL_PRIV 16384
#define ATTR_OPEN 32768 /* Truncating from open(O_TRUNC) */
@@ -351,13 +350,6 @@ struct iattr {
struct timespec ia_atime;
struct timespec ia_mtime;
struct timespec ia_ctime;
-
- /*
- * Not an attribute, but an auxilary info for filesystems wanting to
- * implement an ftruncate() like method. NOTE: filesystem should
- * check for (ia_valid & ATTR_FILE), and not for (ia_file != NULL).
- */
- struct file *ia_file;
};
/*
@@ -1188,6 +1180,7 @@ struct file_operations {
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
+ int (*fsetattr)(struct file *, struct iattr *);
};
struct inode_operations {
@@ -1694,6 +1687,7 @@ extern int do_remount_sb(struct super_bl
extern sector_t bmap(struct inode *, sector_t);
#endif
extern int notify_change(struct dentry *, struct vfsmount *, struct iattr *);
+extern int fnotify_change(struct dentry *, struct vfsmount *, struct iattr *, struct file *);
extern int permission(struct inode *, int, struct nameidata *);
extern int generic_permission(struct inode *, int,
int (*check_acl)(struct inode *, int));

View File

@@ -1,39 +0,0 @@
---
security/apparmor/apparmor.h | 5 +++--
security/apparmor/main.c | 9 +++++++++
2 files changed, 12 insertions(+), 2 deletions(-)
--- a/security/apparmor/apparmor.h
+++ b/security/apparmor/apparmor.h
@@ -68,10 +68,11 @@
#define AA_ALL_EXEC_MODS (AA_USER_EXEC_MODS | \
AA_OTHER_EXEC_MODS)
-/* shared permissions that are not duplicated in user:group:other */
+/* shared permissions that are not duplicated in user::other */
+#define AA_CHANGE_HAT 0x20000000
#define AA_CHANGE_PROFILE 0x40000000
-#define AA_SHARED_PERMS (AA_CHANGE_PROFILE)
+#define AA_SHARED_PERMS (AA_CHANGE_HAT | AA_CHANGE_PROFILE)
#define AA_VALID_PERM_MASK (AA_FILE_PERMS | AA_SHARED_PERMS)
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -1300,6 +1300,15 @@ repeat:
if (hat_name) {
char *name, *profile_name;
+ /* Not Yet. This perm check is currently done by searching
+ for the hat profile. When hat style profile names
+ become more generic then this will be needed.
+ if (!(aa_match(profile->file_rules, hat_name) &
+ AA_CHANGE_PROFILE)) {
+ error = -EACCES;
+ goto out;
+ }
+ */
if (previous_profile)
profile_name = previous_profile->name;
else

View File

@@ -1,86 +0,0 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Remove redundant may_create() argument
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
fs/namei.c | 22 ++++++++++------------
1 file changed, 10 insertions(+), 12 deletions(-)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1462,16 +1462,14 @@ static int may_delete(struct inode *dir,
* 3. We should have write and exec permissions on dir
* 4. We can't do it if dir is immutable (done in permission())
*/
-static inline int may_create(struct inode *dir, struct dentry *child,
- struct nameidata2 *nd)
+static inline int may_create(struct nameidata2 *nd, struct dentry *child)
{
if (child->d_inode)
return -EEXIST;
- if (IS_DEADDIR(dir))
+ if (IS_DEADDIR(nd->dentry->d_inode))
return -ENOENT;
- if (nd)
- nd->flags |= LOOKUP_CONTINUE;
- return permission(dir,MAY_WRITE | MAY_EXEC, nd);
+ nd->flags |= LOOKUP_CONTINUE;
+ return permission(nd->dentry->d_inode, MAY_WRITE | MAY_EXEC, nd);
}
/*
@@ -1537,7 +1535,7 @@ void unlock_rename(struct dentry *p1, st
int vfs_create(struct nameidata2 *nd, struct dentry *dentry, int mode)
{
struct inode *dir = nd->dentry->d_inode;
- int error = may_create(dir, dentry, nd);
+ int error = may_create(nd, dentry);
if (error)
return error;
@@ -1883,7 +1881,7 @@ EXPORT_SYMBOL_GPL(lookup_create);
int vfs_mknod(struct nameidata2 *nd, struct dentry *dentry, int mode, dev_t dev)
{
struct inode *dir = nd->dentry->d_inode;
- int error = may_create(dir, dentry, nd);
+ int error = may_create(nd, dentry);
if (error)
return error;
@@ -1963,7 +1961,7 @@ asmlinkage long sys_mknod(const char __u
int vfs_mkdir(struct nameidata2 *nd, struct dentry *dentry, int mode)
{
struct inode *dir = nd->dentry->d_inode;
- int error = may_create(dir, dentry, nd);
+ int error = may_create(nd, dentry);
if (error)
return error;
@@ -2232,7 +2230,7 @@ int vfs_symlink(struct nameidata2 *nd, s
const char *oldname, int mode)
{
struct inode *dir = nd->dentry->d_inode;
- int error = may_create(dir, dentry, nd);
+ int error = may_create(nd, dentry);
if (error)
return error;
@@ -2304,7 +2302,7 @@ int vfs_link(struct nameidata2 *old_nd,
if (!inode)
return -ENOENT;
- error = may_create(dir, new_dentry, new_parent);
+ error = may_create(new_parent, new_dentry);
if (error)
return error;
@@ -2522,7 +2520,7 @@ int vfs_rename(struct nameidata2 *old_nd
return error;
if (!new_dentry->d_inode)
- error = may_create(new_dir, new_dentry, new_nd);
+ error = may_create(new_nd, new_dentry);
else
error = may_delete(new_dir, new_dentry, is_dir);
if (error)

View File

@@ -1,74 +0,0 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Pass nameidata2 to may_delete()
Pass the nameidata through to may_delete(), and pass it on to the
permission() call.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
fs/namei.c | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1418,8 +1418,9 @@ static inline int check_sticky(struct in
* 10. We don't allow removal of NFS sillyrenamed files; it's handled by
* nfs_async_unlink().
*/
-static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
+static int may_delete(struct nameidata2 *nd, struct dentry *victim, int isdir)
{
+ struct inode *dir = nd->dentry->d_inode;
int error;
if (!victim->d_inode)
@@ -1428,11 +1429,8 @@ static int may_delete(struct inode *dir,
BUG_ON(victim->d_parent->d_inode != dir);
audit_inode_child(victim->d_name.name, victim->d_inode, dir);
-#if 0
- if (nd)
- nd->flags |= LOOKUP_CONTINUE;
-#endif
- error = permission(dir,MAY_WRITE | MAY_EXEC, NULL);
+ nd->flags |= LOOKUP_CONTINUE;
+ error = permission(dir,MAY_WRITE | MAY_EXEC, nd);
if (error)
return error;
if (IS_APPEND(dir))
@@ -2049,7 +2047,7 @@ void dentry_unhash(struct dentry *dentry
int vfs_rmdir(struct nameidata2 *nd, struct dentry *dentry)
{
struct inode *dir = nd->dentry->d_inode;
- int error = may_delete(dir, dentry, 1);
+ int error = may_delete(nd, dentry, 1);
if (error)
return error;
@@ -2131,7 +2129,7 @@ asmlinkage long sys_rmdir(const char __u
int vfs_unlink(struct nameidata2 *nd, struct dentry *dentry)
{
struct inode *dir = nd->dentry->d_inode;
- int error = may_delete(dir, dentry, 0);
+ int error = may_delete(nd, dentry, 0);
if (error)
return error;
@@ -2517,14 +2515,14 @@ int vfs_rename(struct nameidata2 *old_nd
if (old_dentry->d_inode == new_dentry->d_inode)
return 0;
- error = may_delete(old_dir, old_dentry, is_dir);
+ error = may_delete(old_nd, old_dentry, is_dir);
if (error)
return error;
if (!new_dentry->d_inode)
error = may_create(new_nd, new_dentry);
else
- error = may_delete(new_dir, new_dentry, is_dir);
+ error = may_delete(new_nd, new_dentry, is_dir);
if (error)
return error;

View File

@@ -1,60 +0,0 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Make d_path() consistent across mount operations
The path that __d_path() computes can become slightly inconsistent when it
races with mount operations: it grabs the vfsmount_lock when traversing mount
points but immediately drops it again, only to re-grab it when it reaches the
next mount point. The result is that the filename computed is not always
consisent, and the file may never have had that name. (This is unlikely, but
still possible.)
Fix this by grabbing the vfsmount_lock when the first mount point is reached,
and holding onto it until the d_cache lookup is completed.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/dcache.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1786,7 +1786,7 @@ static char *__d_path(struct dentry *den
struct dentry *root, struct vfsmount *rootmnt,
char *buffer, int buflen, int fail_deleted)
{
- int namelen, is_slash;
+ int namelen, is_slash, vfsmount_locked = 0;
if (buflen < 2)
return ERR_PTR(-ENAMETOOLONG);
@@ -1809,14 +1809,14 @@ static char *__d_path(struct dentry *den
struct dentry * parent;
if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
- spin_lock(&vfsmount_lock);
- if (vfsmnt->mnt_parent == vfsmnt) {
- spin_unlock(&vfsmount_lock);
- goto global_root;
+ if (!vfsmount_locked) {
+ spin_lock(&vfsmount_lock);
+ vfsmount_locked = 1;
}
+ if (vfsmnt->mnt_parent == vfsmnt)
+ goto global_root;
dentry = vfsmnt->mnt_mountpoint;
vfsmnt = vfsmnt->mnt_parent;
- spin_unlock(&vfsmount_lock);
continue;
}
parent = dentry->d_parent;
@@ -1835,6 +1835,8 @@ static char *__d_path(struct dentry *den
*--buffer = '/';
out:
+ if (vfsmount_locked)
+ spin_unlock(&vfsmount_lock);
spin_unlock(&dcache_lock);
return buffer;

View File

@@ -1,47 +0,0 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: nfs NULL nameidata check?
nfs_lookup() checks for NULL nameidata in one place, but not in another. In
nfs_sillyrename() it calls lookup_one_len() -> __lookup_hash(), which passes
in a NULL nameidata to nfs_lookup(). Unless I'm overlooking something,
fs/nfs/dir.c:923 will dereference this NULL pointer if the sillyrenamed file
exists?
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
fs/nfs/dir.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -896,15 +896,15 @@ int nfs_is_exclusive_create(struct inode
return (nd->intent.open.flags & O_EXCL) != 0;
}
-static inline int nfs_reval_fsid(struct vfsmount *mnt, struct inode *dir,
+static inline int nfs_reval_fsid(struct nameidata *nd, struct inode *dir,
struct nfs_fh *fh, struct nfs_fattr *fattr)
{
struct nfs_server *server = NFS_SERVER(dir);
- if (!nfs_fsid_equal(&server->fsid, &fattr->fsid))
- /* Revalidate fsid on root dir */
- return __nfs_revalidate_inode(server, mnt->mnt_root->d_inode);
- return 0;
+ if (nd == NULL || nfs_fsid_equal(&server->fsid, &fattr->fsid))
+ return 0;
+ /* Revalidate fsid on root dir */
+ return __nfs_revalidate_inode(server, nd->mnt->mnt_root->d_inode);
}
static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata2 *nd)
@@ -945,7 +945,7 @@ static struct dentry *nfs_lookup(struct
res = ERR_PTR(error);
goto out_unlock;
}
- error = nfs_reval_fsid(nd->mnt, dir, &fhandle, &fattr);
+ error = nfs_reval_fsid(nd, dir, &fhandle, &fattr);
if (error < 0) {
res = ERR_PTR(error);
goto out_unlock;

View File

@@ -1,45 +0,0 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Pass nameidata2 to permission() from nfsd_permission()
Construct a nameidata object and pass it down to permission(), so
that we can do the proper mount flag checks there.
Note that confining nfsd with AppArmor makes no sense, and so this
patch is not necessary for AppArmor alone.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/nfsd/vfs.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1804,6 +1804,7 @@ nfsd_statfs(struct svc_rqst *rqstp, stru
__be32
nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc)
{
+ struct nameidata2 nd;
struct inode *inode = dentry->d_inode;
int err;
@@ -1869,12 +1870,16 @@ nfsd_permission(struct svc_export *exp,
inode->i_uid == current->fsuid)
return 0;
- err = permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC), NULL);
+ nd.dentry = dentry;
+ nd.mnt = exp->ex_mnt;
+ nd.flags = LOOKUP_ACCESS;
+
+ err = permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC), &nd);
/* Allow read access to binaries even when mode 111 */
if (err == -EACCES && S_ISREG(inode->i_mode) &&
acc == (MAY_READ | MAY_OWNER_OVERRIDE))
- err = permission(inode, MAY_EXEC, NULL);
+ err = permission(inode, MAY_EXEC, &nd);
return err? nfserrno(err) : 0;
}

View File

@@ -1,139 +0,0 @@
---
security/apparmor/apparmor.h | 21 ++++++++++-----------
security/apparmor/main.c | 40 ++++++++++++----------------------------
security/apparmor/match.c | 9 +++++++--
3 files changed, 29 insertions(+), 41 deletions(-)
--- a/security/apparmor/apparmor.h
+++ b/security/apparmor/apparmor.h
@@ -42,15 +42,18 @@
AA_EXEC_MOD_2 | AA_EXEC_MOD_3 | \
AA_EXEC_MOD_4)
+#define AA_EXEC_MODIFIERS (AA_EXEC_MOD_0 | AA_EXEC_MOD_1 | \
+ AA_EXEC_MOD_2 | AA_EXEC_MOD_3 | \
+ AA_EXEC_MOD_4)
+
+#define AA_EXEC_TYPE (MAY_EXEC | AA_EXEC_UNSAFE | \
+ AA_EXEC_MODIFIERS)
+
#define AA_EXEC_UNCONFINED AA_EXEC_MOD_0
#define AA_EXEC_INHERIT AA_EXEC_MOD_1
#define AA_EXEC_PROFILE (AA_EXEC_MOD_0 | AA_EXEC_MOD_1)
#define AA_EXEC_PIX AA_EXEC_MOD_2
-#define AA_EXEC_MODIFIERS (AA_EXEC_MOD_0 | AA_EXEC_MOD_1 | \
- AA_EXEC_MOD_2 | AA_EXEC_MOD_3 | \
- AA_EXEC_MOD_4)
-
#define AA_USER_SHIFT 0
#define AA_OTHER_SHIFT 14
@@ -65,16 +68,12 @@
#define AA_USER_EXEC (MAY_EXEC << AA_USER_SHIFT)
#define AA_OTHER_EXEC (MAY_EXEC << AA_OTHER_SHIFT)
-#define AA_USER_EXEC_MODS (AA_EXEC_MODIFIERS << AA_USER_SHIFT)
-#define AA_OTHER_EXEC_MODS (AA_EXEC_MODIFIERS << AA_OTHER_SHIFT)
-
-#define AA_USER_EXEC_UNSAFE (AA_EXEC_UNSAFE << AA_USER_SHIFT)
-#define AA_OTHER_EXEC_UNSAFE (AA_EXEC_UNSAFE << AA_OTHER_SHIFT)
+#define AA_USER_EXEC_TYPE (AA_EXEC_TYPE << AA_USER_SHIFT)
+#define AA_OTHER_EXEC_TYPE (AA_EXEC_TYPE << AA_OTHER_SHIFT)
#define AA_EXEC_BITS (AA_USER_EXEC | AA_OTHER_EXEC)
-#define AA_ALL_EXEC_MODS (AA_USER_EXEC_MODS | \
- AA_OTHER_EXEC_MODS)
+#define ALL_AA_EXEC_TYPE (AA_USER_EXEC_TYPE | AA_OTHER_EXEC_TYPE)
/* overloaded permissions for link pairs */
#define AA_LINK_SUBSET_TEST 0x0020
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -68,7 +68,7 @@ static int aa_link_denied(struct aa_prof
int *request_mask)
{
unsigned int state;
- int l_mode, t_mode, l_subset, denied_mask = 0;
+ int l_mode, t_mode, denied_mask = 0;
int link_mask = AA_MAY_LINK << target_mode;
*request_mask = link_mask;
@@ -90,7 +90,7 @@ static int aa_link_denied(struct aa_prof
/* Do link perm subset test
* If a subset test is required a permission subset test of the
- * perms for the link are done against the user:group:other of the
+ * perms for the link are done against the user::other of the
* target's 'r', 'w', 'x', 'a', 'k', and 'm' permissions.
*
* If the link has 'x', an exact match of all the execute flags
@@ -100,38 +100,22 @@ static int aa_link_denied(struct aa_prof
t_mode = aa_match(profile->file_rules, target);
-
/* For actual subset test ignore valid-profile-transition flags,
* and link bits
*/
- l_mode &= ~(AA_SHARED_PERMS | AA_LINK_BITS);
- t_mode &= ~(AA_SHARED_PERMS | AA_LINK_BITS);
- l_subset = l_mode & AA_FILE_PERMS;
+ l_mode &= AA_FILE_PERMS & ~AA_LINK_BITS;
+ t_mode &= AA_FILE_PERMS & ~AA_LINK_BITS;
*request_mask = l_mode | link_mask;
- if (l_subset) {
- denied_mask |= (l_subset) & ~t_mode;
- if (denied_mask & AA_EXEC_BITS)
- denied_mask |= l_mode & AA_ALL_EXEC_MODS;
- else if (l_mode & AA_EXEC_BITS) {
- if (!(l_mode & AA_USER_EXEC_UNSAFE))
- l_mode |= t_mode & AA_USER_EXEC_UNSAFE;
- if (l_mode & AA_USER_EXEC &&
- (l_mode & AA_USER_EXEC_MODS) !=
- (t_mode & AA_USER_EXEC_MODS))
- denied_mask |= AA_USER_EXEC |
- (l_mode & AA_USER_EXEC_MODS);
- if (!(l_mode & AA_OTHER_EXEC_UNSAFE))
- l_mode |= t_mode & AA_OTHER_EXEC_UNSAFE;
- if (l_mode & AA_OTHER_EXEC &&
- (l_mode & AA_OTHER_EXEC_MODS) !=
- (t_mode & AA_OTHER_EXEC_MODS))
- denied_mask |= AA_OTHER_EXEC |
- (l_mode & AA_OTHER_EXEC_MODS);
- }
- } else if (t_mode & AA_FILE_PERMS)
- denied_mask |= t_mode | link_mask;
+ if (l_mode) {
+ denied_mask |= l_mode & ~t_mode;
+ if ((l_mode & AA_EXEC_BITS) &&
+ (l_mode & ALL_AA_EXEC_TYPE) !=
+ (t_mode & ALL_AA_EXEC_TYPE))
+ denied_mask = (denied_mask & ~ALL_AA_EXEC_TYPE) |
+ (l_mode & ALL_AA_EXEC_TYPE);
+ }
return denied_mask;
}
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -175,9 +175,14 @@ int verify_dfa(struct aa_dfa *dfa)
for (i = 0; i < state_count; i++) {
int mode = ACCEPT_TABLE(dfa)[i];
- if (mode & ~AA_VALID_PERM_MASK) {
+ if (mode & ~AA_VALID_PERM_MASK)
+ goto out;
+
+ /* if any exec modifier is set MAY_EXEC must be set */
+ if ((mode & AA_USER_EXEC_TYPE) && !(mode & AA_USER_EXEC))
+ goto out;
+ if ((mode & AA_OTHER_EXEC_TYPE) && !(mode & AA_OTHER_EXEC))
goto out;
- }
}
error = 0;

View File

@@ -1,3 +0,0 @@
obj-$(CONFIG_SECURITY_FOOBAR) += foobar.o
foobar-y := foobar-lsm.o

View File

@@ -1 +0,0 @@
obj-m += foobar.o

View File

@@ -1,197 +0,0 @@
#include <linux/security.h>
#include <linux/module.h>
#include <linux/namei.h>
static void log_path(char *op, struct dentry *dentry, struct vfsmount *mnt)
{
char *page, *name;
if (!mnt) {
printk(KERN_INFO "foobar(%s): %p NULL vfsmnt\n", op, dentry);
return;
}
page = (char *)__get_free_page(GFP_KERNEL);
if (!page) {
printk(KERN_ERR "foobar(%s): Unable to get page for path %p/%p\n",
op, mnt, dentry);
goto out;
}
name=d_path(dentry, mnt, page, PAGE_SIZE);
if (IS_ERR(name)){
printk(KERN_ERR "foobar(%s): Error path %p/%p overflowed buffer\n",
op, mnt, dentry);
goto out;
}
printk(KERN_INFO "foobar(%s): %p/%p->'%s'\n",
op, mnt, dentry, name);
out:
if (page)
free_page((unsigned long)page);
}
static int foobar_inode_mkdir(struct inode *inode, struct dentry *dentry,
struct vfsmount *mnt, int mask)
{
log_path("inode_mkdir", dentry, mnt);
return 0;
}
static int foobar_inode_rmdir(struct inode *inode, struct dentry *dentry,
struct vfsmount *mnt)
{
log_path("inode_rmdir", dentry, mnt);
return 0;
}
static int foobar_inode_create(struct inode *inode, struct dentry *dentry,
struct vfsmount *mnt, int mask)
{
log_path("inode_create", dentry, mnt);
return 0;
}
static int foobar_inode_link(struct dentry *old_dentry,
struct vfsmount *old_mnt,
struct inode *inode,
struct dentry *new_dentry,
struct vfsmount *new_mnt)
{
log_path("inode_link (old)", old_dentry, old_mnt);
log_path("inode_link (new)", new_dentry, new_mnt);
return 0;
}
static int foobar_inode_unlink(struct inode *dir, struct dentry *dentry,
struct vfsmount *mnt)
{
log_path("inode_unlink", dentry, mnt);
return 0;
}
static int foobar_inode_mknod(struct inode *inode, struct dentry *dentry,
struct vfsmount *mnt, int mode, dev_t dev)
{
log_path("inode_mknod", dentry, mnt);
return 0;
}
static int foobar_inode_rename(struct inode *old_inode,
struct dentry *old_dentry,
struct vfsmount *old_mnt,
struct inode *new_inode,
struct dentry *new_dentry,
struct vfsmount *new_mnt)
{
log_path("inode_rename (old)", old_dentry, old_mnt);
log_path("inode_rename (new)", new_dentry, new_mnt);
return 0;
}
static int foobar_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
struct iattr *iattr)
{
log_path("inode_setattr", dentry, mnt);
return 0;
}
static int foobar_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
char *name, void *value, size_t size,
int flags)
{
log_path("inode_setxattr", dentry, mnt);
return 0;
}
static int foobar_inode_getxattr(struct dentry *dentry,
struct vfsmount *mnt, char *name)
{
log_path("inode_getxattr", dentry, mnt);
return 0;
}
static int foobar_inode_listxattr(struct dentry *dentry,
struct vfsmount *mnt)
{
log_path("inode_listxattr", dentry, mnt);
return 0;
}
static int foobar_inode_removexattr(struct dentry *dentry,
struct vfsmount *mnt, char *name)
{
log_path("inode_removexattr", dentry, mnt);
return 0;
}
static int foobar_inode_symlink(struct inode *dir,
struct dentry *dentry, struct vfsmount *mnt,
const char *old_name)
{
log_path("inode_symlink", dentry, mnt);
return 0;
}
static int foobar_inode_permission(struct inode *inode, int mask,
struct nameidata *nd)
{
log_path("inode_permission", nd->dentry, nd->mnt);
return 0;
}
struct security_operations foobar_ops = {
.inode_create = foobar_inode_create,
.inode_link = foobar_inode_link,
.inode_unlink = foobar_inode_unlink,
.inode_mkdir = foobar_inode_mkdir,
.inode_rmdir = foobar_inode_rmdir,
.inode_mknod = foobar_inode_mknod,
.inode_rename = foobar_inode_rename,
.inode_setattr = foobar_inode_setattr,
.inode_setxattr = foobar_inode_setxattr,
.inode_getxattr = foobar_inode_getxattr,
.inode_listxattr = foobar_inode_listxattr,
.inode_removexattr = foobar_inode_removexattr,
.inode_symlink = foobar_inode_symlink,
// .inode_permission = foobar_inode_permission,
};
static int __init foobar_init(void)
{
int error;
if ((error = register_security(&foobar_ops))) {
printk(KERN_ERR "Unable to load dummy module\n");
}
return error;
}
static void __exit foobar_exit(void)
{
if (unregister_security(&foobar_ops))
printk(KERN_ERR "Unable to properly unregister module\n");
}
module_init(foobar_init);
module_exit(foobar_exit);
MODULE_DESCRIPTION("Test module");
MODULE_LICENSE("GPL");

View File

@@ -1,561 +0,0 @@
---
security/apparmor/apparmor.h | 60 +++++++---
security/apparmor/apparmorfs.c | 2
security/apparmor/lsm.c | 2
security/apparmor/main.c | 193 +++++++++++++++++++++++------------
security/apparmor/match.c | 8 -
security/apparmor/module_interface.c | 3
6 files changed, 179 insertions(+), 89 deletions(-)
--- a/security/apparmor/apparmor.h
+++ b/security/apparmor/apparmor.h
@@ -26,22 +26,50 @@
#define AA_MAY_LINK 0x0010
#define AA_MAY_LOCK 0x0020
#define AA_EXEC_MMAP 0x0040
+#define AA_EXEC_UNSAFE 0x0080
+#define AA_EXEC_MOD_0 0x0100
+#define AA_EXEC_MOD_1 0x0200
+#define AA_BASE_PERMS (MAY_READ | MAY_WRITE | MAY_EXEC | \
+ MAY_APPEND | AA_MAY_LINK | \
+ AA_MAY_LOCK | AA_EXEC_MMAP | \
+ AA_EXEC_UNSAFE | AA_EXEC_MOD_0 | \
+ AA_EXEC_MOD_1)
-#define AA_CHANGE_PROFILE 0x04000000
-#define AA_EXEC_INHERIT 0x08000000
-#define AA_EXEC_UNCONFINED 0x10000000
-#define AA_EXEC_PROFILE 0x20000000
-#define AA_EXEC_UNSAFE 0x40000000
-
-#define AA_EXEC_MODIFIERS (AA_EXEC_INHERIT | \
- AA_EXEC_UNCONFINED | \
- AA_EXEC_PROFILE)
+#define AA_EXEC_UNCONFINED 0
+#define AA_EXEC_INHERIT AA_EXEC_MOD_0
+#define AA_EXEC_PROFILE AA_EXEC_MOD_1
+#define AA_EXEC_PIX (AA_EXEC_MOD_0 | AA_EXEC_MOD_1)
-#define AA_VALID_PERM_MASK (MAY_READ | MAY_WRITE | MAY_EXEC | \
- MAY_APPEND | AA_MAY_LINK | \
- AA_MAY_LOCK | \
- AA_EXEC_MODIFIERS | AA_EXEC_MMAP | \
- AA_EXEC_UNSAFE | AA_CHANGE_PROFILE)
+#define AA_EXEC_MODIFIERS (AA_EXEC_MOD_0 | AA_EXEC_MOD_1)
+
+#define AA_USER_SHIFT 0
+#define AA_OTHER_SHIFT 10
+
+#define AA_USER_PERMS (AA_BASE_PERMS << AA_USER_SHIFT)
+#define AA_OTHER_PERMS (AA_BASE_PERMS << AA_OTHER_SHIFT)
+
+#define AA_FILE_PERMS (AA_USER_PERMS | AA_OTHER_PERMS)
+
+#define AA_LINK_BITS ((AA_MAY_LINK << AA_USER_SHIFT) | \
+ (AA_MAY_LINK << AA_OTHER_SHIFT))
+
+#define AA_USER_EXEC (MAY_EXEC << AA_USER_SHIFT)
+#define AA_OTHER_EXEC (MAY_EXEC << AA_OTHER_SHIFT)
+
+#define AA_USER_EXEC_MODS (AA_EXEC_MODIFIERS << AA_USER_SHIFT)
+#define AA_OTHER_EXEC_MODS (AA_EXEC_MODIFIERS << AA_OTHER_SHIFT)
+
+#define AA_EXEC_BITS (AA_USER_EXEC | AA_OTHER_EXEC)
+
+#define AA_ALL_EXEC_MODS (AA_USER_EXEC_MODS | \
+ AA_OTHER_EXEC_MODS)
+
+/* shared permissions that are not duplicated in user:group:other */
+#define AA_CHANGE_PROFILE 0x40000000
+
+#define AA_SHARED_PERMS (AA_CHANGE_PROFILE)
+
+#define AA_VALID_PERM_MASK (AA_FILE_PERMS | AA_SHARED_PERMS)
#define AA_SECURE_EXEC_NEEDED 1
@@ -183,7 +211,7 @@ struct aa_audit {
const char *name;
const char *name2;
const char *name3;
- int requested_mask, denied_mask;
+ int request_mask, denied_mask;
struct iattr *iattr;
pid_t task, parent;
int family, type, protocol;
@@ -226,7 +254,7 @@ extern int aa_perm_dir(struct aa_profile
struct dentry *dentry, struct vfsmount *mnt,
int mask);
extern int aa_perm_path(struct aa_profile *, const char *operation,
- const char *name, int);
+ const char *name, int mask, uid_t uid);
extern int aa_link(struct aa_profile *profile,
struct dentry *link, struct vfsmount *link_mnt,
struct dentry *target, struct vfsmount *target_mnt);
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -89,7 +89,7 @@ static struct file_operations apparmorfs
static ssize_t aa_matching_read(struct file *file, char __user *buf,
size_t size, loff_t *ppos)
{
- const char *matching = "pattern=aadfa perms=rwxamlz";
+ const char *matching = "pattern=aadfa perms=rwxamlz user:other";
return simple_read_from_buffer(buf, size, ppos, matching,
strlen(matching));
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -199,7 +199,7 @@ static int apparmor_sysctl(struct ctl_ta
if (name && name - buffer >= 5) {
name -= 5;
memcpy(name, "/proc", 5);
- error = aa_perm_path(profile, "sysctl", name, mask);
+ error = aa_perm_path(profile, "sysctl", name, mask, 0);
}
free_page((unsigned long)buffer);
}
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -31,6 +31,14 @@ static const char *capability_names[] =
struct aa_namespace *default_namespace;
+static int aa_inode_mode(struct inode *inode)
+{
+ /* if the inode doesn't exist the user is creating it */
+ if (!inode || current->fsuid == inode->i_uid)
+ return AA_USER_SHIFT;
+ return AA_OTHER_SHIFT;
+}
+
/**
* aa_file_denied - check for @mask access on a file
* @profile: profile to check against
@@ -51,39 +59,52 @@ static int aa_file_denied(struct aa_prof
* @profile: profile to check against
* @link: pathname of link being created
* @target: pathname of target to be linked to
+ * @target_mode: UGO shift for target inode
* @request_mask: the permissions subset valid only if link succeeds
* Return %0 on success, or else the permissions that the profile denies.
*/
static int aa_link_denied(struct aa_profile *profile, const char *link,
- const char *target, int *request_mask)
+ const char *target, int target_mode,
+ int *request_mask)
{
int l_mode, t_mode, denied_mask;
+ int link_mask = AA_MAY_LINK << target_mode;
l_mode = aa_match(profile->file_rules, link);
t_mode = aa_match(profile->file_rules, target);
/* Ignore valid-profile-transition flags. */
- l_mode &= ~AA_CHANGE_PROFILE;
- t_mode &= ~AA_CHANGE_PROFILE;
+ l_mode &= ~AA_SHARED_PERMS;
+ t_mode &= ~AA_SHARED_PERMS;
- *request_mask = l_mode | AA_MAY_LINK;
-
- /* Link always requires 'l' on the link, a subset of the
- * target's 'r', 'w', 'x', 'a', 'z', and 'm' permissions on the link,
- * and if the link has 'x', an exact match of all the execute flags
- * ('i', 'u', 'U', 'p', 'P').
- */
-#define RWXAZM (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND | AA_MAY_LOCK | \
- AA_EXEC_MMAP)
- denied_mask = ~l_mode & AA_MAY_LINK;
- if (l_mode & RWXAZM)
- denied_mask |= (l_mode & ~ AA_MAY_LINK) & ~t_mode;
- else
- denied_mask |= t_mode | AA_MAY_LINK;
- if (denied_mask & AA_EXEC_MODIFIERS)
- denied_mask |= MAY_EXEC;
+ *request_mask = l_mode | link_mask;
-#undef RWXAZM
+ /* Link always requires 'l' on the link, a subset for user:other
+ * of the target's 'r', 'w', 'x', 'a', 'z', and 'm' permissions on
+ * the link, and if the link has 'x', an exact match of all the
+ * execute flags ('i', 'u', 'U', 'p', 'P').
+ */
+#define SUBSET_PERMS (AA_FILE_PERMS & ~AA_LINK_BITS)
+ denied_mask = ~l_mode & link_mask;
+ if (l_mode & SUBSET_PERMS) {
+ denied_mask |= (l_mode & SUBSET_PERMS) & ~t_mode;
+ if (denied_mask & AA_EXEC_BITS)
+ denied_mask |= l_mode & AA_ALL_EXEC_MODS;
+ else if (l_mode & AA_EXEC_BITS) {
+ if (l_mode & AA_USER_EXEC &&
+ (l_mode & AA_USER_EXEC_MODS) !=
+ (t_mode & AA_USER_EXEC_MODS))
+ denied_mask |= AA_USER_EXEC |
+ (l_mode & AA_USER_EXEC_MODS);
+ if (l_mode & AA_OTHER_EXEC &&
+ (l_mode & AA_OTHER_EXEC_MODS) !=
+ (t_mode & AA_OTHER_EXEC_MODS))
+ denied_mask |= AA_OTHER_EXEC |
+ (l_mode & AA_OTHER_EXEC_MODS);
+ }
+ } else
+ denied_mask |= t_mode | link_mask;
+#undef SUBSET_PERMS
return denied_mask;
}
@@ -173,7 +194,7 @@ static int aa_perm_dentry(struct aa_prof
char *buffer = NULL;
sa->name = aa_get_name(dentry, mnt, &buffer, check);
-
+ sa->request_mask <<= aa_inode_mode(dentry->d_inode);
if (IS_ERR(sa->name)) {
/*
* deleted files are given a pass on permission checks when
@@ -182,13 +203,13 @@ static int aa_perm_dentry(struct aa_prof
if (PTR_ERR(sa->name) == -ENOENT && (check & AA_CHECK_FD))
sa->denied_mask = 0;
else {
- sa->denied_mask = sa->requested_mask;
+ sa->denied_mask = sa->request_mask;
sa->error_code = PTR_ERR(sa->name);
}
sa->name = NULL;
} else
sa->denied_mask = aa_file_denied(profile, sa->name,
- sa->requested_mask);
+ sa->request_mask);
if (!sa->denied_mask)
sa->error_code = 0;
@@ -229,10 +250,10 @@ void free_default_namespace(void)
default_namespace = NULL;
}
-static void aa_audit_file_mask(struct audit_buffer *ab, const char *name,
- int mask)
+static void aa_audit_file_sub_mask(struct audit_buffer *ab, char *buffer,
+ int mask)
{
- char mask_str[10], *m = mask_str;
+ char *m = buffer;
if (mask & AA_EXEC_MMAP)
*m++ = 'm';
@@ -242,32 +263,58 @@ static void aa_audit_file_mask(struct au
*m++ = 'w';
else if (mask & MAY_APPEND)
*m++ = 'a';
- if (mask & (MAY_EXEC | AA_EXEC_MODIFIERS)) {
+ if (mask & MAY_EXEC) {
if (mask & AA_EXEC_UNSAFE) {
- if (mask & AA_EXEC_INHERIT)
- *m++ = 'i';
- if (mask & AA_EXEC_UNCONFINED)
+ switch(mask & AA_EXEC_MODIFIERS) {
+ case AA_EXEC_UNCONFINED:
*m++ = 'u';
- if (mask & AA_EXEC_PROFILE)
+ break;
+ case AA_EXEC_PIX:
+ *m++ = 'p';
+ /* fall through */
+ case AA_EXEC_INHERIT:
+ *m++ = 'i';
+ break;
+ case AA_EXEC_PROFILE:
*m++ = 'p';
+ break;
+ }
} else {
- if (mask & AA_EXEC_INHERIT)
- *m++ = 'I';
- if (mask & AA_EXEC_UNCONFINED)
+ switch(mask & AA_EXEC_MODIFIERS) {
+ case AA_EXEC_UNCONFINED:
*m++ = 'U';
- if (mask & AA_EXEC_PROFILE)
+ break;
+ case AA_EXEC_PIX:
*m++ = 'P';
+ /* fall through */
+ case AA_EXEC_INHERIT:
+ *m++ = 'I';
+ break;
+ case AA_EXEC_PROFILE:
+ *m++ = 'P';
+ break;
+ }
}
- if (mask & MAY_EXEC)
- *m++ = 'x';
+ *m++ = 'x';
}
if (mask & AA_MAY_LINK)
*m++ = 'l';
if (mask & AA_MAY_LOCK)
*m++ = 'k';
*m++ = '\0';
+}
+
+static void aa_audit_file_mask(struct audit_buffer *ab, const char *name,
+ int mask)
+{
+ char user[10], other[10];
- audit_log_format(ab, " %s=\"%s\"", name, mask_str);
+ aa_audit_file_sub_mask(ab, user,
+ (mask & AA_USER_PERMS) >> AA_USER_SHIFT);
+ aa_audit_file_sub_mask(ab, other,
+ (mask & AA_OTHER_PERMS) >> AA_OTHER_SHIFT);
+
+ audit_log_format(ab, " %s=\"%s::%s\"", name, user, other);
}
static const char *address_families[] = {
@@ -316,8 +363,8 @@ static int aa_audit_base(struct aa_profi
if (sa->info)
audit_log_format(ab, " info=\"%s\"", sa->info);
- if (sa->requested_mask)
- aa_audit_file_mask(ab, "requested_mask", sa->requested_mask);
+ if (sa->request_mask)
+ aa_audit_file_mask(ab, "request_mask", sa->request_mask);
if (sa->denied_mask)
aa_audit_file_mask(ab, "denied_mask", sa->denied_mask);
@@ -472,7 +519,7 @@ int aa_attr(struct aa_profile *profile,
sa.operation = "setattr";
sa.gfp_mask = GFP_KERNEL;
sa.iattr = iattr;
- sa.requested_mask = MAY_WRITE;
+ sa.request_mask = MAY_WRITE;
sa.error_code = -EACCES;
check = 0;
@@ -506,7 +553,7 @@ int aa_perm_xattr(struct aa_profile *pro
memset(&sa, 0, sizeof(sa));
sa.operation = operation;
sa.gfp_mask = GFP_KERNEL;
- sa.requested_mask = mask;
+ sa.request_mask = mask;
sa.error_code = -EACCES;
if (inode && S_ISDIR(inode->i_mode))
@@ -540,7 +587,7 @@ int aa_perm(struct aa_profile *profile,
memset(&sa, 0, sizeof(sa));
sa.operation = operation;
sa.gfp_mask = GFP_KERNEL;
- sa.requested_mask = mask;
+ sa.request_mask = mask;
sa.error_code = -EACCES;
error = aa_perm_dentry(profile, dentry, mnt, &sa, check);
@@ -569,24 +616,28 @@ int aa_perm_dir(struct aa_profile *profi
memset(&sa, 0, sizeof(sa));
sa.operation = operation;
sa.gfp_mask = GFP_KERNEL;
- sa.requested_mask = mask;
+ sa.request_mask = mask;
sa.error_code = -EACCES;
return aa_perm_dentry(profile, dentry, mnt, &sa, AA_CHECK_DIR);
}
int aa_perm_path(struct aa_profile *profile, const char *operation,
- const char *name, int mask)
+ const char *name, int mask, uid_t uid)
{
struct aa_audit sa;
memset(&sa, 0, sizeof(sa));
sa.operation = operation;
sa.gfp_mask = GFP_KERNEL;
- sa.requested_mask = mask;
+ sa.request_mask = mask;
sa.name = name;
+ if (current->fsuid == uid)
+ sa.request_mask = mask << AA_USER_SHIFT;
+ else
+ sa.request_mask = mask << AA_OTHER_SHIFT;
- sa.denied_mask = aa_file_denied(profile, name, mask);
+ sa.denied_mask = aa_file_denied(profile, name, sa.request_mask) ;
sa.error_code = sa.denied_mask ? -EACCES : 0;
return aa_audit(profile, &sa);
@@ -672,9 +723,10 @@ int aa_link(struct aa_profile *profile,
}
if (sa.name && sa.name2) {
- sa.requested_mask = AA_MAY_LINK;
+ sa.request_mask = AA_MAY_LINK;
sa.denied_mask = aa_link_denied(profile, sa.name, sa.name2,
- &sa.requested_mask);
+ aa_inode_mode(target->d_inode),
+ &sa.request_mask);
sa.error_code = sa.denied_mask ? -EACCES : 0;
}
@@ -818,7 +870,7 @@ aa_register_find(struct aa_profile *prof
__FUNCTION__, new_profile->name);
} else if (mandatory && profile) {
sa->info = "mandatory profile missing";
- sa->denied_mask = MAY_EXEC;
+ sa->denied_mask = sa->request_mask; /* shifted MAY_EXEC */
if (complain) {
aa_audit_hint(profile, sa);
new_profile =
@@ -829,7 +881,7 @@ aa_register_find(struct aa_profile *prof
}
} else {
/* Only way we can get into this code is if task
- * is unconfined.
+ * is unconfined, or pix.
*/
AA_DEBUG("%s: No profile found for exec image '%s'\n",
__FUNCTION__,
@@ -851,7 +903,7 @@ int aa_register(struct linux_binprm *bpr
char *buffer = NULL;
struct file *filp = bprm->file;
struct aa_profile *profile, *old_profile, *new_profile = NULL;
- int exec_mode = AA_EXEC_UNSAFE, complain = 0;
+ int exec_mode, complain = 0, shift;
struct aa_audit sa;
AA_DEBUG("%s\n", __FUNCTION__);
@@ -862,11 +914,14 @@ int aa_register(struct linux_binprm *bpr
return -ENOENT;
}
+ shift = aa_inode_mode(filp->f_dentry->d_inode);
+ exec_mode = AA_EXEC_UNSAFE << shift;
+
memset(&sa, 0, sizeof(sa));
sa.operation = "exec";
sa.gfp_mask = GFP_KERNEL;
sa.name = filename;
- sa.requested_mask = MAY_EXEC;
+ sa.request_mask = MAY_EXEC << shift;
repeat:
profile = aa_get_profile(current);
@@ -878,16 +933,16 @@ repeat:
*/
exec_mode = aa_match(profile->file_rules, filename);
- if (exec_mode & (MAY_EXEC | AA_EXEC_MODIFIERS)) {
- switch (exec_mode & (MAY_EXEC | AA_EXEC_MODIFIERS)) {
- case MAY_EXEC | AA_EXEC_INHERIT:
+ if (exec_mode & sa.request_mask) {
+ switch ((exec_mode >> shift) & AA_EXEC_MODIFIERS) {
+ case AA_EXEC_INHERIT:
AA_DEBUG("%s: INHERIT %s\n",
__FUNCTION__,
filename);
/* nothing to be done here */
goto cleanup;
- case MAY_EXEC | AA_EXEC_UNCONFINED:
+ case AA_EXEC_UNCONFINED:
AA_DEBUG("%s: UNCONFINED %s\n",
__FUNCTION__,
filename);
@@ -896,7 +951,7 @@ repeat:
new_profile = NULL;
break;
- case MAY_EXEC | AA_EXEC_PROFILE:
+ case AA_EXEC_PROFILE:
AA_DEBUG("%s: PROFILE %s\n",
__FUNCTION__,
filename);
@@ -905,6 +960,18 @@ repeat:
1, complain,
&sa);
break;
+ case AA_EXEC_PIX:
+ AA_DEBUG("%s: PROFILE %s\n",
+ __FUNCTION__,
+ filename);
+ new_profile = aa_register_find(profile,
+ filename,
+ 0, complain,
+ &sa);
+ if (!new_profile)
+ /* inherit - nothing to be done here */
+ goto cleanup;
+ break;
}
} else if (complain) {
@@ -914,9 +981,9 @@ repeat:
*/
new_profile =
aa_dup_profile(profile->ns->null_complain_profile);
- exec_mode |= AA_EXEC_UNSAFE;
+ exec_mode |= AA_EXEC_UNSAFE << shift;
} else {
- sa.denied_mask = MAY_EXEC;
+ sa.denied_mask = sa.request_mask;
aa_audit_reject(profile, &sa);
new_profile = ERR_PTR(-EPERM);
}
@@ -937,7 +1004,7 @@ repeat:
if (PTR_ERR(old_profile) == -ESTALE)
goto repeat;
if (PTR_ERR(old_profile) == -EPERM) {
- sa.denied_mask = MAY_EXEC;
+ sa.denied_mask = sa.request_mask;
sa.info = "unable to set profile due to ptrace";
sa.task = current->parent->pid;
aa_audit_reject(profile, &sa);
@@ -957,7 +1024,7 @@ repeat:
* Cases 2 and 3 are marked as requiring secure exec
* (unless policy specified "unsafe exec")
*/
- if (!(exec_mode & AA_EXEC_UNSAFE)) {
+ if (!(exec_mode & (AA_EXEC_UNSAFE << shift))) {
unsigned long bprm_flags;
bprm_flags = AA_SECURE_EXEC_NEEDED;
@@ -967,7 +1034,7 @@ repeat:
if (complain && new_profile &&
new_profile == new_profile->ns->null_complain_profile) {
- sa.requested_mask = 0;
+ sa.request_mask = 0;
sa.name = NULL;
sa.info = "set profile";
aa_audit_hint(new_profile, &sa);
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -175,14 +175,8 @@ int verify_dfa(struct aa_dfa *dfa)
for (i = 0; i < state_count; i++) {
int mode = ACCEPT_TABLE(dfa)[i];
- if (mode & ~AA_VALID_PERM_MASK)
+ if (mode & ~AA_VALID_PERM_MASK) {
goto out;
-
- /* if MAY_EXEC, exactly one exec modifier must be set */
- if (mode & MAY_EXEC) {
- mode &= AA_EXEC_MODIFIERS;
- if (mode & (mode - 1))
- goto out;
}
}
--- a/security/apparmor/module_interface.c
+++ b/security/apparmor/module_interface.c
@@ -349,7 +349,8 @@ fail:
sa.operation = operation;
sa.gfp_mask = GFP_KERNEL;
sa.name = profile && profile->name ? profile->name : "unknown";
- sa.info = "failed to unpack profile";
+ if (!sa.info)
+ sa.info = "failed to unpack profile";
aa_audit_status(NULL, &sa);
if (profile)

View File

@@ -1,24 +0,0 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Allow permission functions to tell between parent and leaf checks
Set the LOOKUP_CONTINUE flag when checking parent permissions. This allows
permission functions to tell between parent and leaf checks.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/namei.c | 2 ++
1 file changed, 2 insertions(+)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1508,6 +1508,8 @@ static inline int may_create(struct inod
return -EEXIST;
if (IS_DEADDIR(dir))
return -ENOENT;
+ if (nd)
+ nd->flags |= LOOKUP_CONTINUE;
return permission(dir,MAY_WRITE | MAY_EXEC, nd);
}

View File

@@ -1,129 +0,0 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Filter out disconnected paths from /proc/mounts
Use d_path() instead of seq_path when generating /proc/mounts and
/proc/$id/mountstats, reuse the same buffer for all mounts, and filter out
disconnected paths.
This path has no net effect in itself because d_path() so far doesn't
distinguish sconnected and disconnected paths yet. The next patch fixes that
though; without this patch, the next patch would break /proc/mounts and
/proc/$id/mountstats.
There is some disagreement what /proc/mounts should include. Currently it
reports all mounts from the current namespace and doesn't include lazy
unmounts. This leads to ambiguities with the rootfs (which is an internal mount
irrelevant to user-space except in the initrd), and in chroots.
With this and the next patch, /proc/mounts only reports the mounts reachable
for the current process, which makes a lot more sense IMO. If the current
process is rooted in the namespace root (which it usually is), it will see all
mounts except for the rootfs.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
fs/namespace.c | 23 +++++++++++++++++++++--
fs/proc/base.c | 10 +++++++++-
2 files changed, 30 insertions(+), 3 deletions(-)
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -348,8 +348,16 @@ static inline void mangle(struct seq_fil
seq_escape(m, s, " \t\n\\");
}
+/* Keep in sync with fs/proc/base.c! */
+struct proc_mounts {
+ struct seq_file m;
+ void *page;
+ int event;
+};
+
static int show_vfsmnt(struct seq_file *m, void *v)
{
+ void *page = container_of(m, struct proc_mounts, m)->page;
struct vfsmount *mnt = v;
int err = 0;
static struct proc_fs_info {
@@ -371,10 +379,15 @@ static int show_vfsmnt(struct seq_file *
{ 0, NULL }
};
struct proc_fs_info *fs_infop;
+ char *path;
+
+ path = d_path(mnt->mnt_root, mnt, page, PAGE_SIZE);
+ if (IS_ERR(path) || *path != '/')
+ return err;
mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none");
seq_putc(m, ' ');
- seq_path(m, mnt, mnt->mnt_root, " \t\n\\");
+ mangle(m, path);
seq_putc(m, ' ');
mangle(m, mnt->mnt_sb->s_type->name);
seq_puts(m, mnt->mnt_sb->s_flags & MS_RDONLY ? " ro" : " rw");
@@ -401,8 +414,14 @@ struct seq_operations mounts_op = {
static int show_vfsstat(struct seq_file *m, void *v)
{
+ void *page = container_of(m, struct proc_mounts, m)->page;
struct vfsmount *mnt = v;
int err = 0;
+ char *path;
+
+ path = d_path(mnt->mnt_root, mnt, page, PAGE_SIZE);
+ if (IS_ERR(path) || *path != '/')
+ return err; /* error or path unreachable from chroot */
/* device */
if (mnt->mnt_devname) {
@@ -413,7 +432,7 @@ static int show_vfsstat(struct seq_file
/* mount point */
seq_puts(m, " mounted on ");
- seq_path(m, mnt, mnt->mnt_root, " \t\n\\");
+ mangle(m, path);
seq_putc(m, ' ');
/* file system type */
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -354,8 +354,11 @@ static const struct inode_operations pro
};
extern struct seq_operations mounts_op;
+
+/* Keep in sync with fs/namespace.c! */
struct proc_mounts {
struct seq_file m;
+ void *page;
int event;
};
@@ -383,12 +386,16 @@ static int __mounts_open(struct inode *i
p = kmalloc(sizeof(struct proc_mounts), GFP_KERNEL);
if (p) {
file->private_data = &p->m;
- ret = seq_open(file, seq_ops);
+ p->page = (void *)__get_free_page(GFP_KERNEL);
+ if (p->page)
+ ret = seq_open(file, seq_ops);
if (!ret) {
p->m.private = ns;
p->event = ns->event;
return 0;
}
+ if (p->page)
+ free_page((unsigned long)p->page);
kfree(p);
}
put_mnt_ns(ns);
@@ -407,6 +414,7 @@ static int mounts_release(struct inode *
container_of(file->private_data, struct proc_mounts, m);
struct mnt_namespace *ns = p->m.private;
+ free_page((unsigned long)p->page);
put_mnt_ns(ns);
return seq_release(inode, file);
}

View File

@@ -1,98 +0,0 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Remove duplicate proc code
Remove some duplicate code in generating the contents of /proc/mounts and
/proc/$pid/mountstats.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
This patch is optional, and does not affect the rest of this series.
fs/proc/base.c | 45 +++++++++++++++------------------------------
1 file changed, 15 insertions(+), 30 deletions(-)
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -359,7 +359,8 @@ struct proc_mounts {
int event;
};
-static int mounts_open(struct inode *inode, struct file *file)
+static int __mounts_open(struct inode *inode, struct file *file,
+ struct seq_operations *seq_ops)
{
struct task_struct *task = get_proc_task(inode);
struct mnt_namespace *ns = NULL;
@@ -382,7 +383,7 @@ static int mounts_open(struct inode *ino
p = kmalloc(sizeof(struct proc_mounts), GFP_KERNEL);
if (p) {
file->private_data = &p->m;
- ret = seq_open(file, &mounts_op);
+ ret = seq_open(file, seq_ops);
if (!ret) {
p->m.private = ns;
p->event = ns->event;
@@ -395,17 +396,25 @@ static int mounts_open(struct inode *ino
return ret;
}
+static int mounts_open(struct inode *inode, struct file *file)
+{
+ return __mounts_open(inode, file, &mounts_op);
+}
+
static int mounts_release(struct inode *inode, struct file *file)
{
- struct seq_file *m = file->private_data;
- struct mnt_namespace *ns = m->private;
+ struct proc_mounts *p =
+ container_of(file->private_data, struct proc_mounts, m);
+ struct mnt_namespace *ns = p->m.private;
+
put_mnt_ns(ns);
return seq_release(inode, file);
}
static unsigned mounts_poll(struct file *file, poll_table *wait)
{
- struct proc_mounts *p = file->private_data;
+ struct proc_mounts *p =
+ container_of(file->private_data, struct proc_mounts, m);
struct mnt_namespace *ns = p->m.private;
unsigned res = 0;
@@ -432,31 +441,7 @@ static const struct file_operations proc
extern struct seq_operations mountstats_op;
static int mountstats_open(struct inode *inode, struct file *file)
{
- int ret = seq_open(file, &mountstats_op);
-
- if (!ret) {
- struct seq_file *m = file->private_data;
- struct mnt_namespace *mnt_ns = NULL;
- struct task_struct *task = get_proc_task(inode);
-
- if (task) {
- task_lock(task);
- if (task->nsproxy)
- mnt_ns = task->nsproxy->mnt_ns;
- if (mnt_ns)
- get_mnt_ns(mnt_ns);
- task_unlock(task);
- put_task_struct(task);
- }
-
- if (mnt_ns)
- m->private = mnt_ns;
- else {
- seq_release(inode, file);
- ret = -EINVAL;
- }
- }
- return ret;
+ return __mounts_open(inode, file, &mountstats_op);
}
static const struct file_operations proc_mountstats_operations = {

Some files were not shown because too many files have changed in this diff Show More