xref: /xnu-10002.1.13/bsd/netinet6/nd6_send.c (revision 1031c584a5e37aff177559b9f69dbd3c8c3fd30a)
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 
48 SYSCTL_DECL(_net_inet6);        /* Note: Not in any common header. */
49 
50 SYSCTL_NODE(_net_inet6, OID_AUTO, send, CTLFLAG_RW | CTLFLAG_LOCKED, 0,
51     "IPv6 Secure Neighbor Discovery");
52 
53 static int nd6_send_opmode = ND6_SEND_OPMODE_CGA_QUIET;
54 SYSCTL_INT(_net_inet6_send, OID_AUTO, opmode, CTLFLAG_RW | CTLFLAG_LOCKED,
55     &nd6_send_opmode, 0, "configured SEND operating mode");
56 
57 int nd6_send_opstate = ND6_SEND_OPMODE_DISABLED;
58 SYSCTL_INT(_net_inet6_send, OID_AUTO, opstate, CTLFLAG_RD | CTLFLAG_LOCKED,
59     &nd6_send_opstate, 0, "current SEND operating state");
60 
61 static int sysctl_cga_parameters SYSCTL_HANDLER_ARGS;
62 
63 SYSCTL_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 
77 static int
78 sysctl_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 #if CONFIG_MACF
90 	kauth_cred_t cred;
91 #endif
92 
93 	namelen = arg2;
94 	if (namelen != 0) {
95 		log(LOG_ERR, "%s: name length err [len=%u]\n", __func__,
96 		    namelen);
97 		return EINVAL;
98 	}
99 
100 	if (req->newlen > SYSCTL_CGA_PARAMETERS_BUFFER_SIZE) {
101 		log(LOG_ERR, "%s: input buffer size error [len=%zu]\n",
102 		    __func__, req->newlen);
103 		return EINVAL;
104 	}
105 
106 #if CONFIG_MACF
107 	cred = kauth_cred_proc_ref(current_proc());
108 	error = mac_system_check_info(cred, "net.inet6.send.cga_parameters");
109 	kauth_cred_unref(&cred);
110 	if (error != 0) {
111 		log(LOG_ERR, "%s: mac_system_check_info denied.\n", __func__);
112 		return EPERM;
113 	}
114 #endif
115 
116 	buffer = (char *)kalloc_data(SYSCTL_CGA_PARAMETERS_BUFFER_SIZE,
117 	    Z_WAITOK | Z_ZERO);
118 	if (buffer == NULL) {
119 		log(LOG_ERR, "%s: could not allocate marshaling buffer.\n",
120 		    __func__);
121 		return ENOMEM;
122 	}
123 
124 	in6_cga_node_lock();
125 
126 	if (req->oldptr != USER_ADDR_NULL && req->oldlen > 0) {
127 		oldp = buffer;
128 		fin = &buffer[SYSCTL_CGA_PARAMETERS_BUFFER_SIZE];
129 		if (req->oldlen < SYSCTL_CGA_PARAMETERS_BUFFER_SIZE) {
130 			fin = &buffer[req->oldlen];
131 		}
132 
133 		in6_cga_query(&cfg);
134 		iov = &cfg.cga_pubkey;
135 		if (iov->iov_len > 0) {
136 			VERIFY(iov->iov_len < UINT16_MAX);
137 
138 			if (&oldp[sizeof(cfg.cga_prepare)] <= fin) {
139 				bcopy(&cfg.cga_prepare, oldp,
140 				    sizeof(cfg.cga_prepare));
141 			}
142 			oldp += sizeof(cfg.cga_prepare);
143 
144 			if (&oldp[sizeof(u16)] < fin) {
145 				u16 = (u_int16_t) iov->iov_len;
146 				bcopy(&u16, oldp, sizeof(u16));
147 			}
148 			oldp += sizeof(u16);
149 
150 			if (&oldp[iov->iov_len] < fin) {
151 				bcopy(iov->iov_base, oldp, iov->iov_len);
152 			}
153 			oldp += iov->iov_len;
154 
155 			if (oldp > fin) {
156 				req->oldlen = oldp - buffer;
157 				log(LOG_ERR, "%s: marshalled data too large.\n",
158 				    __func__);
159 				error = ENOMEM;
160 				goto done;
161 			}
162 		}
163 
164 		error = SYSCTL_OUT(req, buffer, oldp - buffer);
165 		if (error) {
166 			goto done;
167 		}
168 	}
169 
170 	if (req->newptr == USER_ADDR_NULL) {
171 		goto done;
172 	}
173 
174 	error = proc_suser(current_proc());
175 	if (error) {
176 		goto done;
177 	}
178 
179 	if (req->newlen == 0) {
180 		in6_cga_stop();
181 		nd6_send_opstate = ND6_SEND_OPMODE_DISABLED;
182 		goto done;
183 	}
184 
185 	error = SYSCTL_IN(req, buffer, req->newlen);
186 	if (error) {
187 		goto done;
188 	}
189 
190 	newp = buffer;
191 	fin = &buffer[req->newlen];
192 
193 	bzero(&cfg, sizeof cfg);
194 
195 	if (&newp[sizeof(cfg.cga_prepare)] <= fin) {
196 		bcopy(newp, &cfg.cga_prepare, sizeof(cfg.cga_prepare));
197 	}
198 	newp += sizeof(cfg.cga_prepare);
199 
200 	iov = &cfg.cga_privkey;
201 	if (&newp[sizeof(u16)] < fin) {
202 		bcopy(newp, &u16, sizeof(u16));
203 		iov->iov_len = u16;
204 
205 		if (iov->iov_len > IN6_CGA_KEY_MAXSIZE) {
206 			error = EINVAL;
207 			goto done;
208 		}
209 	}
210 	newp += sizeof(u16);
211 
212 	iov->iov_base = newp;
213 	newp += iov->iov_len;
214 
215 	iov = &cfg.cga_pubkey;
216 	if (&newp[sizeof(u16)] < fin) {
217 		bcopy(newp, &u16, sizeof(u16));
218 		iov->iov_len = u16;
219 
220 		if (iov->iov_len > IN6_CGA_KEY_MAXSIZE) {
221 			error = EINVAL;
222 			goto done;
223 		}
224 	}
225 	newp += sizeof(u16);
226 
227 	iov->iov_base = newp;
228 	newp += iov->iov_len;
229 
230 	if (newp > fin) {
231 		log(LOG_ERR, "%s: input too large [octets=%ld].\n", __func__,
232 		    newp - fin);
233 		error = ENOMEM;
234 		goto done;
235 	}
236 
237 	error = in6_cga_start(&cfg);
238 	if (!error) {
239 		nd6_send_opstate = nd6_send_opmode;
240 	} else {
241 		log(LOG_ERR, "%s: in6_cga_start error=%d.\n", __func__,
242 		    error);
243 	}
244 
245 done:
246 	in6_cga_node_unlock();
247 	kfree_data(buffer, SYSCTL_CGA_PARAMETERS_BUFFER_SIZE);
248 	return error;
249 }
250 
251 /* End of file */
252