1 /*
2 * Copyright (c) 2013-2023 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 *__single 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(__unsafe_forge_bidi_indexable(void *, privkey.iov_base, privkey.iov_len), privkeycopy, privkey.iov_len);
262
263 privkey.iov_base = privkeycopy;
264 if (in6_cga.cga_privkey.iov_base != NULL) {
265 kfree_data(in6_cga.cga_privkey.iov_base, in6_cga.cga_privkey.iov_len);
266 }
267 in6_cga.cga_privkey = privkey;
268
269 bcopy(__unsafe_forge_bidi_indexable(void *, pubkey.iov_base, pubkey.iov_len), pubkeycopy, pubkey.iov_len);
270
271 pubkey.iov_base = pubkeycopy;
272 if (in6_cga.cga_pubkey.iov_base != NULL) {
273 kfree_data(in6_cga.cga_pubkey.iov_base, in6_cga.cga_pubkey.iov_len);
274 }
275 in6_cga.cga_pubkey = pubkey;
276
277 return 0;
278 }
279
280 int
in6_cga_stop(void)281 in6_cga_stop(void)
282 {
283 in6_cga_node_lock_assert(LCK_MTX_ASSERT_OWNED);
284
285 if (in6_cga.cga_privkey.iov_base != NULL) {
286 kfree_data(in6_cga.cga_privkey.iov_base, in6_cga.cga_privkey.iov_len);
287 in6_cga.cga_privkey.iov_base = NULL;
288 in6_cga.cga_privkey.iov_len = 0;
289 }
290
291 if (in6_cga.cga_pubkey.iov_base != NULL) {
292 kfree_data(in6_cga.cga_pubkey.iov_base, in6_cga.cga_pubkey.iov_len);
293 in6_cga.cga_pubkey.iov_base = NULL;
294 in6_cga.cga_pubkey.iov_len = 0;
295 }
296
297 return 0;
298 }
299
300 int
in6_cga_generate(struct in6_cga_prepare * prepare,u_int8_t collisions,struct in6_addr * in6,struct ifnet * ifp)301 in6_cga_generate(struct in6_cga_prepare *prepare, u_int8_t collisions,
302 struct in6_addr *in6, struct ifnet *ifp)
303 {
304 int error;
305 const struct iovec *__single pubkey;
306
307 in6_cga_node_lock_assert(LCK_MTX_ASSERT_OWNED);
308 VERIFY(in6 != NULL);
309
310 if (prepare == NULL) {
311 prepare = &in6_cga.cga_prepare;
312 } else {
313 prepare->cga_security_level =
314 in6_cga.cga_prepare.cga_security_level;
315 }
316
317 pubkey = &in6_cga.cga_pubkey;
318
319 if (pubkey->iov_base != NULL) {
320 in6_cga_generate_iid(prepare, pubkey, collisions, in6, ifp);
321 error = 0;
322 } else {
323 error = EADDRNOTAVAIL;
324 }
325
326 return error;
327 }
328
329 /* End of file */
330