diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c
index ffbf7863306d..cd225121aff7 100644
--- a/fs/kernfs/inode.c
+++ b/fs/kernfs/inode.c
@@ -185,8 +185,7 @@ static void kernfs_refresh_inode(struct kernfs_node *kn, struct inode *inode)
* persistent copy in kernfs_node.
*/
set_inode_attr(inode, &attrs->ia_iattr);
- security_inode_notifysecctx(inode, attrs->ia_context.context,
- attrs->ia_context.len);
+ security_inode_notifysecctx(inode, &attrs->ia_context);
}
if (kernfs_type(kn) == KERNFS_DIR)
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 414a90d48493..8acc5eef4d08 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -341,13 +341,16 @@ void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,
struct nfs4_label *label)
{
int error;
+ struct lsmcontext context = { .slot = LSMBLOB_FIRST };
if (label == NULL)
return;
- if ((fattr->valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL) && inode->i_security) {
- error = security_inode_notifysecctx(inode, label->label,
- label->len);
+ if ((fattr->valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL) &&
+ inode->i_security) {
+ context.context = label->label;
+ context.len = label->len;
+ error = security_inode_notifysecctx(inode, &context);
if (error)
printk(KERN_ERR "%s() %s %d "
"security_inode_notifysecctx() %d\n",diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 0cfd257ffdaf..0f166c81f596 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -54,12 +54,14 @@
static inline void
nfsd4_security_inode_setsecctx(struct svc_fh *resfh, struct xdr_netobj *label, u32 *bmval)
{
+ struct lsmcontext context = { .slot = LSMBLOB_FIRST };
struct inode *inode = d_inode(resfh->fh_dentry);
int status;
inode_lock(inode);
- status = security_inode_setsecctx(resfh->fh_dentry,
- label->data, label->len);
+ context.context = label->data;
+ context.len = label->len;
+ status = security_inode_setsecctx(resfh->fh_dentry, &context);
inode_unlock(inode);
if (status)diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 7dc98e14655d..274a998cc123 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -531,6 +531,7 @@ __be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,
__be32 error;
int host_error;
struct dentry *dentry;
+ struct lsmcontext context = { .slot = LSMBLOB_FIRST };
error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, NFSD_MAY_SATTR);
if (error)@@ -539,7 +540,9 @@ __be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,
dentry = fhp->fh_dentry;
inode_lock(d_inode(dentry));
- host_error = security_inode_setsecctx(dentry, label->data, label->len);
+ context.context = label->data;
+ context.len = label->len;
+ host_error = security_inode_setsecctx(dentry, &context);
inode_unlock(d_inode(dentry));
return nfserrno(host_error);
}
diff --git a/include/linux/security.h b/include/linux/security.h
index 0665a27a2891..2f442746dede 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -493,8 +493,8 @@ int security_secctx_to_secid(const char *secdata, u32 seclen,
void security_release_secctx(struct lsmcontext *cp);
void security_inode_invalidate_secctx(struct inode *inode);
-int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
-int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen);
+int security_inode_notifysecctx(struct inode *inode, struct lsmcontext *cp);
+int security_inode_setsecctx(struct dentry *dentry, struct lsmcontext *cp);
int security_inode_getsecctx(struct inode *inode, struct lsmcontext *cp);
#else /* CONFIG_SECURITY */
@@ -1288,11 +1288,13 @@ static inline void security_inode_invalidate_secctx(struct inode *inode)
{
}
-static inline int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
+static inline int security_inode_notifysecctx(struct inode *inode,
+ struct lsmcontext *cp);
{
return -EOPNOTSUPP;
}
-static inline int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
+static inline int security_inode_setsecctx(struct dentry *dentry,
+ struct lsmcontext *cp)
{
return -EOPNOTSUPP;
}diff --git a/security/security.c b/security/security.c
index 13102d16bf2c..c71ddae6760e 100644
--- a/security/security.c
+++ b/security/security.c
@@ -743,6 +743,42 @@ static int lsm_superblock_alloc(struct super_block *sb)
return 0;
}
+/**
+ * append_ctx - append a lsm/context pair to a compound context
+ * @ctx: the existing compound context
+ * @ctxlen: size of the old context, including terminating nul byte
+ * @lsm: new lsm name, nul terminated
+ * @new: new context, possibly nul terminated
+ * @newlen: maximum size of @new
+ *
+ * replace @ctx with a new compound context, appending @newlsm and @new
+ * to @ctx. On exit the new data replaces the old, which is freed.
+ * @ctxlen is set to the new size, which includes a trailing nul byte.
+ *
+ * Returns 0 on success, -ENOMEM if no memory is available.
+ */
+static int append_ctx(char **ctx, int *ctxlen, const char *lsm, char *new,
+ int newlen)
+{
+ char *final;
+ int llen;
+
+ llen = strlen(lsm) + 1;
+ newlen = strnlen(new, newlen) + 1;
+
+ final = kzalloc(*ctxlen + llen + newlen, GFP_KERNEL);
+ if (final == NULL)
+ return -ENOMEM;
+ if (*ctxlen)
+ memcpy(final, *ctx, *ctxlen);
+ memcpy(final + *ctxlen, lsm, llen);
+ memcpy(final + *ctxlen + llen, new, newlen);
+ kfree(*ctx);
+ *ctx = final;
+ *ctxlen = *ctxlen + llen + newlen;
+ return 0;
+}
+
/*
* Hook list operation macros.
*@@ -2083,12 +2119,8 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
struct security_hook_list *hp;
char *final = NULL;
char *cp;
- char *tp;
int rc = 0;
int finallen = 0;
- int llen;
- int clen;
- int tlen;
int display = lsm_task_display(current);
int slot = 0;
@@ -2116,26 +2148,12 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
kfree(final);
return rc;
}
- llen = strlen(hp->lsmid->lsm) + 1;
- clen = strlen(cp) + 1;
- tlen = llen + clen;
- if (final)
- tlen += finallen;
- tp = kzalloc(tlen, GFP_KERNEL);
- if (tp == NULL) {
- kfree(cp);
+ rc = append_ctx(&final, &finallen, hp->lsmid->lsm,
+ cp, rc);
+ if (rc < 0) {
kfree(final);
- return -ENOMEM;
+ return rc;
}
- if (final)
- memcpy(tp, final, finallen);
- memcpy(tp + finallen, hp->lsmid->lsm, llen);
- memcpy(tp + finallen + llen, cp, clen);
- kfree(cp);
- if (final)
- kfree(final);
- final = tp;
- finallen = tlen;
}
if (final == NULL)
return -EINVAL;@@ -2210,13 +2228,22 @@ int security_netlink_send(struct sock *sk, struct sk_buff *skb)
return call_int_hook(netlink_send, 0, sk, skb);
}
+/**
+ * security_ismaclabel - Does @name identify a MAC attribute
+ * @name: attribute name in question
+ *
+ * If @name is the name of a Mandatory Access Control (MAC) attribute
+ * that the first module on the list recognizes return 1. Don't look
+ * beyond the first module, as this is only used by NFS and NFS can't
+ * differentiate which module to use.
+ */
int security_ismaclabel(const char *name)
{
struct security_hook_list *hp;
hlist_for_each_entry(hp, &security_hook_heads.ismaclabel, list)
- if (hp->hook.ismaclabel(name) != 0)
- return 1;
+ return hp->hook.ismaclabel(name);
+
return 0;
}
EXPORT_SYMBOL(security_ismaclabel);@@ -2284,6 +2311,15 @@ void security_release_secctx(struct lsmcontext *cp)
struct security_hook_list *hp;
bool found = false;
+ if (cp->slot == LSMBLOB_INVALID)
+ return;
+
+ if (cp->slot == LSMBLOB_COMPOUND) {
+ kfree(cp->context);
+ found = true;
+ goto clear_out;
+ }
+
hlist_for_each_entry(hp, &security_hook_heads.release_secctx, list)
if (cp->slot == hp->lsmid->slot) {
hp->hook.release_secctx(cp->context, cp->len);@@ -2291,6 +2327,7 @@ void security_release_secctx(struct lsmcontext *cp)
break;
}
+clear_out:
memset(cp, 0, sizeof(*cp));
if (!found)
@@ -2305,30 +2342,82 @@ void security_inode_invalidate_secctx(struct inode *inode)
}
EXPORT_SYMBOL(security_inode_invalidate_secctx);
-int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
+int security_inode_notifysecctx(struct inode *inode, struct lsmcontext *cp)
{
- return call_int_hook(inode_notifysecctx, 0, inode, ctx, ctxlen);
+ struct security_hook_list *hp;
+ char *raw = cp->context;
+ char *ctx;
+ int llen;
+ int clen;
+ int rc;
+
+ if (cp->slot == LSMBLOB_COMPOUND) {
+ hlist_for_each_entry(hp,
+ &security_hook_heads.inode_notifysecctx, list) {
+ llen = strlen(raw) + 1;
+ ctx = raw + llen;
+ clen = strlen(ctx) + 1;
+ if (!strcmp(hp->lsmid->lsm, raw)) {
+ rc = hp->hook.inode_notifysecctx(inode, ctx,
+ clen);
+ if (WARN_ON(rc != 0))
+ return rc;
+ }
+ raw = ctx + clen;
+ }
+ return 0;
+ }
+
+ hlist_for_each_entry(hp, &security_hook_heads.inode_notifysecctx, list)
+ if (cp->slot == LSMBLOB_FIRST || cp->slot == hp->lsmid->slot)
+ return hp->hook.inode_notifysecctx(inode, cp->context,
+ cp->len);
+ return 0;
}
EXPORT_SYMBOL(security_inode_notifysecctx);
-int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
+int security_inode_setsecctx(struct dentry *dentry, struct lsmcontext *cp)
{
- return call_int_hook(inode_setsecctx, 0, dentry, ctx, ctxlen);
+ struct security_hook_list *hp;
+
+ if (WARN_ON(cp->slot != LSMBLOB_FIRST))
+ return -EINVAL;
+
+ hlist_for_each_entry(hp, &security_hook_heads.inode_setsecctx, list)
+ return hp->hook.inode_setsecctx(dentry, cp->context, cp->len);
+ return 0;
}
EXPORT_SYMBOL(security_inode_setsecctx);
int security_inode_getsecctx(struct inode *inode, struct lsmcontext *cp)
{
struct security_hook_list *hp;
+ char *finalctx = NULL;
+ int rc = -EOPNOTSUPP;
+ int finallen = 0;
memset(cp, 0, sizeof(*cp));
hlist_for_each_entry(hp, &security_hook_heads.inode_getsecctx, list) {
+ rc = hp->hook.inode_getsecctx(inode, (void **)&cp->context,
+ &cp->len);
+ if (rc) {
+ kfree(finalctx);
+ return rc;
+ }
cp->slot = hp->lsmid->slot;
- return hp->hook.inode_getsecctx(inode, (void **)&cp->context,
- &cp->len);
+ rc = append_ctx(&finalctx, &finallen, hp->lsmid->lsm,
+ cp->context, cp->len);
+ security_release_secctx(cp);
+ if (rc) {
+ kfree(finalctx);
+ return rc;
+ }
}
- return -EOPNOTSUPP;
+ cp->slot = LSMBLOB_COMPOUND;
+ cp->context = finalctx;
+ cp->len = finallen;
+ return 0;
}
EXPORT_SYMBOL(security_inode_getsecctx);
@@ -2433,12 +2522,9 @@ int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
struct security_hook_list *hp;
char *final = NULL;
char *cp;
- char *tp;
int rc = 0;
unsigned finallen = 0;
- unsigned llen;
unsigned clen = 0;
- unsigned tlen;
switch (display) {
case LSMBLOB_DISPLAY:@@ -2471,29 +2557,8 @@ int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
kfree(final);
return rc;
}
- /*
- * Don't propogate trailing nul bytes.
- */
- clen = strnlen(cp, clen) + 1;
- llen = strlen(hp->lsmid->lsm) + 1;
- tlen = llen + clen;
- if (final)
- tlen += finallen;
- tp = kzalloc(tlen, GFP_KERNEL);
- if (tp == NULL) {
- kfree(cp);
- kfree(final);
- return -ENOMEM;
- }
- if (final)
- memcpy(tp, final, finallen);
- memcpy(tp + finallen, hp->lsmid->lsm, llen);
- memcpy(tp + finallen + llen, cp, clen);
- kfree(cp);
- if (final)
- kfree(final);
- final = tp;
- finallen = tlen;
+ rc = append_ctx(&final, &finallen, hp->lsmid->lsm,
+ cp, clen);
}
if (final == NULL)
return -EINVAL;--
2.20.1