247 lines
6.2 KiB
C
247 lines
6.2 KiB
C
/*
|
|
* Copyright (c) 2013-2021 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 <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>
|
|
|
|
#if CONFIG_MACF
|
|
#include <sys/kauth.h>
|
|
#include <security/mac_framework.h>
|
|
#endif
|
|
|
|
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_CGA_QUIET;
|
|
SYSCTL_INT(_net_inet6_send, OID_AUTO, opmode, CTLFLAG_RW | CTLFLAG_LOCKED,
|
|
&nd6_send_opmode, 0, "configured SEND operating mode");
|
|
|
|
int nd6_send_opstate = ND6_SEND_OPMODE_DISABLED;
|
|
SYSCTL_INT(_net_inet6_send, OID_AUTO, opstate, CTLFLAG_RD | CTLFLAG_LOCKED,
|
|
&nd6_send_opstate, 0, "current SEND operating state");
|
|
|
|
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=%zu]\n",
|
|
__func__, req->newlen);
|
|
return EINVAL;
|
|
}
|
|
|
|
#if CONFIG_MACF
|
|
error = mac_system_check_info(current_cached_proc_cred(req->p),
|
|
"net.inet6.send.cga_parameters");
|
|
if (error != 0) {
|
|
log(LOG_ERR, "%s: mac_system_check_info denied.\n", __func__);
|
|
return EPERM;
|
|
}
|
|
#endif
|
|
|
|
buffer = (char *)kalloc_data(SYSCTL_CGA_PARAMETERS_BUFFER_SIZE,
|
|
Z_WAITOK | Z_ZERO);
|
|
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();
|
|
kfree_data(buffer, SYSCTL_CGA_PARAMETERS_BUFFER_SIZE);
|
|
return error;
|
|
}
|
|
|
|
/* End of file */
|