From: Maneesh Soni <maneesh@in.ibm.com>
To: "Timothy R. Chavez" <tinytim@us.ibm.com>
Cc: linux-fsdevel@vger.kernel.org,
Stephen Smalley <sds@tycho.nsa.gov>,
Chris Wright <chrisw@osdl.org>,
David Woodhouse <dwmw2@infradead.org>
Subject: Re: [RFC][PATCH] (#2) file system auditing
Date: Wed, 20 Apr 2005 12:30:42 +0530 [thread overview]
Message-ID: <20050420070042.GB10147@in.ibm.com> (raw)
In-Reply-To: <200504161006.52766.tinytim@us.ibm.com>
On Sat, Apr 16, 2005 at 10:06:52AM +0000, Timothy R. Chavez wrote:
> Hello,
>
> The audit subsystem is currently incapable of auditing a file system object
> based on its location and name. This is critical for auditing well-defined
> and security-relevant locations such as /etc/shadow, where the file is
> re-created on each transaction, and cannot rely on the (device, inode)-based
> filters to ensure persistence of auditing across transactions. This patch adds
> the necessary functionality to the audit subsystem and VFS to support file
> system auditing in which an object is audited based on its location and name.
> This work is being done to make the audit subsystem compliant with Common
> Criteria's Controlled Access Protection Profile (CAPP) specification.
>
> This is the second (#2) RFC on linux-fsdevel.
>
Please see the comments interspread below.
Thanks
Maneesh
PS: Sorry to miss earlier versions.
[..]
> @@ -987,6 +991,7 @@ struct dentry *d_splice_alias(struct ino
> /* d_instantiate takes dcache_lock, so we do it by hand */
> list_add(&dentry->d_alias, &inode->i_dentry);
> dentry->d_inode = inode;
> + audit_attach_watch(dentry, 0);
> spin_unlock(&dcache_lock);
> security_d_instantiate(dentry, inode);
> d_rehash(dentry);
> @@ -1090,6 +1095,7 @@ struct dentry * __d_lookup(struct dentry
> if (!d_unhashed(dentry)) {
> atomic_inc(&dentry->d_count);
> found = dentry;
> + audit_attach_watch(found, 0);
> }
> spin_unlock(&dentry->d_lock);
> break;
Are you sure about hooking __d_lookup()? I am not getting why would one want
to watch every dcache lookup. IIUC, you probaly want to watch every fs object
entering the dcache, so IMO, the right place is real_lookup() and not the
__d_lookup() which is performance sensitive code.
[..]
> +
> +static inline void audit_watch_free(struct audit_watch *watch)
> +{
> + if (watch) {
> + kfree(watch->name);
> + kfree(watch->filterkey);
> + kmem_cache_free(audit_watch_cache, watch);
> + }
> +}
I think you forgot, kfree(watch->path) here.
[..]
> +static inline int audit_insert_watch(char *path, char *filterkey, __u32 perms)
> +{
> + int ret;
> + struct nameidata nd;
> +
> + ret = path_lookup(path, LOOKUP_PARENT|LOOKUP_FOLLOW, &nd);
> + if (ret < 0)
> + goto audit_insert_watch_exit;
> +
> + ret = -EPERM;
> + if (nd.last_type != LAST_NORM || !nd.last.name)
> + goto audit_insert_watch_release;
> +
> + ret = audit_create_wentry(path, nd.last.name, filterkey, perms,
> + nd.dentry->d_inode->i_sb->s_dev,
> + nd.dentry->d_inode->i_audit);
> + /* __d_lookup will attach the audit data, if nd.last.name exists. */
> + d_lookup(nd.dentry, &nd.last);
Do you mean, if nd.last.name exists in dcache() or exists in the filesystem?
[..]
> +void audit_attach_watch(struct dentry *dentry, int remove)
> +{
> + struct audit_wentry *wentry;
> + struct audit_data *data, *parent;
> +
> + if (!dentry || !dentry->d_inode)
> + return;
> +
> + if (!dentry->d_parent || !dentry->d_parent->d_inode)
> + return;
> +
> + data = dentry->d_inode->i_audit;
> + parent = dentry->d_parent->d_inode->i_audit;
> +
> + wentry = audit_wentry_fetch_lock(dentry->d_name.name, parent);
> +
> + write_lock(&data->lock);
> + /* FIXME: long watchlist == too much spinning? */
> + if (remove) {
> + audit_drain_watchlist(data);
> + if (wentry && data->wentry) {
> + if (!strcmp(wentry->w_watch->name,
> + data->wentry->w_watch->name)) {
> + audit_wentry_put(data->wentry);
> + dentry->d_inode->i_audit->wentry = NULL;
> + }
> + }
> + } else if (!data->wentry || hlist_unhashed(&data->wentry->w_node)) {
> + audit_wentry_put(data->wentry);
> + dentry->d_inode->i_audit->wentry = audit_wentry_get(wentry);
why not just, data->wentry = audit_wentry_get(wentry);
[..]
> +int audit_list_watches(int pid, int seq)
> +{
> + int ret = 0;
> + struct audit_wentry *wentry;
> + struct hlist_node *pos;
> +
> + spin_lock(&audit_master_watchlist_lock);
> + hlist_for_each_entry(wentry, pos, &audit_master_watchlist, w_master) {
> + ret = audit_send_watch(pid, seq, wentry->w_watch);
> + if (ret < 0)
> + break;
> + }
> + spin_unlock(&audit_master_watchlist_lock);
> +
> + audit_send_reply(pid, seq, AUDIT_WATCH_LIST, 1, 1, NULL, 0);
> +
> + return ret;
> +}
> +
Looks like there is lock heirarachy issue here between
audit_master_watchlist_lock and data->lock().
audit_list_watches()---> audit_master_watchlist_lock
audit_send_watch()
audit_wentry_fetch_lock()---> data->lock
audit_insert_watch()
audit_create_wentry()---> data->lock
audit_master_insert()---> audit_master_watchlist_lock
OR
audit_attach_watch()---> data->lock
audit_drain_watchlist()
audit_destroy_watch()
audit_master_remove()---> audit_master_watchlist_lock
> +int audit_receive_watch(int type, int pid, int uid, int seq,
> + struct audit_transport *req)
> +{
> + int ret = 0;
> + char *path = NULL;
> + char *filterkey = NULL;
> +
> + ret = -ENOMEM;
> + path = kmalloc(req->pathlen, GFP_KERNEL);
> + if (!path)
> + goto audit_receive_watch_exit;
> + strncpy(path, req->buf, req->pathlen);
> +
> + if (req->fklen) {
> + filterkey = kmalloc(req->fklen, GFP_KERNEL);
> + if (!filterkey)
> + goto audit_receive_watch_exit;
> + }
> + strncpy(filterkey, req->buf+req->pathlen, req->fklen);
> +
> + ret = -EINVAL;
> + if (!path || strlen(path) + 1 > PATH_MAX)
> + goto audit_receive_watch_exit;
> +
> + /* Includes terminating '\0' */
> + if (req->fklen > AUDIT_FILTERKEY_MAX)
> + goto audit_receive_watch_exit;
> +
> + if (req->perms > (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND))
> + goto audit_receive_watch_exit;
> +
> + switch (type) {
> + case AUDIT_WATCH_INS:
> + ret = audit_insert_watch(path, filterkey, req->perms);
> + break;
> + case AUDIT_WATCH_REM:
> + ret = audit_remove_watch(path);
> + break;
> + default:
> + ret = -EINVAL;
> + }
> +
> +audit_receive_watch_exit:
> + kfree(path);
> + kfree(filterkey);
> + return ret;
> +}
> +
> +int audit_inode_alloc(struct inode *inode)
> +{
> + if (inode) {
> + inode->i_audit = audit_data_alloc();
> + if (!inode->i_audit)
> + return ENOMEM;
> + }
> +
> + return 0;
> +}
> +
> +void audit_inode_free(struct inode *inode)
> +{
> + if (inode)
> + audit_data_free(inode->i_audit);
> +}
> +
> +int audit_filesystem_init()
> +{
> + int ret = 0;
> +
> + audit_watch_cache =
> + kmem_cache_create("audit_watch_cache",
> + sizeof(struct audit_watch), 0, 0, NULL, NULL);
> + if (!audit_watch_cache)
> + goto audit_filesystem_init_fail;
> +
> + audit_wentry_cache =
> + kmem_cache_create("audit_wentry_cache",
> + sizeof(struct audit_wentry), 0, 0, NULL, NULL);
> + if (!audit_wentry_cache)
> + goto audit_filesystem_init_fail;
> +
> + goto audit_filesystem_init_exit;
> +
> +audit_filesystem_init_fail:
> + ret = -ENOMEM;
> + kmem_cache_destroy(audit_watch_cache);
> + kmem_cache_destroy(audit_wentry_cache);
> +audit_filesystem_init_exit:
> + return ret;
> +
> +}
> diff -Nurp linux-2.6.12-rc2-mm1~orig/kernel/auditsc.c linux-2.6.12-rc2-mm1~audit/kernel/auditsc.c
> --- linux-2.6.12-rc2-mm1~orig/kernel/auditsc.c 2005-04-11 14:15:36.000000000 +0000
> +++ linux-2.6.12-rc2-mm1~audit/kernel/auditsc.c 2005-04-08 16:12:23.000000000 +0000
> @@ -102,6 +102,7 @@ struct audit_aux_data {
> };
>
> #define AUDIT_AUX_IPCPERM 0
> +#define AUDIT_AUX_WATCH 1
>
> struct audit_aux_data_ipcctl {
> struct audit_aux_data d;
> @@ -112,6 +113,16 @@ struct audit_aux_data_ipcctl {
> mode_t mode;
> };
>
> +struct audit_aux_data_watched {
> + struct audit_aux_data link;
> + struct audit_wentry *wentry;
> + unsigned long ino;
> + int mask;
> + uid_t uid;
> + gid_t gid;
> + dev_t dev;
> + dev_t rdev;
> +};
>
> /* The per-task audit context. */
> struct audit_context {
> @@ -665,6 +676,24 @@ static void audit_log_exit(struct audit_
> audit_log_format(ab,
> " qbytes=%lx uid=%d gid=%d mode=%x",
> axi->qbytes, axi->uid, axi->gid, axi->mode);
> + break;
> + }
> +
> + case AUDIT_AUX_WATCH: {
> + struct audit_aux_data_watched *axi = (void *)aux;
> + audit_log_format(ab, " name=");
> + audit_log_untrustedstring(ab, axi->wentry->w_watch->name);
> + audit_log_format(ab,
> + " filterkey=%s perm=%u perm_mask=%d"
> + " inode=%lu inode_uid=%d inode_gid=%d"
> + " inode_dev=%02x:%02x inode_rdev=%02x:%02x",
> + axi->wentry->w_watch->filterkey,
> + axi->wentry->w_watch->perms,
> + axi->mask, axi->ino, axi->uid, axi->gid,
> + MAJOR(axi->dev), MINOR(axi->dev),
> + MAJOR(axi->rdev), MINOR(axi->rdev));
> + audit_wentry_put(axi->wentry);
> + break;
> }
> }
> audit_log_end(ab);
> @@ -1024,3 +1053,52 @@ int audit_ipc_perms(unsigned long qbytes
> context->aux = (void *)ax;
> return 0;
> }
> +
> +int audit_notify_watch(struct inode *inode, int mask)
> +{
> + int ret = 0;
> + struct audit_context *context = current->audit_context;
> + struct audit_aux_data_watched *ax;
> + struct audit_wentry *wentry = NULL;
> +
> + if (likely(!context))
> + goto audit_notify_watch_fail;
> +
> + if (!inode)
> + goto audit_notify_watch_fail;
> +
> + wentry = audit_wentry_get(inode->i_audit->wentry);
> + if (!wentry)
> + goto audit_notify_watch_fail;
> +
> + if (mask && (wentry->w_watch->perms && !(wentry->w_watch->perms&mask)))
> + goto audit_notify_watch_fail;
> +
> + ret = -ENOMEM;
> + ax = kmalloc(sizeof(*ax), GFP_KERNEL);
> + if (!ax)
> + goto audit_notify_watch_fail;
> +
> + ret = 0;
> + if (context->in_syscall && !context->auditable)
> + context->auditable = 1;
> +
> + ax->wentry = wentry;
> + ax->mask = mask;
> + ax->ino = inode->i_ino;
> + ax->uid = inode->i_uid;
> + ax->gid = inode->i_gid;
> + ax->dev = inode->i_sb->s_dev;
> + ax->rdev = inode->i_rdev;
> +
> + ax->link.type = AUDIT_AUX_WATCH;
> + ax->link.next = context->aux;
> + context->aux = (void *)ax;
> +
> + goto audit_notify_watch_exit;
> +
> +audit_notify_watch_fail:
> + audit_wentry_put(wentry);
> +audit_notify_watch_exit:
> + return ret;
> +}
> diff -Nurp linux-2.6.12-rc2-mm1~orig/security/selinux/nlmsgtab.c linux-2.6.12-rc2-mm1~audit/security/selinux/nlmsgtab.c
> --- linux-2.6.12-rc2-mm1~orig/security/selinux/nlmsgtab.c 2005-04-11 14:15:36.000000000 +0000
> +++ linux-2.6.12-rc2-mm1~audit/security/selinux/nlmsgtab.c 2005-04-05 22:58:51.000000000 +0000
> @@ -98,6 +98,9 @@ static struct nlmsg_perm nlmsg_audit_per
> { AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
> { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
> { AUDIT_LOGIN, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
> + { AUDIT_WATCH_INS, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
> + { AUDIT_WATCH_REM, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
> + { AUDIT_WATCH_LIST, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
> };
>
--
Maneesh Soni
Linux Technology Center,
IBM India Software Labs,
Bangalore, India
email: maneesh@in.ibm.com
Phone: 91-80-25044990
next prev parent reply other threads:[~2005-04-20 7:01 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-04-16 10:06 [RFC][PATCH] (#2) file system auditing Timothy R. Chavez
2005-04-20 7:00 ` Maneesh Soni [this message]
2005-04-26 20:23 ` Timothy R. Chavez
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20050420070042.GB10147@in.ibm.com \
--to=maneesh@in.ibm.com \
--cc=chrisw@osdl.org \
--cc=dwmw2@infradead.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=sds@tycho.nsa.gov \
--cc=tinytim@us.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).