/* * 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 #include #include #include #include #include #include #include #include #include #include #if CONFIG_MACF #include #include #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 */