1/*
2 * Copyright (c) 2013-2021 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <sys/types.h>
30#include <sys/malloc.h>
31#include <sys/proc.h>
32#include <sys/sysctl.h>
33#include <sys/syslog.h>
34
35#include <net/if.h>
36
37#include <netinet/in.h>
38#include <netinet6/in6_var.h>
39#include <netinet/ip6.h>
40#include <netinet6/ip6_var.h>
41#include <netinet6/nd6.h>
42
43#if CONFIG_MACF
44#include <sys/kauth.h>
45#include <security/mac_framework.h>
46#endif
47
48SYSCTL_DECL(_net_inet6); /* Note: Not in any common header. */
49
50SYSCTL_NODE(_net_inet6, OID_AUTO, send, CTLFLAG_RW | CTLFLAG_LOCKED, 0,
51 "IPv6 Secure Neighbor Discovery");
52
53static int nd6_send_opmode = ND6_SEND_OPMODE_CGA_QUIET;
54SYSCTL_INT(_net_inet6_send, OID_AUTO, opmode, CTLFLAG_RW | CTLFLAG_LOCKED,
55 &nd6_send_opmode, 0, "configured SEND operating mode");
56
57int nd6_send_opstate = ND6_SEND_OPMODE_DISABLED;
58SYSCTL_INT(_net_inet6_send, OID_AUTO, opstate, CTLFLAG_RD | CTLFLAG_LOCKED,
59 &nd6_send_opstate, 0, "current SEND operating state");
60
61static int sysctl_cga_parameters SYSCTL_HANDLER_ARGS;
62
63SYSCTL_PROC(_net_inet6_send, OID_AUTO, cga_parameters,
64 CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_LOCKED, 0, 0,
65 sysctl_cga_parameters, "S,nd6_send_nodecfg", "");
66
67/*
68 * The size of the buffer is sufficient to contain a public key, its size in
69 * machine binary type for the kernel, and the CGA precalc for the global
70 * scope. This interface is not a public API, so we don't anticipate that the
71 * userland and the kernel will be mismatched between ILP32 and LP64.
72 */
73#define SYSCTL_CGA_PARAMETERS_BUFFER_SIZE \
74 (2 * (sizeof (u_int16_t) + IN6_CGA_KEY_MAXSIZE) + \
75 sizeof (struct in6_cga_prepare))
76
77static int
78sysctl_cga_parameters SYSCTL_HANDLER_ARGS
79{
80#pragma unused(oidp, arg1)
81 u_int namelen;
82 char *oldp, *newp;
83 const char *fin;
84 struct in6_cga_nodecfg cfg;
85 struct iovec *iov;
86 int error;
87 char *buffer;
88 u_int16_t u16;
89
90 namelen = arg2;
91 if (namelen != 0) {
92 log(LOG_ERR, "%s: name length err [len=%u]\n", __func__,
93 namelen);
94 return EINVAL;
95 }
96
97 if (req->newlen > SYSCTL_CGA_PARAMETERS_BUFFER_SIZE) {
98 log(LOG_ERR, "%s: input buffer size error [len=%zu]\n",
99 __func__, req->newlen);
100 return EINVAL;
101 }
102
103#if CONFIG_MACF
104 error = mac_system_check_info(current_cached_proc_cred(req->p),
105 info_type: "net.inet6.send.cga_parameters");
106 if (error != 0) {
107 log(LOG_ERR, "%s: mac_system_check_info denied.\n", __func__);
108 return EPERM;
109 }
110#endif
111
112 buffer = (char *)kalloc_data(SYSCTL_CGA_PARAMETERS_BUFFER_SIZE,
113 Z_WAITOK | Z_ZERO);
114 if (buffer == NULL) {
115 log(LOG_ERR, "%s: could not allocate marshaling buffer.\n",
116 __func__);
117 return ENOMEM;
118 }
119
120 in6_cga_node_lock();
121
122 if (req->oldptr != USER_ADDR_NULL && req->oldlen > 0) {
123 oldp = buffer;
124 fin = &buffer[SYSCTL_CGA_PARAMETERS_BUFFER_SIZE];
125 if (req->oldlen < SYSCTL_CGA_PARAMETERS_BUFFER_SIZE) {
126 fin = &buffer[req->oldlen];
127 }
128
129 in6_cga_query(&cfg);
130 iov = &cfg.cga_pubkey;
131 if (iov->iov_len > 0) {
132 VERIFY(iov->iov_len < UINT16_MAX);
133
134 if (&oldp[sizeof(cfg.cga_prepare)] <= fin) {
135 bcopy(src: &cfg.cga_prepare, dst: oldp,
136 n: sizeof(cfg.cga_prepare));
137 }
138 oldp += sizeof(cfg.cga_prepare);
139
140 if (&oldp[sizeof(u16)] < fin) {
141 u16 = (u_int16_t) iov->iov_len;
142 bcopy(src: &u16, dst: oldp, n: sizeof(u16));
143 }
144 oldp += sizeof(u16);
145
146 if (&oldp[iov->iov_len] < fin) {
147 bcopy(src: iov->iov_base, dst: oldp, n: iov->iov_len);
148 }
149 oldp += iov->iov_len;
150
151 if (oldp > fin) {
152 req->oldlen = oldp - buffer;
153 log(LOG_ERR, "%s: marshalled data too large.\n",
154 __func__);
155 error = ENOMEM;
156 goto done;
157 }
158 }
159
160 error = SYSCTL_OUT(req, buffer, oldp - buffer);
161 if (error) {
162 goto done;
163 }
164 }
165
166 if (req->newptr == USER_ADDR_NULL) {
167 goto done;
168 }
169
170 error = proc_suser(p: current_proc());
171 if (error) {
172 goto done;
173 }
174
175 if (req->newlen == 0) {
176 in6_cga_stop();
177 nd6_send_opstate = ND6_SEND_OPMODE_DISABLED;
178 goto done;
179 }
180
181 error = SYSCTL_IN(req, buffer, req->newlen);
182 if (error) {
183 goto done;
184 }
185
186 newp = buffer;
187 fin = &buffer[req->newlen];
188
189 bzero(s: &cfg, n: sizeof cfg);
190
191 if (&newp[sizeof(cfg.cga_prepare)] <= fin) {
192 bcopy(src: newp, dst: &cfg.cga_prepare, n: sizeof(cfg.cga_prepare));
193 }
194 newp += sizeof(cfg.cga_prepare);
195
196 iov = &cfg.cga_privkey;
197 if (&newp[sizeof(u16)] < fin) {
198 bcopy(src: newp, dst: &u16, n: sizeof(u16));
199 iov->iov_len = u16;
200
201 if (iov->iov_len > IN6_CGA_KEY_MAXSIZE) {
202 error = EINVAL;
203 goto done;
204 }
205 }
206 newp += sizeof(u16);
207
208 iov->iov_base = newp;
209 newp += iov->iov_len;
210
211 iov = &cfg.cga_pubkey;
212 if (&newp[sizeof(u16)] < fin) {
213 bcopy(src: newp, dst: &u16, n: sizeof(u16));
214 iov->iov_len = u16;
215
216 if (iov->iov_len > IN6_CGA_KEY_MAXSIZE) {
217 error = EINVAL;
218 goto done;
219 }
220 }
221 newp += sizeof(u16);
222
223 iov->iov_base = newp;
224 newp += iov->iov_len;
225
226 if (newp > fin) {
227 log(LOG_ERR, "%s: input too large [octets=%ld].\n", __func__,
228 newp - fin);
229 error = ENOMEM;
230 goto done;
231 }
232
233 error = in6_cga_start(&cfg);
234 if (!error) {
235 nd6_send_opstate = nd6_send_opmode;
236 } else {
237 log(LOG_ERR, "%s: in6_cga_start error=%d.\n", __func__,
238 error);
239 }
240
241done:
242 in6_cga_node_unlock();
243 kfree_data(buffer, SYSCTL_CGA_PARAMETERS_BUFFER_SIZE);
244 return error;
245}
246
247/* End of file */
248