xref: /xnu-8792.61.2/bsd/netinet6/in6_cga.c (revision 42e220869062b56f8d7d0726fd4c88954f87902c)
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 
32 #include <kern/locks.h>
33 
34 #include <libkern/crypto/sha1.h>
35 
36 #include <net/if.h>
37 
38 #include <netinet/in.h>
39 #include <netinet6/in6_var.h>
40 #include <netinet/ip6.h>
41 #include <netinet6/ip6_var.h>
42 #include <netinet6/nd6.h>
43 
44 #define IN6_CGA_HASH1_LENGTH    8
45 #define IN6_CGA_HASH2_LENGTH    14
46 #define IN6_CGA_PREPARE_ZEROES  9
47 
48 struct in6_cga_hash1 {
49 	u_int8_t octets[IN6_CGA_HASH1_LENGTH];
50 };
51 
52 struct in6_cga_hash2 {
53 	u_int8_t octets[IN6_CGA_HASH2_LENGTH];
54 };
55 
56 struct in6_cga_singleton {
57 	boolean_t cga_initialized;
58 	decl_lck_mtx_data(, cga_mutex);
59 	struct in6_cga_prepare cga_prepare;
60 	struct iovec cga_pubkey;
61 	struct iovec cga_privkey;
62 };
63 
64 static struct in6_cga_singleton in6_cga = {
65 	.cga_initialized = FALSE,
66 	.cga_mutex = {},
67 	.cga_prepare = {
68 		.cga_modifier = {},
69 		.cga_security_level = 0,
70 	},
71 	.cga_pubkey = {
72 		.iov_base = NULL,
73 		.iov_len = 0,
74 	},
75 	.cga_privkey = {
76 		.iov_base = NULL,
77 		.iov_len = 0,
78 	},
79 };
80 
81 static void
in6_cga_node_lock_assert(int owned)82 in6_cga_node_lock_assert(int owned)
83 {
84 #if !MACH_ASSERT
85 #pragma unused(owned)
86 #endif
87 	VERIFY(in6_cga.cga_initialized);
88 	LCK_MTX_ASSERT(&in6_cga.cga_mutex, owned);
89 }
90 
91 static boolean_t
in6_cga_is_prepare_valid(const struct in6_cga_prepare * prepare,const struct iovec * pubkey)92 in6_cga_is_prepare_valid(const struct in6_cga_prepare *prepare,
93     const struct iovec *pubkey)
94 {
95 	static const u_int8_t zeroes[IN6_CGA_PREPARE_ZEROES] = { };
96 	SHA1_CTX ctx;
97 	u_int8_t sha1[SHA1_RESULTLEN];
98 	u_int i, n;
99 
100 	VERIFY(prepare != NULL);
101 	VERIFY(pubkey != NULL && pubkey->iov_base != NULL);
102 
103 	if (prepare->cga_security_level == 0) {
104 		return TRUE;
105 	}
106 
107 	if (prepare->cga_security_level > 7) {
108 		return FALSE;
109 	}
110 
111 	SHA1Init(&ctx);
112 	SHA1Update(&ctx, &prepare->cga_modifier.octets,
113 	    IN6_CGA_MODIFIER_LENGTH);
114 	SHA1Update(&ctx, &zeroes, IN6_CGA_PREPARE_ZEROES);
115 	SHA1Update(&ctx, pubkey->iov_base, pubkey->iov_len);
116 	/* FUTURE: extension fields */
117 	SHA1Final(sha1, &ctx);
118 
119 	n = 2 * (u_int) prepare->cga_security_level;
120 	VERIFY(n < SHA1_RESULTLEN);
121 	for (i = 0; i < n; ++i) {
122 		if (sha1[i] != 0) {
123 			return FALSE;
124 		}
125 	}
126 
127 	return TRUE;
128 }
129 
130 /*
131  * @brief Generate interface identifier for CGA
132  *      XXX You may notice that following does not really
133  *      mirror what is decribed in:
134  *      https://tools.ietf.org/html/rfc3972#section-4
135  *      By design kernel here will assume that that
136  *      modifier has been converged on by userspace
137  *      for first part of the algorithm for the given
138  *      security level.
139  *      We are not doing that yet but that's how the code
140  *      below is written. So really we are starting
141  *      from bullet 4 of the algorithm.
142  *
143  * @param prepare Pointer to object containing modifier,
144  *      security level & extension to be used.
145  * @param pubkey Public key used for IID generation
146  * @param collisions Collission count on DAD failure
147  *      XXX We are not really re-generating IID on DAD
148  *      failures for now.
149  * @param in6 Pointer to the address containing
150  *      the prefix.
151  *
152  * @return void
153  */
154 static void
in6_cga_generate_iid(const struct in6_cga_prepare * prepare,const struct iovec * pubkey,u_int8_t collisions,struct in6_addr * in6,struct ifnet * ifp)155 in6_cga_generate_iid(const struct in6_cga_prepare *prepare,
156     const struct iovec *pubkey, u_int8_t collisions,
157     struct in6_addr *in6, struct ifnet *ifp)
158 {
159 	SHA1_CTX ctx;
160 	u_int8_t sha1[SHA1_RESULTLEN];
161 
162 	VERIFY(prepare != NULL);
163 	VERIFY(prepare->cga_security_level < 8);
164 	VERIFY(pubkey != NULL && pubkey->iov_base != NULL);
165 	VERIFY(in6 != NULL);
166 
167 	SHA1Init(&ctx);
168 	SHA1Update(&ctx, &prepare->cga_modifier.octets, 16);
169 	SHA1Update(&ctx, in6->s6_addr, 8);
170 	SHA1Update(&ctx, &collisions, 1);
171 	SHA1Update(&ctx, pubkey->iov_base, pubkey->iov_len);
172 	if (ifp->network_id_len) {
173 		SHA1Update(&ctx, &ifp->network_id, ifp->network_id_len);
174 	}
175 	/* FUTURE: extension fields */
176 	SHA1Final(sha1, &ctx);
177 
178 	in6->s6_addr8[8] =
179 	    (u_int8_t)((prepare->cga_security_level << 5) | (sha1[0] & 0x1c));
180 	in6->s6_addr8[9] = sha1[1];
181 	in6->s6_addr8[10] = sha1[2];
182 	in6->s6_addr8[11] = sha1[3];
183 	in6->s6_addr8[12] = sha1[4];
184 	in6->s6_addr8[13] = sha1[5];
185 	in6->s6_addr8[14] = sha1[6];
186 	in6->s6_addr8[15] = sha1[7];
187 }
188 
189 void
in6_cga_init(void)190 in6_cga_init(void)
191 {
192 	lck_mtx_init(&in6_cga.cga_mutex, &ifa_mtx_grp, &ifa_mtx_attr);
193 	in6_cga.cga_initialized = TRUE;
194 }
195 
196 void
in6_cga_node_lock(void)197 in6_cga_node_lock(void)
198 {
199 	VERIFY(in6_cga.cga_initialized);
200 	lck_mtx_lock(&in6_cga.cga_mutex);
201 }
202 
203 void
in6_cga_node_unlock(void)204 in6_cga_node_unlock(void)
205 {
206 	VERIFY(in6_cga.cga_initialized);
207 	lck_mtx_unlock(&in6_cga.cga_mutex);
208 }
209 
210 void
in6_cga_query(struct in6_cga_nodecfg * cfg)211 in6_cga_query(struct in6_cga_nodecfg *cfg)
212 {
213 	VERIFY(cfg != NULL);
214 	in6_cga_node_lock_assert(LCK_MTX_ASSERT_OWNED);
215 
216 	cfg->cga_pubkey = in6_cga.cga_pubkey;
217 	cfg->cga_prepare = in6_cga.cga_prepare;
218 }
219 
220 int
in6_cga_start(const struct in6_cga_nodecfg * cfg)221 in6_cga_start(const struct in6_cga_nodecfg *cfg)
222 {
223 	struct iovec privkey, pubkey;
224 	const struct in6_cga_prepare *prepare;
225 	caddr_t pubkeycopy, privkeycopy;
226 
227 	VERIFY(cfg != NULL);
228 	in6_cga_node_lock_assert(LCK_MTX_ASSERT_OWNED);
229 
230 	privkey = cfg->cga_privkey;
231 	if (privkey.iov_base == NULL || privkey.iov_len == 0 ||
232 	    privkey.iov_len >= IN6_CGA_KEY_MAXSIZE) {
233 		return EINVAL;
234 	}
235 	pubkey = cfg->cga_pubkey;
236 	if (pubkey.iov_base == NULL || pubkey.iov_len == 0 ||
237 	    pubkey.iov_len >= IN6_CGA_KEY_MAXSIZE) {
238 		return EINVAL;
239 	}
240 	prepare = &cfg->cga_prepare;
241 
242 	if (!in6_cga_is_prepare_valid(prepare, &pubkey)) {
243 		return EINVAL;
244 	}
245 
246 	in6_cga.cga_prepare = *prepare;
247 
248 	privkeycopy = (caddr_t)kalloc_data(privkey.iov_len, Z_WAITOK);
249 	if (privkeycopy == NULL) {
250 		return ENOMEM;
251 	}
252 
253 	pubkeycopy = (caddr_t)kalloc_data(pubkey.iov_len, Z_WAITOK);
254 	if (pubkeycopy == NULL) {
255 		if (privkeycopy != NULL) {
256 			kfree_data(privkeycopy, privkey.iov_len);
257 		}
258 		return ENOMEM;
259 	}
260 
261 	bcopy(privkey.iov_base, privkeycopy, privkey.iov_len);
262 	privkey.iov_base = privkeycopy;
263 	if (in6_cga.cga_privkey.iov_base != NULL) {
264 		kfree_data(in6_cga.cga_privkey.iov_base, in6_cga.cga_privkey.iov_len);
265 	}
266 	in6_cga.cga_privkey = privkey;
267 
268 	bcopy(pubkey.iov_base, pubkeycopy, pubkey.iov_len);
269 	pubkey.iov_base = pubkeycopy;
270 	if (in6_cga.cga_pubkey.iov_base != NULL) {
271 		kfree_data(in6_cga.cga_pubkey.iov_base, in6_cga.cga_pubkey.iov_len);
272 	}
273 	in6_cga.cga_pubkey = pubkey;
274 
275 	return 0;
276 }
277 
278 int
in6_cga_stop(void)279 in6_cga_stop(void)
280 {
281 	in6_cga_node_lock_assert(LCK_MTX_ASSERT_OWNED);
282 
283 	if (in6_cga.cga_privkey.iov_base != NULL) {
284 		kfree_data(in6_cga.cga_privkey.iov_base, in6_cga.cga_privkey.iov_len);
285 		in6_cga.cga_privkey.iov_base = NULL;
286 		in6_cga.cga_privkey.iov_len = 0;
287 	}
288 
289 	if (in6_cga.cga_pubkey.iov_base != NULL) {
290 		kfree_data(in6_cga.cga_pubkey.iov_base, in6_cga.cga_pubkey.iov_len);
291 		in6_cga.cga_pubkey.iov_base = NULL;
292 		in6_cga.cga_pubkey.iov_len = 0;
293 	}
294 
295 	return 0;
296 }
297 
298 ssize_t
in6_cga_parameters_prepare(void * output,size_t max,const struct in6_addr * prefix,u_int8_t collisions,const struct in6_cga_modifier * modifier)299 in6_cga_parameters_prepare(void *output, size_t max,
300     const struct in6_addr *prefix, u_int8_t collisions,
301     const struct in6_cga_modifier *modifier)
302 {
303 	caddr_t cursor;
304 
305 	in6_cga_node_lock_assert(LCK_MTX_ASSERT_OWNED);
306 
307 	if (in6_cga.cga_pubkey.iov_len == 0) {
308 		/* No public key */
309 		return EINVAL;
310 	}
311 
312 	if (output == NULL ||
313 	    max < in6_cga.cga_pubkey.iov_len + sizeof(modifier->octets) + 9) {
314 		/* Output buffer error */
315 		return EINVAL;
316 	}
317 
318 	cursor = output;
319 	if (modifier == NULL) {
320 		modifier = &in6_cga.cga_prepare.cga_modifier;
321 	}
322 	if (prefix == NULL) {
323 		static const struct in6_addr llprefix = {{{ 0xfe, 0x80 }}};
324 		prefix = &llprefix;
325 	}
326 
327 	bcopy(&modifier->octets, cursor, sizeof(modifier->octets));
328 	cursor += sizeof(modifier->octets);
329 
330 	*cursor++ = (char) collisions;
331 
332 	bcopy(&prefix->s6_addr[0], cursor, 8);
333 	cursor += 8;
334 
335 	bcopy(in6_cga.cga_pubkey.iov_base, cursor, in6_cga.cga_pubkey.iov_len);
336 	cursor += in6_cga.cga_pubkey.iov_len;
337 
338 	/* FUTURE: Extension fields */
339 
340 	return (ssize_t)(cursor - (caddr_t)output);
341 }
342 
343 int
in6_cga_generate(struct in6_cga_prepare * prepare,u_int8_t collisions,struct in6_addr * in6,struct ifnet * ifp)344 in6_cga_generate(struct in6_cga_prepare *prepare, u_int8_t collisions,
345     struct in6_addr *in6, struct ifnet *ifp)
346 {
347 	int error;
348 	const struct iovec *pubkey;
349 
350 	in6_cga_node_lock_assert(LCK_MTX_ASSERT_OWNED);
351 	VERIFY(in6 != NULL);
352 
353 	if (prepare == NULL) {
354 		prepare = &in6_cga.cga_prepare;
355 	} else {
356 		prepare->cga_security_level =
357 		    in6_cga.cga_prepare.cga_security_level;
358 	}
359 
360 	pubkey = &in6_cga.cga_pubkey;
361 
362 	if (pubkey->iov_base != NULL) {
363 		in6_cga_generate_iid(prepare, pubkey, collisions, in6, ifp);
364 		error = 0;
365 	} else {
366 		error = EADDRNOTAVAIL;
367 	}
368 
369 	return error;
370 }
371 
372 /* End of file */
373