]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/nd6_send.c
xnu-2422.1.72.tar.gz
[apple/xnu.git] / bsd / netinet6 / nd6_send.c
diff --git a/bsd/netinet6/nd6_send.c b/bsd/netinet6/nd6_send.c
new file mode 100644 (file)
index 0000000..916607b
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2013 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+#include <sys/types.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+#include <sys/syslog.h>
+
+#include <libkern/crypto/sha1.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet6/in6_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+
+SYSCTL_DECL(_net_inet6);       /* Note: Not in any common header. */
+
+SYSCTL_NODE(_net_inet6, OID_AUTO, send, CTLFLAG_RW | CTLFLAG_LOCKED, 0,
+       "IPv6 Secure Neighbor Discovery");
+
+static int nd6_send_opmode = ND6_SEND_OPMODE_DISABLED;
+
+SYSCTL_INT(_net_inet6_send, OID_AUTO, opstate, CTLFLAG_RD | CTLFLAG_LOCKED,
+       &nd6_send_opstate, 0, "current SEND operating state");
+
+int nd6_send_opstate = ND6_SEND_OPMODE_DISABLED;
+SYSCTL_INT(_net_inet6_send, OID_AUTO, opmode, CTLFLAG_RW | CTLFLAG_LOCKED,
+       &nd6_send_opmode, 0, "configured SEND operating mode");
+
+static int sysctl_cga_parameters SYSCTL_HANDLER_ARGS;
+
+SYSCTL_PROC(_net_inet6_send, OID_AUTO, cga_parameters,
+       CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_LOCKED, 0, 0,
+       sysctl_cga_parameters, "S,nd6_send_nodecfg", "");
+
+/*
+ * The size of the buffer is sufficient to contain a public key, its size in
+ * machine binary type for the kernel, and the CGA precalc for the global
+ * scope. This interface is not a public API, so we don't anticipate that the
+ * userland and the kernel will be mismatched between ILP32 and LP64.
+ */
+#define        SYSCTL_CGA_PARAMETERS_BUFFER_SIZE \
+       2 * (sizeof (u_int16_t) + IN6_CGA_KEY_MAXSIZE) + \
+       sizeof (struct in6_cga_prepare)
+
+static int
+sysctl_cga_parameters SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1)
+       u_int namelen;
+       char *oldp, *newp;
+       const char *fin;
+       struct in6_cga_nodecfg cfg;
+       struct iovec *iov;
+       int error;
+       char *buffer;
+       u_int16_t u16;
+
+       namelen = arg2;
+       if (namelen != 0) {
+               log(LOG_ERR, "%s: name length err [len=%u]\n", __func__,
+                   namelen);
+               return (EINVAL);
+       }
+
+       if (req->newlen > SYSCTL_CGA_PARAMETERS_BUFFER_SIZE) {
+               log(LOG_ERR, "%s: input buffer size error [len=%u]\n", __func__,
+                   req->newlen);
+               return (EINVAL);
+       }
+
+       MALLOC(buffer, char *, SYSCTL_CGA_PARAMETERS_BUFFER_SIZE, M_IP6CGA,
+           M_WAITOK);
+       if (buffer == NULL) {
+               log(LOG_ERR, "%s: could not allocate marshaling buffer.\n",
+                   __func__);
+               return (ENOMEM);
+       }
+
+       in6_cga_node_lock();
+
+       if (req->oldptr != USER_ADDR_NULL && req->oldlen > 0) {
+               oldp = buffer;
+               fin = &buffer[SYSCTL_CGA_PARAMETERS_BUFFER_SIZE];
+               if (req->oldlen < SYSCTL_CGA_PARAMETERS_BUFFER_SIZE)
+                       fin = &buffer[req->oldlen];
+
+               in6_cga_query(&cfg);
+               iov = &cfg.cga_pubkey;
+               if (iov->iov_len > 0) {
+                       VERIFY(iov->iov_len < UINT16_MAX);
+
+                       if (&oldp[sizeof (cfg.cga_prepare)] <= fin)
+                               bcopy(&cfg.cga_prepare, oldp,
+                                   sizeof (cfg.cga_prepare));
+                       oldp += sizeof (cfg.cga_prepare);
+
+                       if (&oldp[sizeof (u16)] < fin) {
+                               u16 = (u_int16_t) iov->iov_len;
+                               bcopy(&u16, oldp, sizeof (u16));
+                       }
+                       oldp += sizeof (u16);
+
+                       if (&oldp[iov->iov_len] < fin)
+                               bcopy(iov->iov_base, oldp, iov->iov_len);
+                       oldp += iov->iov_len;
+
+                       if (oldp > fin) {
+                               req->oldlen = oldp - buffer;
+                               log(LOG_ERR, "%s: marshalled data too large.\n",
+                                   __func__);
+                               error = ENOMEM;
+                               goto done;
+                       }
+               }
+
+               error = SYSCTL_OUT(req, buffer, oldp - buffer);
+               if (error)
+                       goto done;
+       }
+
+       if (req->newptr == USER_ADDR_NULL)
+               goto done;
+
+       error = proc_suser(current_proc());
+       if (error)
+               goto done;
+
+       if (req->newlen == 0) {
+               in6_cga_stop();
+               nd6_send_opstate = ND6_SEND_OPMODE_DISABLED;
+               goto done;
+       }
+
+       error = SYSCTL_IN(req, buffer, req->newlen);
+       if (error)
+               goto done;
+
+       newp = buffer;
+       fin = &buffer[req->newlen];
+
+       bzero(&cfg, sizeof cfg);
+
+       if (&newp[sizeof (cfg.cga_prepare)] <= fin)
+               bcopy(newp, &cfg.cga_prepare, sizeof (cfg.cga_prepare));
+       newp += sizeof (cfg.cga_prepare);
+
+       iov = &cfg.cga_privkey;
+       if (&newp[sizeof (u16)] < fin) {
+               bcopy(newp, &u16, sizeof (u16));
+               iov->iov_len = u16;
+
+               if (iov->iov_len > IN6_CGA_KEY_MAXSIZE) {
+                       error = EINVAL;
+                       goto done;
+               }
+       }
+       newp += sizeof (u16);
+
+       iov->iov_base = newp;
+       newp += iov->iov_len;
+
+       iov = &cfg.cga_pubkey;
+       if (&newp[sizeof (u16)] < fin) {
+               bcopy(newp, &u16, sizeof (u16));
+               iov->iov_len = u16;
+
+               if (iov->iov_len > IN6_CGA_KEY_MAXSIZE) {
+                       error = EINVAL;
+                       goto done;
+               }
+       }
+       newp += sizeof (u16);
+
+       iov->iov_base = newp;
+       newp += iov->iov_len;
+
+       if (newp > fin) {
+               log(LOG_ERR, "%s: input too large [octets=%ld].\n", __func__,
+                   newp - fin);
+               error = ENOMEM;
+               goto done;
+       }
+
+       error = in6_cga_start(&cfg);
+       if (!error)
+               nd6_send_opstate = nd6_send_opmode;
+       else
+               log(LOG_ERR, "%s: in6_cga_start error=%d.\n", __func__,
+                   error);
+
+done:
+       in6_cga_node_unlock();
+       FREE(buffer, M_IP6CGA);
+       return (error);
+}
+
+/* End of file */