--- v5
+++ v33
@@ -1,412 +1,142 @@
-The getsockopt SO_PEERSEC provides the LSM based security
-information for a single module, but for reasons of backward
-compatibility cannot include the information for multiple
-modules. A new option SO_PEERCONTEXT is added to report the
-security "context" of multiple modules using a "compound" format
+Replace the single skb pointer in an audit_buffer with
+a list of skb pointers. Add the audit_stamp information
+to the audit_buffer as there's no guarantee that there
+will be an audit_context containing the stamp associated
+with the event. At audit_log_end() time create auxiliary
+records (none are currently defined) as have been added
+to the list.
- lsm1\0value\0lsm2\0value\0
-
-This is expected to be used by system services, including dbus-daemon.
-
+Suggested-by: Paul Moore <paul@paul-moore.com>
Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
- arch/alpha/include/uapi/asm/socket.h | 1 +
- arch/mips/include/uapi/asm/socket.h | 1 +
- arch/parisc/include/uapi/asm/socket.h | 1 +
- arch/sparc/include/uapi/asm/socket.h | 1 +
- include/linux/lsm_hooks.h | 9 ++-
- include/linux/security.h | 10 ++-
- include/uapi/asm-generic/socket.h | 1 +
- net/core/sock.c | 7 +-
- security/apparmor/lsm.c | 20 ++----
- security/security.c | 94 ++++++++++++++++++++++++---
- security/selinux/hooks.c | 20 ++----
- security/smack/smack_lsm.c | 31 ++++-----
- 12 files changed, 134 insertions(+), 62 deletions(-)
+ kernel/audit.c | 53 +++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 35 insertions(+), 18 deletions(-)
-diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h
-index 976e89b116e5..019e5fa8bcda 100644
---- a/arch/alpha/include/uapi/asm/socket.h
-+++ b/arch/alpha/include/uapi/asm/socket.h
-@@ -121,6 +121,7 @@
+diff --git a/kernel/audit.c b/kernel/audit.c
+index f012c3786264..4713e66a12af 100644
+--- a/kernel/audit.c
++++ b/kernel/audit.c
+@@ -197,8 +197,10 @@ static struct audit_ctl_mutex {
+ * to place it on a transmit queue. Multiple audit_buffers can be in
+ * use simultaneously. */
+ struct audit_buffer {
+- struct sk_buff *skb; /* formatted skb ready to send */
++ struct sk_buff *skb; /* the skb for audit_log functions */
++ struct sk_buff_head skb_list; /* formatted skbs, ready to send */
+ struct audit_context *ctx; /* NULL or associated context */
++ struct audit_stamp stamp; /* audit stamp for these records */
+ gfp_t gfp_mask;
+ };
- #define SO_RCVTIMEO_NEW 66
- #define SO_SNDTIMEO_NEW 67
-+#define SO_PEERCONTEXT 68
+@@ -1744,7 +1746,6 @@ static void audit_buffer_free(struct audit_buffer *ab)
+ if (!ab)
+ return;
- #if !defined(__KERNEL__)
+- kfree_skb(ab->skb);
+ kmem_cache_free(audit_buffer_cache, ab);
+ }
-diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h
-index d41765cfbc6e..df8d984d76ed 100644
---- a/arch/mips/include/uapi/asm/socket.h
-+++ b/arch/mips/include/uapi/asm/socket.h
-@@ -132,6 +132,7 @@
+@@ -1760,11 +1761,15 @@ static struct audit_buffer *audit_buffer_alloc(struct audit_context *ctx,
+ ab->skb = nlmsg_new(AUDIT_BUFSIZ, gfp_mask);
+ if (!ab->skb)
+ goto err;
+- if (!nlmsg_put(ab->skb, 0, 0, type, 0, 0))
++ if (!nlmsg_put(ab->skb, 0, 0, type, 0, 0)) {
++ kfree_skb(ab->skb);
+ goto err;
++ }
- #define SO_RCVTIMEO_NEW 66
- #define SO_SNDTIMEO_NEW 67
-+#define SO_PEERCONTEXT 68
+ ab->ctx = ctx;
+ ab->gfp_mask = gfp_mask;
++ skb_queue_head_init(&ab->skb_list);
++ skb_queue_tail(&ab->skb_list, ab->skb);
- #if !defined(__KERNEL__)
+ return ab;
-diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h
-index 66c5dd245ac7..9ae358309f46 100644
---- a/arch/parisc/include/uapi/asm/socket.h
-+++ b/arch/parisc/include/uapi/asm/socket.h
-@@ -113,6 +113,7 @@
+@@ -1825,7 +1830,6 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
+ int type)
+ {
+ struct audit_buffer *ab;
+- struct audit_stamp stamp;
- #define SO_RCVTIMEO_NEW 0x4040
- #define SO_SNDTIMEO_NEW 0x4041
-+#define SO_PEERCONTEXT 0x4042
+ if (audit_initialized != AUDIT_INITIALIZED)
+ return NULL;
+@@ -1880,14 +1884,14 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
+ return NULL;
+ }
- #if !defined(__KERNEL__)
+- audit_get_stamp(ab->ctx, &stamp);
++ audit_get_stamp(ab->ctx, &ab->stamp);
+ /* cancel dummy context to enable supporting records */
+ if (ctx)
+ ctx->dummy = 0;
+ audit_log_format(ab, "audit(%llu.%03lu:%u): ",
+- (unsigned long long)stamp.ctime.tv_sec,
+- stamp.ctime.tv_nsec/1000000,
+- stamp.serial);
++ (unsigned long long)ab->stamp.ctime.tv_sec,
++ ab->stamp.ctime.tv_nsec/1000000,
++ ab->stamp.serial);
-diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h
-index 9265a9eece15..e8a53ef65210 100644
---- a/arch/sparc/include/uapi/asm/socket.h
-+++ b/arch/sparc/include/uapi/asm/socket.h
-@@ -114,6 +114,7 @@
-
- #define SO_RCVTIMEO_NEW 0x0044
- #define SO_SNDTIMEO_NEW 0x0045
-+#define SO_PEERCONTEXT 0x0046
-
- #if !defined(__KERNEL__)
-
-diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
-index 33e5ab4af9f8..b0f788bf82b6 100644
---- a/include/linux/lsm_hooks.h
-+++ b/include/linux/lsm_hooks.h
-@@ -864,8 +864,8 @@
- * SO_GETPEERSEC. For tcp sockets this can be meaningful if the
- * socket is associated with an ipsec SA.
- * @sock is the local socket.
-- * @optval userspace memory where the security state is to be copied.
-- * @optlen userspace int where the module should copy the actual length
-+ * @optval memory where the security state is to be copied.
-+ * @optlen int where the module should copy the actual length
- * of the security state.
- * @len as input is the maximum length to copy to userspace provided
- * by the caller.
-@@ -1697,9 +1697,8 @@ union security_list_options {
- int (*socket_setsockopt)(struct socket *sock, int level, int optname);
- int (*socket_shutdown)(struct socket *sock, int how);
- int (*socket_sock_rcv_skb)(struct sock *sk, struct sk_buff *skb);
-- int (*socket_getpeersec_stream)(struct socket *sock,
-- char __user *optval,
-- int __user *optlen, unsigned len);
-+ int (*socket_getpeersec_stream)(struct socket *sock, char **optval,
-+ int *optlen, unsigned len);
- int (*socket_getpeersec_dgram)(struct socket *sock,
- struct sk_buff *skb, u32 *secid);
- int (*sk_alloc_security)(struct sock *sk, int family, gfp_t priority);
-diff --git a/include/linux/security.h b/include/linux/security.h
-index 12f6d5fcbf6a..0665a27a2891 100644
---- a/include/linux/security.h
-+++ b/include/linux/security.h
-@@ -130,6 +130,7 @@ struct lsmblob {
- #define LSMBLOB_NOT_NEEDED -3 /* Slot not requested */
- #define LSMBLOB_DISPLAY -4 /* Use the "display" slot */
- #define LSMBLOB_FIRST -5 /* Use the default "display" slot */
-+#define LSMBLOB_COMPOUND -6 /* A compound "display" */
+ return ab;
+ }
+@@ -2378,26 +2382,19 @@ int audit_signal_info(int sig, struct task_struct *t)
+ }
/**
- * lsmblob_init - initialize an lsmblob structure.
-@@ -1324,7 +1325,8 @@ int security_socket_setsockopt(struct socket *sock, int level, int optname);
- int security_socket_shutdown(struct socket *sock, int how);
- int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb);
- int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
-- int __user *optlen, unsigned len);
-+ int __user *optlen, unsigned len,
-+ int display);
- int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb,
- struct lsmblob *blob);
- int security_sk_alloc(struct sock *sk, int family, gfp_t priority);
-@@ -1458,8 +1460,10 @@ static inline int security_sock_rcv_skb(struct sock *sk,
- return 0;
- }
+- * audit_log_end - end one audit record
+- * @ab: the audit_buffer
++ * __audit_log_end - end one audit record
++ * @skb: the buffer to send
+ *
+ * We can not do a netlink send inside an irq context because it blocks (last
+ * arg, flags, is not set to MSG_DONTWAIT), so the audit buffer is placed on a
+ * queue and a kthread is scheduled to remove them from the queue outside the
+ * irq context. May be called in any context.
+ */
+-void audit_log_end(struct audit_buffer *ab)
++static void __audit_log_end(struct sk_buff *skb)
+ {
+- struct sk_buff *skb;
+ struct nlmsghdr *nlh;
--static inline int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
-- int __user *optlen, unsigned len)
-+static inline int security_socket_getpeersec_stream(struct socket *sock,
-+ char __user *optval,
-+ int __user *optlen,
-+ unsigned len, int display)
- {
- return -ENOPROTOOPT;
- }
-diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h
-index 8c1391c89171..b38d080c2802 100644
---- a/include/uapi/asm-generic/socket.h
-+++ b/include/uapi/asm-generic/socket.h
-@@ -116,6 +116,7 @@
+- if (!ab)
+- return;
+-
+ if (audit_rate_check()) {
+- skb = ab->skb;
+- ab->skb = NULL;
+-
+ /* setup the netlink header, see the comments in
+ * kauditd_send_multicast_skb() for length quirks */
+ nlh = nlmsg_hdr(skb);
+@@ -2408,6 +2405,26 @@ void audit_log_end(struct audit_buffer *ab)
+ wake_up_interruptible(&kauditd_wait);
+ } else
+ audit_log_lost("rate limit exceeded");
++}
++
++/**
++ * audit_log_end - end one audit record
++ * @ab: the audit_buffer
++ *
++ * We can not do a netlink send inside an irq context because it blocks (last
++ * arg, flags, is not set to MSG_DONTWAIT), so the audit buffer is placed on a
++ * queue and a kthread is scheduled to remove them from the queue outside the
++ * irq context. May be called in any context.
++ */
++void audit_log_end(struct audit_buffer *ab)
++{
++ struct sk_buff *skb;
++
++ if (!ab)
++ return;
++
++ while ((skb = skb_dequeue(&ab->skb_list)))
++ __audit_log_end(skb);
- #define SO_RCVTIMEO_NEW 66
- #define SO_SNDTIMEO_NEW 67
-+#define SO_PEERCONTEXT 68
-
- #if !defined(__KERNEL__)
-
-diff --git a/net/core/sock.c b/net/core/sock.c
-index 782343bb925b..b0955a34167c 100644
---- a/net/core/sock.c
-+++ b/net/core/sock.c
-@@ -1412,7 +1412,12 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
- break;
-
- case SO_PEERSEC:
-- return security_socket_getpeersec_stream(sock, optval, optlen, len);
-+ return security_socket_getpeersec_stream(sock, optval, optlen,
-+ len, LSMBLOB_DISPLAY);
-+
-+ case SO_PEERCONTEXT:
-+ return security_socket_getpeersec_stream(sock, optval, optlen,
-+ len, LSMBLOB_COMPOUND);
-
- case SO_MARK:
- v.val = sk->sk_mark;
-diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
-index ec2e39aa9a84..5d25959610f9 100644
---- a/security/apparmor/lsm.c
-+++ b/security/apparmor/lsm.c
-@@ -1037,10 +1037,8 @@ static struct aa_label *sk_peer_label(struct sock *sk)
- *
- * Note: for tcp only valid if using ipsec or cipso on lan
- */
--static int apparmor_socket_getpeersec_stream(struct socket *sock,
-- char __user *optval,
-- int __user *optlen,
-- unsigned int len)
-+static int apparmor_socket_getpeersec_stream(struct socket *sock, char **optval,
-+ int *optlen, unsigned int len)
- {
- char *name;
- int slen, error = 0;
-@@ -1060,17 +1058,11 @@ static int apparmor_socket_getpeersec_stream(struct socket *sock,
- if (slen < 0) {
- error = -ENOMEM;
- } else {
-- if (slen > len) {
-+ if (slen > len)
- error = -ERANGE;
-- } else if (copy_to_user(optval, name, slen)) {
-- error = -EFAULT;
-- goto out;
-- }
-- if (put_user(slen, optlen))
-- error = -EFAULT;
--out:
-- kfree(name);
--
-+ else
-+ *optval = name;
-+ *optlen = slen;
- }
-
- done:
-diff --git a/security/security.c b/security/security.c
-index 9e5e3ebd169d..5551c146c035 100644
---- a/security/security.c
-+++ b/security/security.c
-@@ -2126,8 +2126,8 @@ int security_setprocattr(const char *lsm, const char *name, void *value,
- hlist_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
- if (lsm != NULL && strcmp(lsm, hp->lsmid->lsm))
- continue;
-- if (lsm == NULL && *display != LSMBLOB_INVALID &&
-- *display != hp->lsmid->slot)
-+ if (lsm == NULL && display != NULL &&
-+ *display != LSMBLOB_INVALID && *display != hp->lsmid->slot)
- continue;
- return hp->hook.setprocattr(name, value, size);
- }
-@@ -2351,17 +2351,91 @@ int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
- EXPORT_SYMBOL(security_sock_rcv_skb);
-
- int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
-- int __user *optlen, unsigned len)
-+ int __user *optlen, unsigned len,
-+ int display)
- {
-- int display = lsm_task_display(current);
- 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:
-+ rc = -ENOPROTOOPT;
-+ display = lsm_task_display(current);
-+ hlist_for_each_entry(hp,
-+ &security_hook_heads.socket_getpeersec_stream,
-+ list)
-+ if (display == LSMBLOB_INVALID ||
-+ display == hp->lsmid->slot) {
-+ rc = hp->hook.socket_getpeersec_stream(sock,
-+ &final, &finallen, len);
-+ break;
-+ }
-+ break;
-+ case LSMBLOB_COMPOUND:
-+ /*
-+ * A compound context, in the form lsm='value'[,lsm='value']...
-+ */
-+ hlist_for_each_entry(hp,
-+ &security_hook_heads.socket_getpeersec_stream,
-+ list) {
-+ rc = hp->hook.socket_getpeersec_stream(sock, &cp, &clen,
-+ len);
-+ if (rc == -EINVAL || rc == -ENOPROTOOPT) {
-+ rc = 0;
-+ continue;
-+ }
-+ if (rc) {
-+ 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;
-+ }
-+ if (final == NULL)
-+ return -EINVAL;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-
-- hlist_for_each_entry(hp, &security_hook_heads.socket_getpeersec_stream,
-- list)
-- if (display == LSMBLOB_INVALID || display == hp->lsmid->slot)
-- return hp->hook.socket_getpeersec_stream(sock, optval,
-- optlen, len);
-- return -ENOPROTOOPT;
-+ if (finallen > len)
-+ rc = -ERANGE;
-+ else if (copy_to_user(optval, final, finallen))
-+ rc = -EFAULT;
-+
-+ if (put_user(finallen, optlen))
-+ rc = -EFAULT;
-+
-+ kfree(final);
-+ return rc;
- }
-
- int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb,
-diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
-index fcad2e3432d2..5e7d61754798 100644
---- a/security/selinux/hooks.c
-+++ b/security/selinux/hooks.c
-@@ -4923,10 +4923,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
- return err;
- }
-
--static int selinux_socket_getpeersec_stream(struct socket *sock,
-- char __user *optval,
-- int __user *optlen,
-- unsigned int len)
-+static int selinux_socket_getpeersec_stream(struct socket *sock, char **optval,
-+ int *optlen, unsigned int len)
- {
- int err = 0;
- char *scontext;
-@@ -4946,18 +4944,12 @@ static int selinux_socket_getpeersec_stream(struct socket *sock,
- if (err)
- return err;
-
-- if (scontext_len > len) {
-+ if (scontext_len > len)
- err = -ERANGE;
-- goto out_len;
-- }
--
-- if (copy_to_user(optval, scontext, scontext_len))
-- err = -EFAULT;
-+ else
-+ *optval = scontext;
-
--out_len:
-- if (put_user(scontext_len, optlen))
-- err = -EFAULT;
-- kfree(scontext);
-+ *optlen = scontext_len;
- return err;
- }
-
-diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
-index 7a30b8692b1e..40c75205a914 100644
---- a/security/smack/smack_lsm.c
-+++ b/security/smack/smack_lsm.c
-@@ -3919,28 +3919,29 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
- *
- * returns zero on success, an error code otherwise
- */
--static int smack_socket_getpeersec_stream(struct socket *sock,
-- char __user *optval,
-- int __user *optlen, unsigned len)
-+static int smack_socket_getpeersec_stream(struct socket *sock, char **optval,
-+ int *optlen, unsigned len)
- {
-- struct socket_smack *ssp;
-- char *rcp = "";
-- int slen = 1;
-+ struct socket_smack *ssp = smack_sock(sock->sk);
-+ char *rcp;
-+ int slen;
- int rc = 0;
-
-- ssp = smack_sock(sock->sk);
-- if (ssp->smk_packet != NULL) {
-- rcp = ssp->smk_packet->smk_known;
-- slen = strlen(rcp) + 1;
-+ if (ssp->smk_packet == NULL) {
-+ *optlen = 0;
-+ return -EINVAL;
- }
-
-+ rcp = ssp->smk_packet->smk_known;
-+ slen = strlen(rcp) + 1;
- if (slen > len)
- rc = -ERANGE;
-- else if (copy_to_user(optval, rcp, slen) != 0)
-- rc = -EFAULT;
--
-- if (put_user(slen, optlen) != 0)
-- rc = -EFAULT;
-+ else {
-+ *optval = kstrdup(rcp, GFP_KERNEL);
-+ if (*optval == NULL)
-+ rc = -ENOMEM;
-+ }
-+ *optlen = slen;
-
- return rc;
+ audit_buffer_free(ab);
}
--
-2.20.1
+2.31.1