1/*
2 * Copyright (c) 2013 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 <libkern/crypto/sha1.h>
36
37#include <net/if.h>
38
39#include <netinet/in.h>
40#include <netinet6/in6_var.h>
41#include <netinet/ip6.h>
42#include <netinet6/ip6_var.h>
43#include <netinet6/nd6.h>
44
45#if CONFIG_MACF
46#include <sys/kauth.h>
47#include <security/mac_framework.h>
48#endif
49
50SYSCTL_DECL(_net_inet6);	/* Note: Not in any common header. */
51
52SYSCTL_NODE(_net_inet6, OID_AUTO, send, CTLFLAG_RW | CTLFLAG_LOCKED, 0,
53	"IPv6 Secure Neighbor Discovery");
54
55static int nd6_send_opmode = ND6_SEND_OPMODE_DISABLED;
56
57SYSCTL_INT(_net_inet6_send, OID_AUTO, opstate, CTLFLAG_RD | CTLFLAG_LOCKED,
58	&nd6_send_opstate, 0, "current SEND operating state");
59
60int nd6_send_opstate = ND6_SEND_OPMODE_DISABLED;
61SYSCTL_INT(_net_inet6_send, OID_AUTO, opmode, CTLFLAG_RW | CTLFLAG_LOCKED,
62	&nd6_send_opmode, 0, "configured SEND operating mode");
63
64static int sysctl_cga_parameters SYSCTL_HANDLER_ARGS;
65
66SYSCTL_PROC(_net_inet6_send, OID_AUTO, cga_parameters,
67	CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_LOCKED, 0, 0,
68	sysctl_cga_parameters, "S,nd6_send_nodecfg", "");
69
70/*
71 * The size of the buffer is sufficient to contain a public key, its size in
72 * machine binary type for the kernel, and the CGA precalc for the global
73 * scope. This interface is not a public API, so we don't anticipate that the
74 * userland and the kernel will be mismatched between ILP32 and LP64.
75 */
76#define	SYSCTL_CGA_PARAMETERS_BUFFER_SIZE \
77	2 * (sizeof (u_int16_t) + IN6_CGA_KEY_MAXSIZE) + \
78	sizeof (struct in6_cga_prepare)
79
80static int
81sysctl_cga_parameters SYSCTL_HANDLER_ARGS
82{
83#pragma unused(oidp, arg1)
84	u_int namelen;
85	char *oldp, *newp;
86	const char *fin;
87	struct in6_cga_nodecfg cfg;
88	struct iovec *iov;
89	int error;
90	char *buffer;
91	u_int16_t u16;
92#if CONFIG_MACF
93	kauth_cred_t cred;
94#endif
95
96	namelen = arg2;
97	if (namelen != 0) {
98		log(LOG_ERR, "%s: name length err [len=%u]\n", __func__,
99		    namelen);
100		return (EINVAL);
101	}
102
103	if (req->newlen > SYSCTL_CGA_PARAMETERS_BUFFER_SIZE) {
104		log(LOG_ERR, "%s: input buffer size error [len=%u]\n", __func__,
105		    req->newlen);
106		return (EINVAL);
107	}
108
109#if CONFIG_MACF
110	cred = kauth_cred_proc_ref(current_proc());
111	error = mac_system_check_info(cred, "net.inet6.send.cga_parameters");
112	kauth_cred_unref(&cred);
113	if (error != 0) {
114		log(LOG_ERR, "%s: mac_system_check_info denied.\n", __func__);
115		return (EPERM);
116	}
117#endif
118
119	MALLOC(buffer, char *, SYSCTL_CGA_PARAMETERS_BUFFER_SIZE, M_IP6CGA,
120	    M_WAITOK);
121	if (buffer == NULL) {
122		log(LOG_ERR, "%s: could not allocate marshaling buffer.\n",
123		    __func__);
124		return (ENOMEM);
125	}
126
127	in6_cga_node_lock();
128
129	if (req->oldptr != USER_ADDR_NULL && req->oldlen > 0) {
130		oldp = buffer;
131		fin = &buffer[SYSCTL_CGA_PARAMETERS_BUFFER_SIZE];
132		if (req->oldlen < SYSCTL_CGA_PARAMETERS_BUFFER_SIZE)
133			fin = &buffer[req->oldlen];
134
135		in6_cga_query(&cfg);
136		iov = &cfg.cga_pubkey;
137		if (iov->iov_len > 0) {
138			VERIFY(iov->iov_len < UINT16_MAX);
139
140			if (&oldp[sizeof (cfg.cga_prepare)] <= fin)
141				bcopy(&cfg.cga_prepare, oldp,
142				    sizeof (cfg.cga_prepare));
143			oldp += sizeof (cfg.cga_prepare);
144
145			if (&oldp[sizeof (u16)] < fin) {
146				u16 = (u_int16_t) iov->iov_len;
147				bcopy(&u16, oldp, sizeof (u16));
148			}
149			oldp += sizeof (u16);
150
151			if (&oldp[iov->iov_len] < fin)
152				bcopy(iov->iov_base, oldp, iov->iov_len);
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	if (req->newptr == USER_ADDR_NULL)
170		goto done;
171
172	error = proc_suser(current_proc());
173	if (error)
174		goto done;
175
176	if (req->newlen == 0) {
177		in6_cga_stop();
178		nd6_send_opstate = ND6_SEND_OPMODE_DISABLED;
179		goto done;
180	}
181
182	error = SYSCTL_IN(req, buffer, req->newlen);
183	if (error)
184		goto done;
185
186	newp = buffer;
187	fin = &buffer[req->newlen];
188
189	bzero(&cfg, sizeof cfg);
190
191	if (&newp[sizeof (cfg.cga_prepare)] <= fin)
192		bcopy(newp, &cfg.cga_prepare, sizeof (cfg.cga_prepare));
193	newp += sizeof (cfg.cga_prepare);
194
195	iov = &cfg.cga_privkey;
196	if (&newp[sizeof (u16)] < fin) {
197		bcopy(newp, &u16, sizeof (u16));
198		iov->iov_len = u16;
199
200		if (iov->iov_len > IN6_CGA_KEY_MAXSIZE) {
201			error = EINVAL;
202			goto done;
203		}
204	}
205	newp += sizeof (u16);
206
207	iov->iov_base = newp;
208	newp += iov->iov_len;
209
210	iov = &cfg.cga_pubkey;
211	if (&newp[sizeof (u16)] < fin) {
212		bcopy(newp, &u16, sizeof (u16));
213		iov->iov_len = u16;
214
215		if (iov->iov_len > IN6_CGA_KEY_MAXSIZE) {
216			error = EINVAL;
217			goto done;
218		}
219	}
220	newp += sizeof (u16);
221
222	iov->iov_base = newp;
223	newp += iov->iov_len;
224
225	if (newp > fin) {
226		log(LOG_ERR, "%s: input too large [octets=%ld].\n", __func__,
227		    newp - fin);
228		error = ENOMEM;
229		goto done;
230	}
231
232	error = in6_cga_start(&cfg);
233	if (!error)
234		nd6_send_opstate = nd6_send_opmode;
235	else
236		log(LOG_ERR, "%s: in6_cga_start error=%d.\n", __func__,
237		    error);
238
239done:
240	in6_cga_node_unlock();
241	FREE(buffer, M_IP6CGA);
242	return (error);
243}
244
245/* End of file */
246