]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
netlabel: Add CIPSO {set, del}attr request_sock functions
authorPaul Moore <paul.moore@hp.com>
Fri, 8 May 2009 21:58:36 +0000 (17:58 -0400)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 18 May 2009 23:34:49 +0000 (16:34 -0700)
[NOTE: based on 389fb800ac8be2832efedd19978a2b8ced37eb61]

Add the cipso_v4_req_setattr() and cipso_v4_req_delattr() functions to set and
delete the CIPSO security attributes on a request_sock used during a incoming
connection request.

Signed-off-by: Paul Moore <paul.moore@hp.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
include/net/cipso_ipv4.h
net/ipv4/cipso_ipv4.c

index bedc7f62e35d43e522efcd348c202dfb7f4ad261..abd443604c9fa042ec72d564eff7fb7a86307300 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/net.h>
 #include <linux/skbuff.h>
 #include <net/netlabel.h>
+#include <net/request_sock.h>
 #include <asm/atomic.h>
 
 /* known doi values */
@@ -215,6 +216,10 @@ int cipso_v4_sock_setattr(struct sock *sk,
                          const struct netlbl_lsm_secattr *secattr);
 void cipso_v4_sock_delattr(struct sock *sk);
 int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr);
+int cipso_v4_req_setattr(struct request_sock *req,
+                        const struct cipso_v4_doi *doi_def,
+                        const struct netlbl_lsm_secattr *secattr);
+void cipso_v4_req_delattr(struct request_sock *req);
 int cipso_v4_skbuff_setattr(struct sk_buff *skb,
                            const struct cipso_v4_doi *doi_def,
                            const struct netlbl_lsm_secattr *secattr);
@@ -247,6 +252,18 @@ static inline int cipso_v4_sock_getattr(struct sock *sk,
        return -ENOSYS;
 }
 
+static inline int cipso_v4_req_setattr(struct request_sock *req,
+                                      const struct cipso_v4_doi *doi_def,
+                                      const struct netlbl_lsm_secattr *secattr)
+{
+       return -ENOSYS;
+}
+
+static inline void cipso_v4_req_delattr(struct request_sock *req)
+{
+       return;
+}
+
 static inline int cipso_v4_skbuff_setattr(struct sk_buff *skb,
                                      const struct cipso_v4_doi *doi_def,
                                      const struct netlbl_lsm_secattr *secattr)
index 7bc992976d29bd18b70dd39544791d66f4351901..4ea2c389c8419004c5adf1f0bae2a5ada8f4d067 100644 (file)
@@ -1941,6 +1941,72 @@ socket_setattr_failure:
        return ret_val;
 }
 
+/**
+ * cipso_v4_req_setattr - Add a CIPSO option to a connection request socket
+ * @req: the connection request socket
+ * @doi_def: the CIPSO DOI to use
+ * @secattr: the specific security attributes of the socket
+ *
+ * Description:
+ * Set the CIPSO option on the given socket using the DOI definition and
+ * security attributes passed to the function.  Returns zero on success and
+ * negative values on failure.
+ *
+ */
+int cipso_v4_req_setattr(struct request_sock *req,
+                        const struct cipso_v4_doi *doi_def,
+                        const struct netlbl_lsm_secattr *secattr)
+{
+       int ret_val = -EPERM;
+       unsigned char *buf = NULL;
+       u32 buf_len;
+       u32 opt_len;
+       struct ip_options *opt = NULL;
+       struct inet_request_sock *req_inet;
+
+       /* We allocate the maximum CIPSO option size here so we are probably
+        * being a little wasteful, but it makes our life _much_ easier later
+        * on and after all we are only talking about 40 bytes. */
+       buf_len = CIPSO_V4_OPT_LEN_MAX;
+       buf = kmalloc(buf_len, GFP_ATOMIC);
+       if (buf == NULL) {
+               ret_val = -ENOMEM;
+               goto req_setattr_failure;
+       }
+
+       ret_val = cipso_v4_genopt(buf, buf_len, doi_def, secattr);
+       if (ret_val < 0)
+               goto req_setattr_failure;
+       buf_len = ret_val;
+
+       /* We can't use ip_options_get() directly because it makes a call to
+        * ip_options_get_alloc() which allocates memory with GFP_KERNEL and
+        * we won't always have CAP_NET_RAW even though we _always_ want to
+        * set the IPOPT_CIPSO option. */
+       opt_len = (buf_len + 3) & ~3;
+       opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC);
+       if (opt == NULL) {
+               ret_val = -ENOMEM;
+               goto req_setattr_failure;
+       }
+       memcpy(opt->__data, buf, buf_len);
+       opt->optlen = opt_len;
+       opt->cipso = sizeof(struct iphdr);
+       kfree(buf);
+       buf = NULL;
+
+       req_inet = inet_rsk(req);
+       opt = xchg(&req_inet->opt, opt);
+       kfree(opt);
+
+       return 0;
+
+req_setattr_failure:
+       kfree(buf);
+       kfree(opt);
+       return ret_val;
+}
+
 /**
  * cipso_v4_sock_delattr - Delete the CIPSO option from a socket
  * @sk: the socket
@@ -2015,6 +2081,70 @@ void cipso_v4_sock_delattr(struct sock *sk)
        }
 }
 
+/**
+ * cipso_v4_req_delattr - Delete the CIPSO option from a request socket
+ * @reg: the request socket
+ *
+ * Description:
+ * Removes the CIPSO option from a request socket, if present.
+ *
+ */
+void cipso_v4_req_delattr(struct request_sock *req)
+{
+       struct ip_options *opt;
+       struct inet_request_sock *req_inet;
+
+       req_inet = inet_rsk(req);
+       opt = req_inet->opt;
+       if (opt == NULL || opt->cipso == 0)
+               return;
+
+       if (opt->srr || opt->rr || opt->ts || opt->router_alert) {
+               u8 cipso_len;
+               u8 cipso_off;
+               unsigned char *cipso_ptr;
+               int iter;
+               int optlen_new;
+
+               cipso_off = opt->cipso - sizeof(struct iphdr);
+               cipso_ptr = &opt->__data[cipso_off];
+               cipso_len = cipso_ptr[1];
+
+               if (opt->srr > opt->cipso)
+                       opt->srr -= cipso_len;
+               if (opt->rr > opt->cipso)
+                       opt->rr -= cipso_len;
+               if (opt->ts > opt->cipso)
+                       opt->ts -= cipso_len;
+               if (opt->router_alert > opt->cipso)
+                       opt->router_alert -= cipso_len;
+               opt->cipso = 0;
+
+               memmove(cipso_ptr, cipso_ptr + cipso_len,
+                       opt->optlen - cipso_off - cipso_len);
+
+               /* determining the new total option length is tricky because of
+                * the padding necessary, the only thing i can think to do at
+                * this point is walk the options one-by-one, skipping the
+                * padding at the end to determine the actual option size and
+                * from there we can determine the new total option length */
+               iter = 0;
+               optlen_new = 0;
+               while (iter < opt->optlen)
+                       if (opt->__data[iter] != IPOPT_NOP) {
+                               iter += opt->__data[iter + 1];
+                               optlen_new = iter;
+                       } else
+                               iter++;
+               opt->optlen = (optlen_new + 3) & ~3;
+       } else {
+               /* only the cipso option was present on the socket so we can
+                * remove the entire option struct */
+               req_inet->opt = NULL;
+               kfree(opt);
+       }
+}
+
 /**
  * cipso_v4_getattr - Helper function for the cipso_v4_*_getattr functions
  * @cipso: the CIPSO v4 option