namespaces: log namespaces per task
Subsystems (5)
v2 cover namespaces: log namespaces per task
The purpose is to track namespaces in use by logged processes from the perspective of init_*_ns. The first patch defines a function to generate them and assigns them. The second patch provides an example of usage for audit_log_task_info() which is used by syscall audits, among others. audit_log_task() and audit_common_recv_message() would be other potential use cases.
Use a serial number per namespace (unique across one boot of one kernel) instead of the inode number (which is claimed to have had the right to change reserved and is not necessarily unique if there is more than one proc fs). It could be argued that the inode numbers have now become a defacto interface and can't change now, but I'm proposing this approach to see if this helps address some of the objections to the earlier patchset.
There could also have messages added to track the creation and the destruction of namespaces, listing the parent for hierarchical namespaces such as pidns, userns, and listing other ids for non-hierarchical namespaces, as well as other information to help identify a namespace.
There has been some progress made for audit in net namespaces and pid namespaces since this previous thread. net namespaces are now served as peers by one auditd in the init_net namespace with processes in a non-init_net namespace being able to write records if they are in the init_user_ns and have CAP_AUDIT_WRITE. Processes in a non-init_pid_ns can now similarly write records. As for CAP_AUDIT_READ, I just posted a patchset to check capabilities of userspace processes that try to join netlink broadcast groups.
Questions:
Is there a way to link serial numbers of namespaces involved in migration of a container to another kernel? It sounds like what is needed is a part of a mangement application that is able to pull the audit rcords from constituent hosts to build an audit trail of a container.
What additional events should list this information?
Does this present any problematic information leaks? Only CAP_AUDIT_CONTROL (and proposed CAP_AUDIT_READ) in init_user_ns can get to this information in the init namespace at the moment from audit. *However*, the addition of the proc/<pid>/ns/*_snum does make it available to other processes now.
Proposed output format:
This differs slightly from Aristeu's patch because of the label conflict with "pid=" due to including it in existing records rather than it being a seperate record. The serial numbers are printed in hex.
type=SYSCALL msg=audit(1399651071.433:72): arch=c000003e syscall=272 success=yes exit=0 a0=40000000 a1=ffffffffffffffff a2=0 a3=22 items=0 ppid=1 pid=483 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="(t-daemon)" exe="/usr/lib/systemd/systemd" netns=97 utsns=2 ipcns=1 pidns=4 userns=3 mntns=5 subj=system_u:system_r:init_t:s0 key=(null)
The third patch adds access functions to get to the serial numbers in a similar way to inode access for namespace proc operations.
The fourth patch implements, as suggested by Serge Hallyn, making these serial numbers available in /proc/self/ns/{ipc,mnt,net,pid,user,uts}_snum. I chose "snum" instead of "seq" for consistency with inum and there are a number of other uses of "seq" in the namespace code.
**Although it works as expected, I'm not that happy with that patch because it duplicates a lot of code, including minor changes to proc_ns_follow_link(), proc_ns_readlink(), ns_dname(). The only way I could see to get that information into those functions would be through dentry. Maybe the information I need is already in there in d_name or d_iname? Or I could add a flag to d_flags (there are 9 bits unused?) but that flag isn't useful for any other types of entries, so I'm not so keen to pollute it.
The fifth patch exposes proc's ns entries structure which lists a number of useful operations per namespace type for other subsystems to use.
The sixth patch converts the audit namespace serial number logging over to use the ns_entries access methods.
The two audit patches should really be squashed down to one if the exposure of ns_entries is ok.
Note: This set does not try to solve the non-init namespace audit messages and auditd problem yet. That will come later, likely with additional auditd instances running in another namespace with a limited ability to influence the master auditd. I echo Eric B's idea that messages destined for different namespaces would have to be tailored for that namespace with references that make sense (such as the right pid number reported to that pid namespace, and not leaking info about parents or peers).
v1 -> v2:
Avoid rollover by switching from an int to a long long. Change rollover behaviour from simply avoiding zero to raising a BUG. Expose serial numbers in /proc/<pid>/ns/*_snum.
Expose ns_entries and use it in audit.
Richard Guy Briggs (6):
namespaces: assign each namespace instance a serial number
audit: log namespace serial numbers
namespaces: expose namespace instance serial number in
proc_ns_operations
namespaces: expose ns instance serial numbers in proc
namespaces: expose ns_entries
audit: convert namespace serial number logging to use proc ns_entries
fs/mount.h | 1 + fs/namespace.c | 8 ++ fs/proc/namespaces.c | 152 ++++++++++++++++++++++++++++++++++++---- include/linux/audit.h | 7 ++ include/linux/ipc_namespace.h | 1 + include/linux/nsproxy.h | 8 ++ include/linux/pid_namespace.h | 1 + include/linux/proc_ns.h | 2 + include/linux/user_namespace.h | 1 + include/linux/utsname.h | 1 + include/net/net_namespace.h | 1 + init/version.c | 1 + ipc/msgutil.c | 1 + ipc/namespace.c | 10 +++ kernel/audit.c | 21 +++++- kernel/nsproxy.c | 20 +++++ kernel/pid.c | 1 + kernel/pid_namespace.c | 9 +++ kernel/user.c | 1 + kernel/user_namespace.c | 9 +++ kernel/utsname.c | 10 +++ net/core/net_namespace.c | 11 +++- 22 files changed, 262 insertions(+), 15 deletions(-)
PATCH v2 1/6 namespaces: assign each namespace instance a serial number
Generate and assign a serial number per namespace instance since boot.
Use a serial number per namespace (unique across one boot of one kernel) instead of the inode number (which is claimed to have had the right to change reserved and is not necessarily unique if there is more than one proc fs) to uniquely identify it per kernel boot.
--- fs/mount.h | 1 + fs/namespace.c | 1 + include/linux/ipc_namespace.h | 1 + include/linux/nsproxy.h | 8 ++++++++ include/linux/pid_namespace.h | 1 + include/linux/user_namespace.h | 1 + include/linux/utsname.h | 1 + include/net/net_namespace.h | 1 + init/version.c | 1 + ipc/msgutil.c | 1 + ipc/namespace.c | 2 ++ kernel/nsproxy.c | 20 ++++++++++++++++++++ kernel/pid.c | 1 + kernel/pid_namespace.c | 2 ++ kernel/user.c | 1 + kernel/user_namespace.c | 2 ++ kernel/utsname.c | 2 ++ net/core/net_namespace.c | 4 +++- 18 files changed, 50 insertions(+), 1 deletions(-)
fs/mount.h +1 -0
@@ -5,6 +5,7 @@struct mnt_namespace {atomic_t count;unsigned int proc_inum; +unsigned long long serial_num;struct mount * root;struct list_head list;struct user_namespace *user_ns;
fs/namespace.c +1 -0
@@ -2472,6 +2472,7 @@ static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns)kfree (new_ns);return ERR_PTR (ret); } + new_ns->serial_num =ns_serial (); new_ns->seq =atomic64_add_return (1 , &mnt_ns_seq);atomic_set (&new_ns->count,1 ); new_ns->root =NULL ;
include/linux/ipc_namespace.h +1 -0
@@ -69,6 +69,7 @@ struct ipc_namespace {struct user_namespace *user_ns;unsigned int proc_inum; +unsigned long long serial_num; };extern struct ipc_namespace init_ipc_ns;
include/linux/nsproxy.h +8 -0
@@ -66,6 +66,14 @@ static inline struct nsproxy *task_nsproxy(struct task_struct *tsk)return rcu_dereference (tsk->nsproxy); } +unsigned long long ns_serial (void ); +enum { +NS_IPC_INIT_SN =1 , +NS_UTS_INIT_SN =2 , +NS_USER_INIT_SN =3 , +NS_PID_INIT_SN =4 , +}; +int copy_namespaces (unsigned long flags,struct task_struct *tsk);void exit_task_namespaces (struct task_struct *tsk);void switch_task_namespaces (struct task_struct *tsk,struct nsproxy *new);
include/linux/pid_namespace.h +1 -0
@@ -43,6 +43,7 @@ struct pid_namespace {int hide_pid;int reboot;/* group exit code if this pidns was rebooted */ unsigned int proc_inum; +unsigned long long serial_num; };extern struct pid_namespace init_pid_ns;
include/linux/user_namespace.h +1 -0
@@ -27,6 +27,7 @@ struct user_namespace {kuid_t owner;kgid_t group;unsigned int proc_inum; +unsigned long long serial_num;/* Register of per-UID persistent keyrings for this namespace */ #ifdef CONFIG_PERSISTENT_KEYRINGS
include/linux/utsname.h +1 -0
@@ -24,6 +24,7 @@ struct uts_namespace {struct new_utsname name;struct user_namespace *user_ns;unsigned int proc_inum; +unsigned long long serial_num; };extern struct uts_namespace init_uts_ns;
include/net/net_namespace.h +1 -0
@@ -59,6 +59,7 @@ struct net {struct user_namespace *user_ns;/* Owning user namespace */ unsigned int proc_inum; +unsigned long long serial_num;struct proc_dir_entry *proc_net;struct proc_dir_entry *proc_net_stat;
init/version.c +1 -0
@@ -36,6 +36,7 @@ struct uts_namespace init_uts_ns = { }, .user_ns = &init_user_ns, .proc_inum = PROC_UTS_INIT_INO, + .serial_num = NS_UTS_INIT_SN /* ns_serial() */, };EXPORT_SYMBOL_GPL (init_uts_ns);
ipc/msgutil.c +1 -0
@@ -32,6 +32,7 @@ struct ipc_namespace init_ipc_ns = { .count = ATOMIC_INIT(1), .user_ns = &init_user_ns, .proc_inum = PROC_IPC_INIT_INO, + .serial_num = NS_IPC_INIT_SN /* ns_serial() */, };atomic_t nr_ipc_ns =ATOMIC_INIT (1 );
ipc/namespace.c +2 -0
@@ -41,6 +41,8 @@ static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns, }atomic_inc (&nr_ipc_ns); + ns->serial_num =ns_serial (); +sem_init_ns (ns);msg_init_ns (ns);shm_init_ns (ns);
kernel/nsproxy.c +20 -0
@@ -41,6 +41,26 @@ struct nsproxy init_nsproxy = {#endif }; +/** +* ns_serial - compute a serial number for the namespace +* +* Compute a serial number for the namespace to uniquely identify it in +* audit records. +*/ +unsigned long long ns_serial (void ) +{ +static DEFINE_SPINLOCK (serial_lock); +static unsigned long long serial =4 ;/* reserved for IPC, UTS, user, PID */ +unsigned long flags; + +spin_lock_irqsave (&serial_lock, flags); + ++serial; +spin_unlock_irqrestore (&serial_lock, flags); +BUG_ON (!serial); + +return serial; +} +static inline struct nsproxy *create_nsproxy (void ) {struct nsproxy *nsproxy;
kernel/pid.c +1 -0
@@ -80,6 +80,7 @@ struct pid_namespace init_pid_ns = { .child_reaper = &init_task, .user_ns = &init_user_ns, .proc_inum = PROC_PID_INIT_INO, + .serial_num = NS_PID_INIT_SN /* ns_serial() */, };EXPORT_SYMBOL_GPL (init_pid_ns);
kernel/pid_namespace.c +2 -0
@@ -109,6 +109,8 @@ static struct pid_namespace *create_pid_namespace(struct user_namespace *user_nsif (err) goto out_free_map; + ns->serial_num =ns_serial (); +kref_init (&ns->kref); ns->level = level; ns->parent =get_pid_ns (parent_pid_ns);
kernel/user.c +1 -0
@@ -51,6 +51,7 @@ struct user_namespace init_user_ns = { .owner = GLOBAL_ROOT_UID, .group = GLOBAL_ROOT_GID, .proc_inum = PROC_USER_INIT_INO, + .serial_num =NS_USER_INIT_SN /* ns_serial() */ , #ifdef CONFIG_PERSISTENT_KEYRINGS .persistent_keyring_register_sem = __RWSEM_INITIALIZER(init_user_ns.persistent_keyring_register_sem),
kernel/user_namespace.c +2 -0
@@ -92,6 +92,8 @@ int create_user_ns(struct cred *new)return ret; } + ns->serial_num =ns_serial (); +atomic_set (&ns->count,1 );/* Leave the new->user_ns reference with the new user namespace. */ ns->parent = parent_ns;
kernel/utsname.c +2 -0
@@ -48,6 +48,8 @@ static struct uts_namespace *clone_uts_ns(struct user_namespace *user_ns,return ERR_PTR (err); } + ns->serial_num =ns_serial (); +down_read (&uts_sem);memcpy (&ns->name, &old_ns->name,sizeof (ns->name)); ns->user_ns =get_user_ns (user_ns);
net/core/net_namespace.c +3 -1
@@ -104,8 +104,10 @@ static int ops_init(const struct pernet_operations *ops, struct net *net) err =0 ;if (ops->init) err = ops->init (net); -if (!err) +if (!err) { + net->serial_num =ns_serial ();return 0 ; + } cleanup:kfree (data);
Signature · 1 line
1.7.1
Eric Paris
maintainerRe: [PATCH V2 1/6] namespaces: assign each namespace instance a serial number
Quoted 23 lines · Richard Guy Briggs
Generate and assign a serial number per namespace instance since boot.
Use a serial number per namespace (unique across one boot of one kernel) instead of the inode number (which is claimed to have had the right to change reserved and is not necessarily unique if there is more than one proc fs) to uniquely identify it per kernel boot.
Signed-off-by Richard Guy Briggs <[email protected]>---
kernel/nsproxy.c +13
-0+/** +* ns_serial - compute a serial number for the namespace +* +* Compute a serial number for the namespace to uniquely identify it in +* audit records. +*/ +unsigned long long ns_serial (void ) +{ +static DEFINE_SPINLOCK (serial_lock); +static unsigned long long serial =4 ;/* reserved for IPC, UTS, user, PID */ +unsigned long flags; + +spin_lock_irqsave (&serial_lock, flags);
kernel/nsproxy.c struct nsproxy init_nsproxy = { · line 66
Quoted 10 lines
kernel/nsproxy.c +7
-0+ ++serial; +spin_unlock_irqrestore (&serial_lock, flags); +BUG_ON (!serial); + +return serial; +} +static inline struct nsproxy *create_nsproxy (void ) {struct nsproxy *nsproxy;
atomic64_t instead of doing it yourself?
and why _irqsave() ? Can we seriously create new namespaces in irq context? If you use the atomic though, you don't have to worry about it...
Re: [PATCH V2 1/6] namespaces: assign each namespace instance a serial number
Quoted 28 lines · Eric Paris
Quoted 27 lines · Richard Guy Briggs
Generate and assign a serial number per namespace instance since boot.
Use a serial number per namespace (unique across one boot of one kernel) instead of the inode number (which is claimed to have had the right to change reserved and is not necessarily unique if there is more than one proc fs) to uniquely identify it per kernel boot.
Signed-off-by Richard Guy Briggs <[email protected]>---
kernel/nsproxy.c +17
-0+/** +* ns_serial - compute a serial number for the namespace +* +* Compute a serial number for the namespace to uniquely identify it in +* audit records. +*/ +unsigned long long ns_serial (void ) +{ +static DEFINE_SPINLOCK (serial_lock); +static unsigned long long serial =4 ;/* reserved for IPC, UTS, user, PID */ +unsigned long flags; + +spin_lock_irqsave (&serial_lock, flags); + ++serial; +spin_unlock_irqrestore (&serial_lock, flags); +BUG_ON (!serial); +
Quoted 8 lines
kernel/nsproxy.c struct nsproxy init_nsproxy = { · line 66
Quoted 6 lines
kernel/nsproxy.c +3
-0+return serial; +} +static inline struct nsproxy *create_nsproxy (void ) {struct nsproxy *nsproxy;
atomic64_tinstead of doing it yourself?
I'm willing to switch to atomic64_*. Thanks for pointing out its existence.
Quoted 3 lines
and why
_irqsave()? Can we seriously create new namespaces in irq context? If you use the atomic though, you don't have to worry about it...
Agreed. That is unlikely.
- RGB
--
Richard Guy Briggs <[email protected]> Senior Software Engineer, Kernel Security, AMER ENG Base Operating Systems, Red Hat Remote, Ottawa, Canada
Voice: +1.647.777.2635, Internal: (81) 32635, Alt: +1.613.693.0684x3545
Re: [PATCH V2 1/6] namespaces: assign each namespace instance a serial number
Quoted 38 lines · Richard Guy Briggs
Quoted 28 lines · Eric Paris
Quoted 27 lines · Richard Guy Briggs
Generate and assign a serial number per namespace instance since boot.
Use a serial number per namespace (unique across one boot of one kernel) instead of the inode number (which is claimed to have had the right to change reserved and is not necessarily unique if there is more than one proc fs) to uniquely identify it per kernel boot.
Signed-off-by Richard Guy Briggs <[email protected]>---
kernel/nsproxy.c +17
-0+/** +* ns_serial - compute a serial number for the namespace +* +* Compute a serial number for the namespace to uniquely identify it in +* audit records. +*/ +unsigned long long ns_serial (void ) +{ +static DEFINE_SPINLOCK (serial_lock); +static unsigned long long serial =4 ;/* reserved for IPC, UTS, user, PID */ +unsigned long flags; + +spin_lock_irqsave (&serial_lock, flags); + ++serial; +spin_unlock_irqrestore (&serial_lock, flags); +BUG_ON (!serial); +Quoted 8 lines
kernel/nsproxy.c struct nsproxy init_nsproxy = { · line 66
Quoted 6 lines
kernel/nsproxy.c +3
-0+return serial; +} +static inline struct nsproxy *create_nsproxy (void ) {struct nsproxy *nsproxy;
atomic64_tinstead of doing it yourself?
Quoted 2 lines
I'm willing to switch to atomic64_*. Thanks for pointing out its existence.
Same would then go for using atomic_t in audit_serial().
Quoted 6 lines
Quoted 3 lines
and why
_irqsave()? Can we seriously create new namespaces in irq context? If you use the atomic though, you don't have to worry about it...Agreed. That is unlikely.
Quoted 7 lines
- RGB
--
Richard Guy Briggs <[email protected]> Senior Software Engineer, Kernel Security, AMER ENG Base Operating Systems, Red Hat Remote, Ottawa, Canada
Voice: +1.647.777.2635, Internal: (81) 32635, Alt: +1.613.693.0684x3545
- RGB
--
Richard Guy Briggs <[email protected]> Senior Software Engineer, Kernel Security, AMER ENG Base Operating Systems, Red Hat Remote, Ottawa, Canada
Voice: +1.647.777.2635, Internal: (81) 32635, Alt: +1.613.693.0684x3545
Eric Paris
maintainerRe: [PATCH V2 1/6] namespaces: assign each namespace instance a serial number
Quoted 39 lines · Richard Guy Briggs
Quoted 38 lines · Richard Guy Briggs
Quoted 36 lines · Eric Paris
Quoted 23 lines · Richard Guy Briggs
Generate and assign a serial number per namespace instance since boot.
Use a serial number per namespace (unique across one boot of one kernel) instead of the inode number (which is claimed to have had the right to change reserved and is not necessarily unique if there is more than one proc fs) to uniquely identify it per kernel boot.
Signed-off-by Richard Guy Briggs <[email protected]>---
kernel/nsproxy.c +13
-0+/** +* ns_serial - compute a serial number for the namespace +* +* Compute a serial number for the namespace to uniquely identify it in +* audit records. +*/ +unsigned long long ns_serial (void ) +{ +static DEFINE_SPINLOCK (serial_lock); +static unsigned long long serial =4 ;/* reserved for IPC, UTS, user, PID */ +unsigned long flags; + +spin_lock_irqsave (&serial_lock, flags);kernel/nsproxy.c struct nsproxy init_nsproxy = { · line 66
Quoted 10 lines
kernel/nsproxy.c +7
-0+ ++serial; +spin_unlock_irqrestore (&serial_lock, flags); +BUG_ON (!serial); + +return serial; +} +static inline struct nsproxy *create_nsproxy (void ) {struct nsproxy *nsproxy;
atomic64_tinstead of doing it yourself?
Quoted 4 lines
Quoted 2 lines
I'm willing to switch to atomic64_*. Thanks for pointing out its existence.
Same would then go for using
atomic_tinaudit_serial().
Yup, moving to an atomic in audit_serial() looks like a good idea to me.
Eric Paris
maintainerRe: [PATCH V2 1/6] namespaces: assign each namespace instance a serial number
Quoted 40 lines · Eric Paris
Quoted 39 lines · Richard Guy Briggs
Quoted 38 lines · Richard Guy Briggs
Quoted 36 lines · Eric Paris
Quoted 23 lines · Richard Guy Briggs
Generate and assign a serial number per namespace instance since boot.
Use a serial number per namespace (unique across one boot of one kernel) instead of the inode number (which is claimed to have had the right to change reserved and is not necessarily unique if there is more than one proc fs) to uniquely identify it per kernel boot.
Signed-off-by Richard Guy Briggs <[email protected]>---
kernel/nsproxy.c +13
-0+/** +* ns_serial - compute a serial number for the namespace +* +* Compute a serial number for the namespace to uniquely identify it in +* audit records. +*/ +unsigned long long ns_serial (void ) +{ +static DEFINE_SPINLOCK (serial_lock); +static unsigned long long serial =4 ;/* reserved for IPC, UTS, user, PID */ +unsigned long flags; + +spin_lock_irqsave (&serial_lock, flags);kernel/nsproxy.c struct nsproxy init_nsproxy = { · line 66
Quoted 10 lines
kernel/nsproxy.c +7
-0+ ++serial; +spin_unlock_irqrestore (&serial_lock, flags); +BUG_ON (!serial); + +return serial; +} +static inline struct nsproxy *create_nsproxy (void ) {struct nsproxy *nsproxy;
atomic64_tinstead of doing it yourself?
Quoted 6 lines
Quoted 4 lines
Quoted 2 lines
I'm willing to switch to atomic64_*. Thanks for pointing out its existence.
Same would then go for using
atomic_tinaudit_serial().Yup, moving to an atomic in
audit_serial()looks like a good idea to me.
Talking with steve on irc, neither of us see a need for the double increment quirk if the serial wraps back to 0. Nothing wrong with 0, it is a number.
-Eric
PATCH v2 2/6 audit: log namespace serial numbers
In reply to Richard Guy BriggsLog the namespace serial numbers of a task in audit_log_task_info() which is used by syscall audits, among others..
Idea first presented:
https://www.redhat.com/archives/linux-audit/2013-March/msg00020.html
Typical output format would look something like:
type=SYSCALL msg=audit(1399651071.433:72): arch=c000003e syscall=272 success=yes exit=0 a0=40000000 a1=ffffffffffffffff a2=0 a3=22 items=0 ppid=1 pid=483 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="(t-daemon)" exe="/usr/lib/systemd/systemd" netns=97 utsns=2 ipcns=1 pidns=4 userns=3 mntns=5 subj=system_u:system_r:init_t:s0 key=(null)
The serial numbers are printed in hex.
--- include/linux/audit.h | 7 +++++++ kernel/audit.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 0 deletions(-)
include/linux/audit.h +7 -0
@@ -101,6 +101,13 @@ extern int __weak audit_classify_compat_syscall(int abi, unsigned syscall);struct filename ;extern void audit_log_session_info (struct audit_buffer *ab); +#ifdef CONFIG_NAMESPACES +extern void audit_log_namespace_info (struct audit_buffer *ab,struct task_struct *tsk); +#else +void audit_log_namespace_info (struct audit_buffer *ab,struct task_struct *tsk) +{ +} +#endif #ifdef CONFIG_AUDIT_COMPAT_GENERIC #define audit_is_compat (arch) (!((arch) & __AUDIT_ARCH_64BIT))
kernel/audit.c +38 -0
@@ -64,7 +64,15 @@#endif #include <linux/freezer.h> #include <linux/tty.h> +#include <linux/nsproxy.h> +#include <linux/utsname.h> +#include <linux/ipc_namespace.h> +#include "../fs/mount.h" +#include <linux/mount.h> +#include <linux/mnt_namespace.h> #include <linux/pid_namespace.h> +#include <net/net_namespace.h> +#include <linux/user_namespace.h> #include <net/netns/generic.h> #include "audit.h"
@@ -1617,6 +1625,35 @@ void audit_log_session_info(struct audit_buffer *ab)audit_log_format (ab," auid=%u ses=%u" , auid, sessionid); } +#ifdef CONFIG_NAMESPACES +void audit_log_namespace_info (struct audit_buffer *ab,struct task_struct *tsk) +{ +struct nsproxy *nsproxy; + +rcu_read_lock (); + nsproxy =task_nsproxy (tsk); +if (nsproxy !=NULL ) { +audit_log_format (ab," mntns=%llx" , nsproxy->mnt_ns->serial_num); +#ifdef CONFIG_NET_NS +audit_log_format (ab," netns=%llx" , nsproxy->net_ns->serial_num); +#endif +#ifdef CONFIG_UTS_NS +audit_log_format (ab," utsns=%llx" , nsproxy->uts_ns->serial_num); +#endif +#ifdef CONFIG_IPC_NS +audit_log_format (ab," ipcns=%llx" , nsproxy->ipc_ns->serial_num); +#endif + } +#ifdef CONFIG_PID_NS +audit_log_format (ab," pidns=%llx" ,task_active_pid_ns (tsk)->serial_num); +#endif +#ifdef CONFIG_USER_NS +audit_log_format (ab," userns=%llx" ,task_cred_xxx (tsk, user_ns)->serial_num); +#endif +rcu_read_unlock (); +} +#endif /* CONFIG_NAMESPACES */ +void audit_log_key (struct audit_buffer *ab,char *key) {audit_log_format (ab," key=" );
@@ -1861,6 +1898,7 @@ void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)up_read (&mm->mmap_sem); }else audit_log_format (ab," exe=(null)" ); +audit_log_namespace_info (ab, tsk);audit_log_task_context (ab); }EXPORT_SYMBOL (audit_log_task_info);
Signature · 1 line
1.7.1
Eric Paris
maintainerRe: [PATCH V2 2/6] audit: log namespace serial numbers
Quoted 42 lines · Richard Guy Briggs
Log the namespace serial numbers of a task in
audit_log_task_info()which is used by syscall audits, among others..Idea first presented:
https://www.redhat.com/archives/linux-audit/2013-March/msg00020.htmlTypical output format would look something like:
type=SYSCALL msg=audit(1399651071.433:72): arch=c000003e syscall=272 success=yes exit=0 a0=40000000 a1=ffffffffffffffff a2=0 a3=22 items=0 ppid=1 pid=483 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="(t-daemon)" exe="/usr/lib/systemd/systemd" netns=97 utsns=2 ipcns=1 pidns=4 userns=3 mntns=5 subj=system_u:system_r:init_t:s0 key=(null)The serial numbers are printed in hex.
Suggested-by Aristeu Rozanski <[email protected]>Signed-off-by Richard Guy Briggs <[email protected]>Acked-by Serge Hallyn <[email protected]>--- include/linux/audit.h | 7 +++++++ kernel/audit.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 0 deletions(-)include/linux/audit.h +7
-0@@ -101,6 +101,13 @@ extern int __weak audit_classify_compat_syscall(int abi, unsigned syscall);struct filename ;extern void audit_log_session_info (struct audit_buffer *ab); +#ifdef CONFIG_NAMESPACES +extern void audit_log_namespace_info (struct audit_buffer *ab,struct task_struct *tsk); +#else +void audit_log_namespace_info (struct audit_buffer *ab,struct task_struct *tsk) +{ +} +#endif #ifdef CONFIG_AUDIT_COMPAT_GENERIC #define audit_is_compat (arch) (!((arch) & __AUDIT_ARCH_64BIT))kernel/audit.c +0
-0
kernel/audit.c line 70
Quoted 7 lines
kernel/audit.c +4
-0@@ -64,7 +64,15 @@#endif #include <linux/freezer.h> #include <linux/tty.h> +#include <linux/nsproxy.h> +#include <linux/utsname.h> +#include <linux/ipc_namespace.h> +#include "../fs/mount.h"
I don't think such an include is ever a good idea and likely to get us SHOT by Viro...
Why do we need this include?
Quoted 52 lines
kernel/audit.c +34
-0+#include <linux/mount.h> +#include <linux/mnt_namespace.h> #include <linux/pid_namespace.h> +#include <net/net_namespace.h> +#include <linux/user_namespace.h> #include <net/netns/generic.h> #include "audit.h" @@ -1617,6 +1625,35 @@ void audit_log_session_info(struct audit_buffer *ab)audit_log_format (ab," auid=%u ses=%u" , auid, sessionid); } +#ifdef CONFIG_NAMESPACES +void audit_log_namespace_info (struct audit_buffer *ab,struct task_struct *tsk) +{ +struct nsproxy *nsproxy; + +rcu_read_lock (); + nsproxy =task_nsproxy (tsk); +if (nsproxy !=NULL ) { +audit_log_format (ab," mntns=%llx" , nsproxy->mnt_ns->serial_num); +#ifdef CONFIG_NET_NS +audit_log_format (ab," netns=%llx" , nsproxy->net_ns->serial_num); +#endif +#ifdef CONFIG_UTS_NS +audit_log_format (ab," utsns=%llx" , nsproxy->uts_ns->serial_num); +#endif +#ifdef CONFIG_IPC_NS +audit_log_format (ab," ipcns=%llx" , nsproxy->ipc_ns->serial_num); +#endif + } +#ifdef CONFIG_PID_NS +audit_log_format (ab," pidns=%llx" ,task_active_pid_ns (tsk)->serial_num); +#endif +#ifdef CONFIG_USER_NS +audit_log_format (ab," userns=%llx" ,task_cred_xxx (tsk, user_ns)->serial_num); +#endif +rcu_read_unlock (); +} +#endif /* CONFIG_NAMESPACES */ +void audit_log_key (struct audit_buffer *ab,char *key) {audit_log_format (ab," key=" );@@ -1861,6 +1898,7 @@ void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)up_read (&mm->mmap_sem); }else audit_log_format (ab," exe=(null)" ); +audit_log_namespace_info (ab, tsk);audit_log_task_context (ab); }EXPORT_SYMBOL (audit_log_task_info);
Re: [PATCH V2 2/6] audit: log namespace serial numbers
Quoted 45 lines · Eric Paris
Quoted 44 lines · Richard Guy Briggs
Log the namespace serial numbers of a task in
audit_log_task_info()which is used by syscall audits, among others..Idea first presented:
https://www.redhat.com/archives/linux-audit/2013-March/msg00020.htmlTypical output format would look something like:
type=SYSCALL msg=audit(1399651071.433:72): arch=c000003e syscall=272 success=yes exit=0 a0=40000000 a1=ffffffffffffffff a2=0 a3=22 items=0 ppid=1 pid=483 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="(t-daemon)" exe="/usr/lib/systemd/systemd" netns=97 utsns=2 ipcns=1 pidns=4 userns=3 mntns=5 subj=system_u:system_r:init_t:s0 key=(null)The serial numbers are printed in hex.
Suggested-by Aristeu Rozanski <[email protected]>Signed-off-by Richard Guy Briggs <[email protected]>Acked-by Serge Hallyn <[email protected]>--- include/linux/audit.h | 7 +++++++ kernel/audit.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 0 deletions(-)include/linux/audit.h +7
-0@@ -101,6 +101,13 @@ extern int __weak audit_classify_compat_syscall(int abi, unsigned syscall);struct filename ;extern void audit_log_session_info (struct audit_buffer *ab); +#ifdef CONFIG_NAMESPACES +extern void audit_log_namespace_info (struct audit_buffer *ab,struct task_struct *tsk); +#else +void audit_log_namespace_info (struct audit_buffer *ab,struct task_struct *tsk) +{ +} +#endif #ifdef CONFIG_AUDIT_COMPAT_GENERIC #define audit_is_compat (arch) (!((arch) & __AUDIT_ARCH_64BIT))kernel/audit.c +0
-0@@ -64,7 +64,15 @@#endif #include <linux/freezer.h>
Quoted 8 lines
kernel/audit.c line 70
Quoted 5 lines
kernel/audit.c +4
-0#include <linux/tty.h> +#include <linux/nsproxy.h> +#include <linux/utsname.h> +#include <linux/ipc_namespace.h> +#include "../fs/mount.h" I don't think such an include is ever a good idea and likely to get us SHOT by Viro...
Which is why patch 6 would replace it, but that depends on getting buy-in to expose ns_entries in patch 5.
Quoted 1 line
Why do we need this include?
For "struct mnt_namespace", to get access to the serial number.
I agree. It is ugly. In fact, it is the one below that isn't necessary since it is pulled in by ../fs/mount.h
Quoted 44 lines
Quoted 44 lines
kernel/audit.c +33
-0+#include <linux/mount.h> +#include <linux/mnt_namespace.h> #include <linux/pid_namespace.h> +#include <net/net_namespace.h> +#include <linux/user_namespace.h> #include <net/netns/generic.h> #include "audit.h" @@ -1617,6 +1625,35 @@ void audit_log_session_info(struct audit_buffer *ab)audit_log_format (ab," auid=%u ses=%u" , auid, sessionid); } +#ifdef CONFIG_NAMESPACES +void audit_log_namespace_info (struct audit_buffer *ab,struct task_struct *tsk) +{ +struct nsproxy *nsproxy; + +rcu_read_lock (); + nsproxy =task_nsproxy (tsk); +if (nsproxy !=NULL ) { +audit_log_format (ab," mntns=%llx" , nsproxy->mnt_ns->serial_num); +#ifdef CONFIG_NET_NS +audit_log_format (ab," netns=%llx" , nsproxy->net_ns->serial_num); +#endif +#ifdef CONFIG_UTS_NS +audit_log_format (ab," utsns=%llx" , nsproxy->uts_ns->serial_num); +#endif +#ifdef CONFIG_IPC_NS +audit_log_format (ab," ipcns=%llx" , nsproxy->ipc_ns->serial_num); +#endif + } +#ifdef CONFIG_PID_NS +audit_log_format (ab," pidns=%llx" ,task_active_pid_ns (tsk)->serial_num); +#endif +#ifdef CONFIG_USER_NS +audit_log_format (ab," userns=%llx" ,task_cred_xxx (tsk, user_ns)->serial_num); +#endif +rcu_read_unlock (); +} +#endif /* CONFIG_NAMESPACES */ +void audit_log_key (struct audit_buffer *ab,char *key) {audit_log_format (ab," key=" );
Quoted 10 lines
kernel/audit.c void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk) · line 1904
Quoted 8 lines
kernel/audit.c +1
-0@@ -1861,6 +1898,7 @@ void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)up_read (&mm->mmap_sem); }else audit_log_format (ab," exe=(null)" ); +audit_log_namespace_info (ab, tsk);audit_log_task_context (ab); }EXPORT_SYMBOL (audit_log_task_info);
- RGB
--
Richard Guy Briggs <[email protected]> Senior Software Engineer, Kernel Security, AMER ENG Base Operating Systems, Red Hat Remote, Ottawa, Canada
Voice: +1.647.777.2635, Internal: (81) 32635, Alt: +1.613.693.0684x3545
Eric Paris
maintainerRe: [PATCH V2 2/6] audit: log namespace serial numbers
In reply to Richard Guy BriggsOn Fri, 2014-05-09 at 20:27 -0400, Richard Guy Briggs wrote:
Not so relevant because you delete all of this code later... But still...
kernel/audit.c void audit_log_session_info(struct audit_buffer *ab) · line 1633
Quoted 6 lines
kernel/audit.c +6
-0+#ifdef CONFIG_NAMESPACES +void audit_log_namespace_info (struct audit_buffer *ab,struct task_struct *tsk) +{ +struct nsproxy *nsproxy; + +rcu_read_lock ();
ok, so we are under rcu_read_lock() and cannot sleep
kernel/audit.c void audit_log_session_info(struct audit_buffer *ab) · line 1636
Quoted 3 lines
kernel/audit.c +3
-0+ nsproxy =task_nsproxy (tsk); +if (nsproxy !=NULL ) { +audit_log_format (ab," mntns=%llx" , nsproxy->mnt_ns->serial_num);
But this could do an allocation, are we sure that everything used GFP_ATOMIC when creating the audit buffer? [hint: it doesn't]
Re: [PATCH V2 2/6] audit: log namespace serial numbers
Quoted 14 lines · Eric Paris
On Fri, 2014-05-09 at 20:27 -0400, Richard Guy Briggs wrote:
Not so relevant because you delete all of this code later... But still...
kernel/audit.c void audit_log_session_info(struct audit_buffer *ab) · line 1633
Quoted 6 lines
kernel/audit.c +6
-0+#ifdef CONFIG_NAMESPACES +void audit_log_namespace_info (struct audit_buffer *ab,struct task_struct *tsk) +{ +struct nsproxy *nsproxy; + +rcu_read_lock ();ok, so we are under
rcu_read_lock()and cannot sleep
Quoted 6 lines
kernel/audit.c void audit_log_session_info(struct audit_buffer *ab) · line 1636
Quoted 3 lines
kernel/audit.c +3
-0+ nsproxy =task_nsproxy (tsk); +if (nsproxy !=NULL ) { +audit_log_format (ab," mntns=%llx" , nsproxy->mnt_ns->serial_num);But this could do an allocation, are we sure that everything used
GFP_ATOMICwhen creating the audit buffer? [hint: it doesn't]
Ugh, missed that. Even more reason to go with the ns_entries export.
- RGB
--
Richard Guy Briggs <[email protected]> Senior Software Engineer, Kernel Security, AMER ENG Base Operating Systems, Red Hat Remote, Ottawa, Canada
Voice: +1.647.777.2635, Internal: (81) 32635, Alt: +1.613.693.0684x3545
PATCH v2 3/6 namespaces: expose namespace instance serial number in proc_ns_operations
In reply to Richard Guy BriggsExpose the namespace instance serial number for each namespace type in the proc namespace operations structure to make it available for the proc filesystem.
--- fs/namespace.c | 7 +++++++ include/linux/proc_ns.h | 1 + ipc/namespace.c | 8 ++++++++ kernel/pid_namespace.c | 7 +++++++ kernel/user_namespace.c | 7 +++++++ kernel/utsname.c | 8 ++++++++ net/core/net_namespace.c | 7 +++++++ 7 files changed, 45 insertions(+), 0 deletions(-)
fs/namespace.c +7 -0
@@ -3014,6 +3014,12 @@ static unsigned int mntns_inum(void *ns)return mnt_ns->proc_inum; } +static unsigned long long mntns_snum (void *ns) +{ +struct mnt_namespace *mnt_ns = ns; +return mnt_ns->serial_num; +} +const struct proc_ns_operations mntns_operations = { .name ="mnt" , .type =CLONE_NEWNS ,
@@ -3021,4 +3027,5 @@ const struct proc_ns_operations mntns_operations = { .put = mntns_put, .install = mntns_install, .inum = mntns_inum, + .snum = mntns_snum, };
include/linux/proc_ns.h +1 -0
@@ -14,6 +14,7 @@ struct proc_ns_operations {void (*put)(void *ns);int (*install)(struct nsproxy *nsproxy,void *ns);unsigned int (*inum)(void *ns); +unsigned long long (*snum)(void *ns); };struct proc_ns {
ipc/namespace.c +8 -0
@@ -191,6 +191,13 @@ static unsigned int ipcns_inum(void *vp)return ns->proc_inum; } +static unsigned long long ipcns_snum (void *vp) +{ +struct ipc_namespace *ns = vp; + +return ns->serial_num; +} +const struct proc_ns_operations ipcns_operations = { .name ="ipc" , .type =CLONE_NEWIPC ,
@@ -198,4 +205,5 @@ const struct proc_ns_operations ipcns_operations = { .put = ipcns_put, .install = ipcns_install, .inum = ipcns_inum, + .snum = ipcns_snum, };
kernel/pid_namespace.c +7 -0
@@ -368,6 +368,12 @@ static unsigned int pidns_inum(void *ns)return pid_ns->proc_inum; } +static unsigned long long pidns_snum (void *ns) +{ +struct pid_namespace *pid_ns = ns; +return pid_ns->serial_num; +} +const struct proc_ns_operations pidns_operations = { .name ="pid" , .type =CLONE_NEWPID ,
@@ -375,6 +381,7 @@ const struct proc_ns_operations pidns_operations = { .put = pidns_put, .install = pidns_install, .inum = pidns_inum, + .snum = pidns_snum, };static __init int pid_namespaces_init (void )
kernel/user_namespace.c +7 -0
@@ -890,6 +890,12 @@ static unsigned int userns_inum(void *ns)return user_ns->proc_inum; } +static unsigned long long userns_snum (void *ns) +{ +struct user_namespace *user_ns = ns; +return user_ns->serial_num; +} +const struct proc_ns_operations userns_operations = { .name ="user" , .type =CLONE_NEWUSER ,
@@ -897,6 +903,7 @@ const struct proc_ns_operations userns_operations = { .put = userns_put, .install = userns_install, .inum = userns_inum, + .snum = userns_snum, };static __init int user_namespaces_init (void )
kernel/utsname.c +8 -0
@@ -132,6 +132,13 @@ static unsigned int utsns_inum(void *vp)return ns->proc_inum; } +static unsigned long long utsns_snum (void *vp) +{ +struct uts_namespace *ns = vp; + +return ns->serial_num; +} +const struct proc_ns_operations utsns_operations = { .name ="uts" , .type =CLONE_NEWUTS ,
@@ -139,4 +146,5 @@ const struct proc_ns_operations utsns_operations = { .put = utsns_put, .install = utsns_install, .inum = utsns_inum, + .snum = utsns_snum, };
net/core/net_namespace.c +7 -0
@@ -667,6 +667,12 @@ static unsigned int netns_inum(void *ns)return net->proc_inum; } +static unsigned long long netns_snum (void *ns) +{ +struct net *net = ns; +return net->serial_num; +} +const struct proc_ns_operations netns_operations = { .name ="net" , .type =CLONE_NEWNET ,
@@ -674,5 +680,6 @@ const struct proc_ns_operations netns_operations = { .put = netns_put, .install = netns_install, .inum = netns_inum, + .snum = netns_snum, };#endif
Signature · 1 line
1.7.1
PATCH v2 4/6 namespaces: expose ns instance serial numbers in proc
In reply to Richard Guy BriggsExpose the namespace instace serial numbers in the proc filesystem at /proc/<pid>/ns/<ns>_snum. The link text gives the serial number in hex.
"snum" was chosen instead of "seq" for consistency with inum and there are a number of other uses of "seq" in the namespace code.
---
Although it works as expected, I'm not that happy with this patch because it duplicates a lot of code, including minor changes to proc_ns_follow_link(), proc_ns_readlink(), ns_dname() that will be harder to keep consistent. The only way I could see to get that information into those functions would be through dentry. Maybe the information needed is already in there in d_name or d_iname? Or I could add a flag to d_flags (there are 9 bits unused?) but that flag isn't useful for any other types of entries, so I'm not so keen to pollute it.
--- fs/proc/namespaces.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 138 insertions(+), 12 deletions(-)
fs/proc/namespaces.c +138 -12
@@ -42,6 +42,11 @@ static const struct inode_operations ns_inode_operations = { .setattr = proc_setattr, }; +enum ns_num { +PROC_NS_INUM , +PROC_NS_SNUM +}; +static char *ns_dname (struct dentry *dentry,char *buffer,int buflen) {struct inode *inode = dentry->d_inode;
@@ -51,14 +56,28 @@ static char *ns_dname(struct dentry *dentry, char *buffer, int buflen) ns_ops->name, inode->i_ino); } -const struct dentry_operations ns_dentry_operations = -{ +const struct dentry_operations ns_dentry_operations = { .d_delete = always_delete_dentry, .d_dname = ns_dname, }; +static char *ns_snum_dname (struct dentry *dentry,char *buffer,int buflen) +{ +struct inode *inode = dentry->d_inode; +const struct proc_ns_operations *ns_ops =PROC_I (inode)->ns.ns_ops; + +return dynamic_dname (dentry, buffer, buflen,"%s_snum:[%llx]" , + ns_ops->name, ns_ops->snum (PROC_I (inode)->ns.ns)); +} + +const struct dentry_operations ns_snum_dentry_operations = { + .d_delete = always_delete_dentry, + .d_dname = ns_snum_dname, +}; +static struct dentry *proc_ns_get_dentry (struct super_block *sb, -struct task_struct *task,const struct proc_ns_operations *ns_ops) +struct task_struct *task,const struct proc_ns_operations *ns_ops, +enum ns_num ns_num) {struct dentry *dentry, *result;struct inode *inode;
@@ -96,7 +115,10 @@ static struct dentry *proc_ns_get_dentry(struct super_block *sb, ns_ops->put (ns); } -d_set_d_op (dentry, &ns_dentry_operations); +if (ns_num ==PROC_NS_INUM ) +d_set_d_op (dentry, &ns_dentry_operations); +else if (ns_num ==PROC_NS_SNUM ) +d_set_d_op (dentry, &ns_snum_dentry_operations); result =d_instantiate_unique (dentry, inode);if (result) {dput (dentry);
@@ -122,7 +144,39 @@ static void *proc_ns_follow_link(struct dentry *dentry, struct nameidata *nd)if (!ptrace_may_access (task,PTRACE_MODE_READ )) goto out_put_task; - ns_path.dentry =proc_ns_get_dentry (sb, task, ei->ns.ns_ops); + ns_path.dentry =proc_ns_get_dentry (sb, task, ei->ns.ns_ops,PROC_NS_INUM ); +if (IS_ERR (ns_path.dentry)) { + error =ERR_CAST (ns_path.dentry); + goto out_put_task; + } + + ns_path.mnt =mntget (nd->path.mnt); +nd_jump_link (nd, &ns_path); + error =NULL ; + +out_put_task: +put_task_struct (task); +out: +return error; +} + +static void *proc_ns_snum_follow_link (struct dentry *dentry,struct nameidata *nd) +{ +struct inode *inode = dentry->d_inode; +struct super_block *sb = inode->i_sb; +struct proc_inode *ei =PROC_I (inode); +struct task_struct *task; +struct path ns_path; +void *error =ERR_PTR (-EACCES ); + + task =get_proc_task (inode); +if (!task) + goto out; + +if (!ptrace_may_access (task,PTRACE_MODE_READ )) + goto out_put_task; + + ns_path.dentry =proc_ns_get_dentry (sb, task, ei->ns.ns_ops,PROC_NS_SNUM );if (IS_ERR (ns_path.dentry)) { error =ERR_CAST (ns_path.dentry); goto out_put_task;
@@ -181,10 +235,59 @@ static const struct inode_operations proc_ns_link_inode_operations = { .setattr = proc_setattr, }; +static int proc_ns_snum_readlink (struct dentry *dentry,char __user *buffer,int buflen) +{ +struct inode *inode = dentry->d_inode; +struct proc_inode *ei =PROC_I (inode); +const struct proc_ns_operations *ns_ops = ei->ns.ns_ops; +struct task_struct *task; +void *ns; +char name[50 ]; +int len = -EACCES ; + + task =get_proc_task (inode); +if (!task) + goto out; + +if (!ptrace_may_access (task,PTRACE_MODE_READ )) + goto out_put_task; + + len = -ENOENT ; + ns = ns_ops->get (task); +if (!ns) + goto out_put_task; + +snprintf (name,sizeof (name),"%s_snum:[%llx]" , ns_ops->name, ns_ops->snum (ns)); + len =strlen (name); + +if (len > buflen) + len = buflen; +if (copy_to_user (buffer, name, len)) + len = -EFAULT ; + + ns_ops->put (ns); +out_put_task: +put_task_struct (task); +out: +return len; +} + +static const struct inode_operations proc_ns_snum_link_inode_operations = { + .readlink = proc_ns_snum_readlink, + .follow_link = proc_ns_snum_follow_link, + .setattr = proc_setattr, +}; + +struct proc_ns_entry { +const struct proc_ns_operations *ns_ops; +enum ns_num ns_num; +}; +static int proc_ns_instantiate (struct inode *dir,struct dentry *dentry,struct task_struct *task,const void *ptr) { -const struct proc_ns_operations *ns_ops = ptr; +const struct proc_ns_entry *ns_entry = ptr; +const struct proc_ns_operations *ns_ops = ns_entry->ns_ops;struct inode *inode;struct proc_inode *ei;
@@ -194,7 +297,10 @@ static int proc_ns_instantiate(struct inode *dir, ei =PROC_I (inode); inode->i_mode =S_IFLNK |S_IRWXUGO ; - inode->i_op = &proc_ns_link_inode_operations; +if (ns_entry->ns_num ==PROC_NS_INUM ) + inode->i_op = &proc_ns_link_inode_operations; +else if (ns_entry->ns_num ==PROC_NS_SNUM ) + inode->i_op = &proc_ns_snum_link_inode_operations; ei->ns.ns_ops = ns_ops;d_set_d_op (dentry, &pid_dentry_operations);
@@ -216,14 +322,23 @@ static int proc_ns_dir_readdir(struct file *file, struct dir_context *ctx)if (!dir_emit_dots (file, ctx)) goto out; -if (ctx->pos >=2 +ARRAY_SIZE (ns_entries)) +if (ctx->pos >=2 +2 *ARRAY_SIZE (ns_entries)) goto out; entry = ns_entries + (ctx->pos -2 ); last = &ns_entries[ARRAY_SIZE (ns_entries) -1 ];while (entry <= last) {const struct proc_ns_operations *ops = *entry; +char name[50 ]; +struct proc_ns_entry ns_entry = { ops,PROC_NS_INUM }; +if (!proc_fill_cache (file, ctx, ops->name,strlen (ops->name), - proc_ns_instantiate, task, ops)) + proc_ns_instantiate, task, &ns_entry)) +break ; + ctx->pos++; + ns_entry.ns_num =PROC_NS_SNUM ; +snprintf (name,sizeof (name),"%s_snum" , ops->name); +if (!proc_fill_cache (file, ctx, name,strlen (name), + proc_ns_instantiate, task, &ns_entry))break ; ctx->pos++; entry++;
@@ -245,6 +360,7 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir,struct task_struct *task =get_proc_task (dir);const struct proc_ns_operations **entry, **last;unsigned int len = dentry->d_name.len; +struct proc_ns_entry ns_entry; error = -ENOENT ;
@@ -253,15 +369,25 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir, last = &ns_entries[ARRAY_SIZE (ns_entries)];for (entry = ns_entries; entry < last; entry++) { -if (strlen ((*entry)->name) != len) +char name[50 ]; + +snprintf (name,sizeof (name),"%s_snum" , (*entry)->name); +if (strlen ((*entry)->name) != len &&strlen (name) != len)continue ; -if (!memcmp (dentry->d_name.name, (*entry)->name, len)) +if (!memcmp (dentry->d_name.name, (*entry)->name, len)) { + ns_entry.ns_ops = *entry; + ns_entry.ns_num =PROC_NS_INUM ; +break ; + }else if (!memcmp (dentry->d_name.name, name, len)) { + ns_entry.ns_ops = *entry; + ns_entry.ns_num =PROC_NS_SNUM ;break ; + } }if (entry == last) goto out; - error =proc_ns_instantiate (dir, dentry, task, *entry); + error =proc_ns_instantiate (dir, dentry, task, &ns_entry); out:put_task_struct (task); out_no_task:
Signature · 1 line
1.7.1
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH v2 4/6 namespaces: expose ns instance serial numbers in proc
Expose the namespace instace serial numbers in the proc filesystem at /proc/<pid>/ns/<ns>_snum. The link text gives the serial number in hex.
"snum" was chosen instead of "seq" for consistency with inum and there are a number of other uses of "seq" in the namespace code.
---
Although it works as expected, I'm not that happy with this patch because it duplicates a lot of code, including minor changes to proc_ns_follow_link(), proc_ns_readlink(), ns_dname() that will be harder to keep consistent. The only way I could see to get that information into those functions would be through dentry. Maybe the information needed is already in there in d_name or d_iname? Or I could add a flag to d_flags (there are 9 bits unused?) but that flag isn't useful for any other types of entries, so I'm not so keen to pollute it.
--- fs/proc/namespaces.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 138 insertions(+), 12 deletions(-)
fs/proc/namespaces.c +138 -12
@@ -42,6 +42,11 @@ static const struct inode_operations ns_inode_operations = { .setattr = proc_setattr, }; +enum ns_num { +PROC_NS_INUM , +PROC_NS_SNUM +}; +static char *ns_dname (struct dentry *dentry,char *buffer,int buflen) {struct inode *inode = dentry->d_inode;
@@ -51,14 +56,28 @@ static char *ns_dname(struct dentry *dentry, char *buffer, int buflen) ns_ops->name, inode->i_ino); } -const struct dentry_operations ns_dentry_operations = -{ +const struct dentry_operations ns_dentry_operations = { .d_delete = always_delete_dentry, .d_dname = ns_dname, }; +static char *ns_snum_dname (struct dentry *dentry,char *buffer,int buflen) +{ +struct inode *inode = dentry->d_inode; +const struct proc_ns_operations *ns_ops =PROC_I (inode)->ns.ns_ops; + +return dynamic_dname (dentry, buffer, buflen,"%s_snum:[%llx]" , + ns_ops->name, ns_ops->snum (PROC_I (inode)->ns.ns)); +} + +const struct dentry_operations ns_snum_dentry_operations = { + .d_delete = always_delete_dentry, + .d_dname = ns_snum_dname, +}; +static struct dentry *proc_ns_get_dentry (struct super_block *sb, -struct task_struct *task,const struct proc_ns_operations *ns_ops) +struct task_struct *task,const struct proc_ns_operations *ns_ops, +enum ns_num ns_num) {struct dentry *dentry, *result;struct inode *inode;
@@ -96,7 +115,10 @@ static struct dentry *proc_ns_get_dentry(struct super_block *sb, ns_ops->put (ns); } -d_set_d_op (dentry, &ns_dentry_operations); +if (ns_num ==PROC_NS_INUM ) +d_set_d_op (dentry, &ns_dentry_operations); +else if (ns_num ==PROC_NS_SNUM ) +d_set_d_op (dentry, &ns_snum_dentry_operations); result =d_instantiate_unique (dentry, inode);if (result) {dput (dentry);
@@ -122,7 +144,39 @@ static void *proc_ns_follow_link(struct dentry *dentry, struct nameidata *nd)if (!ptrace_may_access (task,PTRACE_MODE_READ )) goto out_put_task; - ns_path.dentry =proc_ns_get_dentry (sb, task, ei->ns.ns_ops); + ns_path.dentry =proc_ns_get_dentry (sb, task, ei->ns.ns_ops,PROC_NS_INUM ); +if (IS_ERR (ns_path.dentry)) { + error =ERR_CAST (ns_path.dentry); + goto out_put_task; + } + + ns_path.mnt =mntget (nd->path.mnt); +nd_jump_link (nd, &ns_path); + error =NULL ; + +out_put_task: +put_task_struct (task); +out: +return error; +} + +static void *proc_ns_snum_follow_link (struct dentry *dentry,struct nameidata *nd) +{ +struct inode *inode = dentry->d_inode; +struct super_block *sb = inode->i_sb; +struct proc_inode *ei =PROC_I (inode); +struct task_struct *task; +struct path ns_path; +void *error =ERR_PTR (-EACCES ); + + task =get_proc_task (inode); +if (!task) + goto out; + +if (!ptrace_may_access (task,PTRACE_MODE_READ )) + goto out_put_task; + + ns_path.dentry =proc_ns_get_dentry (sb, task, ei->ns.ns_ops,PROC_NS_SNUM );if (IS_ERR (ns_path.dentry)) { error =ERR_CAST (ns_path.dentry); goto out_put_task;
@@ -181,10 +235,59 @@ static const struct inode_operations proc_ns_link_inode_operations = { .setattr = proc_setattr, }; +static int proc_ns_snum_readlink (struct dentry *dentry,char __user *buffer,int buflen) +{ +struct inode *inode = dentry->d_inode; +struct proc_inode *ei =PROC_I (inode); +const struct proc_ns_operations *ns_ops = ei->ns.ns_ops; +struct task_struct *task; +void *ns; +char name[50 ]; +int len = -EACCES ; + + task =get_proc_task (inode); +if (!task) + goto out; + +if (!ptrace_may_access (task,PTRACE_MODE_READ )) + goto out_put_task; + + len = -ENOENT ; + ns = ns_ops->get (task); +if (!ns) + goto out_put_task; + +snprintf (name,sizeof (name),"%s_snum:[%llx]" , ns_ops->name, ns_ops->snum (ns)); + len =strlen (name); + +if (len > buflen) + len = buflen; +if (copy_to_user (buffer, name, len)) + len = -EFAULT ; + + ns_ops->put (ns); +out_put_task: +put_task_struct (task); +out: +return len; +} + +static const struct inode_operations proc_ns_snum_link_inode_operations = { + .readlink = proc_ns_snum_readlink, + .follow_link = proc_ns_snum_follow_link, + .setattr = proc_setattr, +}; + +struct proc_ns_entry { +const struct proc_ns_operations *ns_ops; +enum ns_num ns_num; +}; +static int proc_ns_instantiate (struct inode *dir,struct dentry *dentry,struct task_struct *task,const void *ptr) { -const struct proc_ns_operations *ns_ops = ptr; +const struct proc_ns_entry *ns_entry = ptr; +const struct proc_ns_operations *ns_ops = ns_entry->ns_ops;struct inode *inode;struct proc_inode *ei;
@@ -194,7 +297,10 @@ static int proc_ns_instantiate(struct inode *dir, ei =PROC_I (inode); inode->i_mode =S_IFLNK |S_IRWXUGO ; - inode->i_op = &proc_ns_link_inode_operations; +if (ns_entry->ns_num ==PROC_NS_INUM ) + inode->i_op = &proc_ns_link_inode_operations; +else if (ns_entry->ns_num ==PROC_NS_SNUM ) + inode->i_op = &proc_ns_snum_link_inode_operations; ei->ns.ns_ops = ns_ops;d_set_d_op (dentry, &pid_dentry_operations);
@@ -216,14 +322,23 @@ static int proc_ns_dir_readdir(struct file *file, struct dir_context *ctx)if (!dir_emit_dots (file, ctx)) goto out; -if (ctx->pos >=2 +ARRAY_SIZE (ns_entries)) +if (ctx->pos >=2 +2 *ARRAY_SIZE (ns_entries)) goto out; entry = ns_entries + (ctx->pos -2 ); last = &ns_entries[ARRAY_SIZE (ns_entries) -1 ];while (entry <= last) {const struct proc_ns_operations *ops = *entry; +char name[50 ]; +struct proc_ns_entry ns_entry = { ops,PROC_NS_INUM }; +if (!proc_fill_cache (file, ctx, ops->name,strlen (ops->name), - proc_ns_instantiate, task, ops)) + proc_ns_instantiate, task, &ns_entry)) +break ; + ctx->pos++; + ns_entry.ns_num =PROC_NS_SNUM ; +snprintf (name,sizeof (name),"%s_snum" , ops->name); +if (!proc_fill_cache (file, ctx, name,strlen (name), + proc_ns_instantiate, task, &ns_entry))break ; ctx->pos++; entry++;
@@ -245,6 +360,7 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir,struct task_struct *task =get_proc_task (dir);const struct proc_ns_operations **entry, **last;unsigned int len = dentry->d_name.len; +struct proc_ns_entry ns_entry; error = -ENOENT ;
@@ -253,15 +369,25 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir, last = &ns_entries[ARRAY_SIZE (ns_entries)];for (entry = ns_entries; entry < last; entry++) { -if (strlen ((*entry)->name) != len) +char name[50 ]; + +snprintf (name,sizeof (name),"%s_snum" , (*entry)->name); +if (strlen ((*entry)->name) != len &&strlen (name) != len)continue ; -if (!memcmp (dentry->d_name.name, (*entry)->name, len)) +if (!memcmp (dentry->d_name.name, (*entry)->name, len)) { + ns_entry.ns_ops = *entry; + ns_entry.ns_num =PROC_NS_INUM ; +break ; + }else if (!memcmp (dentry->d_name.name, name, len)) { + ns_entry.ns_ops = *entry; + ns_entry.ns_num =PROC_NS_SNUM ;break ; + } }if (entry == last) goto out; - error =proc_ns_instantiate (dir, dentry, task, *entry); + error =proc_ns_instantiate (dir, dentry, task, &ns_entry); out:put_task_struct (task); out_no_task:
Signature · 1 line
1.7.1
PATCH v2 5/6 namespaces: expose ns_entries
In reply to a message not in the archiveExpose ns_entries so subsystems other than proc can use this set of namespace operations.
--- fs/proc/namespaces.c | 2 +- include/linux/proc_ns.h | 1 + 2 files changed, 2 insertions(+), 1 deletions(-)
fs/proc/namespaces.c +1 -1
@@ -15,7 +15,7 @@#include "internal.h" -static const struct proc_ns_operations *ns_entries[] = { +const struct proc_ns_operations *ns_entries[] = { #ifdef CONFIG_NET_NS &netns_operations, #endif
include/linux/proc_ns.h +1 -0
@@ -28,6 +28,7 @@ extern const struct proc_ns_operations ipcns_operations;extern const struct proc_ns_operations pidns_operations;extern const struct proc_ns_operations userns_operations;extern const struct proc_ns_operations mntns_operations; +extern const struct proc_ns_operations *ns_entries[];/* * We always define these enumerators
Signature · 1 line
1.7.1
PATCH v2 6/6 audit: convert namespace serial number logging to use proc ns_entries
In reply to a message not in the archiveConvert audit direct access to namespace serial numbers to insead use the proc namespace operations structure which lists all namespace types with a consistent api.
This should eventually squash down to: [audit: log namespace serial numbers] (which in turn will need to follow [namespaces: expose ns_entries])
--- kernel/audit.c | 43 ++++++++++++------------------------------- 1 files changed, 12 insertions(+), 31 deletions(-)
kernel/audit.c +12 -31
@@ -64,15 +64,7 @@#endif #include <linux/freezer.h> #include <linux/tty.h> -#include <linux/nsproxy.h> -#include <linux/utsname.h> -#include <linux/ipc_namespace.h> -#include "../fs/mount.h" -#include <linux/mount.h> -#include <linux/mnt_namespace.h> -#include <linux/pid_namespace.h> -#include <net/net_namespace.h> -#include <linux/user_namespace.h> +#include <linux/proc_ns.h> #include <net/netns/generic.h> #include "audit.h"
@@ -1628,29 +1620,18 @@ void audit_log_session_info(struct audit_buffer *ab)#ifdef CONFIG_NAMESPACES void audit_log_namespace_info (struct audit_buffer *ab,struct task_struct *tsk) { -struct nsproxy *nsproxy; - -rcu_read_lock (); - nsproxy =task_nsproxy (tsk); -if (nsproxy !=NULL ) { -audit_log_format (ab," mntns=%llx" , nsproxy->mnt_ns->serial_num); -#ifdef CONFIG_NET_NS -audit_log_format (ab," netns=%llx" , nsproxy->net_ns->serial_num); -#endif -#ifdef CONFIG_UTS_NS -audit_log_format (ab," utsns=%llx" , nsproxy->uts_ns->serial_num); -#endif -#ifdef CONFIG_IPC_NS -audit_log_format (ab," ipcns=%llx" , nsproxy->ipc_ns->serial_num); -#endif +const struct proc_ns_operations **entry; +bool end = false; + +if (!tsk) +return ; +for (entry = ns_entries; !end; entry++) { +void *ns = (*entry)->get (tsk); +audit_log_format (ab," %sns=%llx" , (*entry)->name, + (*entry)->snum (ns)); + (*entry)->put (ns); + end = (*entry)->type ==CLONE_NEWNS ; } -#ifdef CONFIG_PID_NS -audit_log_format (ab," pidns=%llx" ,task_active_pid_ns (tsk)->serial_num); -#endif -#ifdef CONFIG_USER_NS -audit_log_format (ab," userns=%llx" ,task_cred_xxx (tsk, user_ns)->serial_num); -#endif -rcu_read_unlock (); }#endif /* CONFIG_NAMESPACES */
Signature · 1 line
1.7.1
PATCH v2 5/6 namespaces: expose ns_entries
In reply to Richard Guy BriggsExpose ns_entries so subsystems other than proc can use this set of namespace operations.
--- fs/proc/namespaces.c | 2 +- include/linux/proc_ns.h | 1 + 2 files changed, 2 insertions(+), 1 deletions(-)
fs/proc/namespaces.c +1 -1
@@ -15,7 +15,7 @@#include "internal.h" -static const struct proc_ns_operations *ns_entries[] = { +const struct proc_ns_operations *ns_entries[] = { #ifdef CONFIG_NET_NS &netns_operations, #endif
include/linux/proc_ns.h +1 -0
@@ -28,6 +28,7 @@ extern const struct proc_ns_operations ipcns_operations;extern const struct proc_ns_operations pidns_operations;extern const struct proc_ns_operations userns_operations;extern const struct proc_ns_operations mntns_operations; +extern const struct proc_ns_operations *ns_entries[];/* * We always define these enumerators
Signature · 1 line
1.7.1
PATCH v2 6/6 audit: convert namespace serial number logging to use proc ns_entries
In reply to Richard Guy BriggsConvert audit direct access to namespace serial numbers to insead use the proc namespace operations structure which lists all namespace types with a consistent api.
This should eventually squash down to: [audit: log namespace serial numbers] (which in turn will need to follow [namespaces: expose ns_entries])
--- kernel/audit.c | 43 ++++++++++++------------------------------- 1 files changed, 12 insertions(+), 31 deletions(-)
kernel/audit.c +12 -31
@@ -64,15 +64,7 @@#endif #include <linux/freezer.h> #include <linux/tty.h> -#include <linux/nsproxy.h> -#include <linux/utsname.h> -#include <linux/ipc_namespace.h> -#include "../fs/mount.h" -#include <linux/mount.h> -#include <linux/mnt_namespace.h> -#include <linux/pid_namespace.h> -#include <net/net_namespace.h> -#include <linux/user_namespace.h> +#include <linux/proc_ns.h> #include <net/netns/generic.h> #include "audit.h"
@@ -1628,29 +1620,18 @@ void audit_log_session_info(struct audit_buffer *ab)#ifdef CONFIG_NAMESPACES void audit_log_namespace_info (struct audit_buffer *ab,struct task_struct *tsk) { -struct nsproxy *nsproxy; - -rcu_read_lock (); - nsproxy =task_nsproxy (tsk); -if (nsproxy !=NULL ) { -audit_log_format (ab," mntns=%llx" , nsproxy->mnt_ns->serial_num); -#ifdef CONFIG_NET_NS -audit_log_format (ab," netns=%llx" , nsproxy->net_ns->serial_num); -#endif -#ifdef CONFIG_UTS_NS -audit_log_format (ab," utsns=%llx" , nsproxy->uts_ns->serial_num); -#endif -#ifdef CONFIG_IPC_NS -audit_log_format (ab," ipcns=%llx" , nsproxy->ipc_ns->serial_num); -#endif +const struct proc_ns_operations **entry; +bool end = false; + +if (!tsk) +return ; +for (entry = ns_entries; !end; entry++) { +void *ns = (*entry)->get (tsk); +audit_log_format (ab," %sns=%llx" , (*entry)->name, + (*entry)->snum (ns)); + (*entry)->put (ns); + end = (*entry)->type ==CLONE_NEWNS ; } -#ifdef CONFIG_PID_NS -audit_log_format (ab," pidns=%llx" ,task_active_pid_ns (tsk)->serial_num); -#endif -#ifdef CONFIG_USER_NS -audit_log_format (ab," userns=%llx" ,task_cred_xxx (tsk, user_ns)->serial_num); -#endif -rcu_read_unlock (); }#endif /* CONFIG_NAMESPACES */
Signature · 1 line
1.7.1