]> git.saurik.com Git - apple/xnu.git/blame - bsd/net/if_utun_crypto.c
xnu-2050.18.24.tar.gz
[apple/xnu.git] / bsd / net / if_utun_crypto.c
CommitLineData
316670eb
A
1/*
2 * Copyright (c) 2011 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
30
31#include <sys/systm.h>
32#include <net/if.h>
33#include <net/if_types.h>
34#include <net/if_utun.h>
35#include <sys/mbuf.h>
36#include <net/if_utun_crypto.h>
37#include <net/if_utun_crypto_ipsec.h>
38
39void
40utun_cleanup_crypto (struct utun_pcb *pcb)
41{
42 utun_cleanup_all_crypto_ipsec(pcb);
43 // utun_cleanup_all_crypto_dtls(pcb);
44 pcb->utun_flags &= ~UTUN_FLAGS_CRYPTO;
45}
46
47errno_t
48utun_ctl_enable_crypto (__unused kern_ctl_ref kctlref,
49 __unused u_int32_t unit,
50 __unused void *unitinfo,
51 __unused int opt,
52 void *data,
53 size_t len)
54{
55 struct utun_pcb *pcb = unitinfo;
56
57 /*
58 * - verify the crypto context args passed from user-land.
59 * - check the size of the argument buffer.
60 * - check the direction (IN or OUT)
61 * - check the type (IPSec or DTLS)
62 * - ensure that the crypto context is *not* already valid (don't recreate already valid context).
63 * - we have only one context per direction and type.
64 * - any error should be equivalent to noop.
65 */
66 if (len < UTUN_CRYPTO_ARGS_HDR_SIZE) {
67 return EMSGSIZE;
68 } else {
69 int idx;
70 utun_crypto_args_t *crypto_args = (__typeof__(crypto_args))data;
71 utun_crypto_ctx_t *crypto_ctx;
72
73 if (crypto_args->ver == 0 || crypto_args->ver >= UTUN_CRYPTO_ARGS_VER_MAX) {
74 printf("%s: ver check failed %d\n", __FUNCTION__, crypto_args->ver);
75 return EINVAL;
76 }
77 if (crypto_args->type == 0 || crypto_args->type >= UTUN_CRYPTO_TYPE_MAX) {
78 printf("%s: type check failed %d\n", __FUNCTION__, crypto_args->type);
79 return EINVAL;
80 }
81 if (len < UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args)) {
82 printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
83 (int)len, (int)UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args));
84 return EINVAL;
85 }
86 if (crypto_args->args_ulen != sizeof(crypto_args->u)) {
87 printf("%s: compatibility mode\n", __FUNCTION__);
88 }
89 if (crypto_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
90 utun_ctl_enable_crypto_ipsec(pcb, crypto_args);
91 } else {
92 // unsupported
93 return EPROTONOSUPPORT;
94 }
95 for (idx = 0; idx < UTUN_CRYPTO_DIR_TO_IDX(UTUN_CRYPTO_DIR_MAX); idx++) {
96 crypto_ctx = &pcb->utun_crypto_ctx[idx];
97 if (crypto_ctx->valid) {
98 return EBADF;
99 }
100
101 crypto_ctx->type = crypto_args->type;
102 LIST_INIT(&crypto_ctx->keys_listhead);
103 crypto_ctx->valid = 1;
104 }
105 // data traffic is stopped by default
106 pcb->utun_flags |= (UTUN_FLAGS_CRYPTO | UTUN_FLAGS_CRYPTO_STOP_DATA_TRAFFIC);
107 return 0;
108 }
109}
110
111errno_t
112utun_ctl_disable_crypto (__unused kern_ctl_ref kctlref,
113 __unused u_int32_t unit,
114 __unused void *unitinfo,
115 __unused int opt,
116 void *data,
117 size_t len)
118{
119 struct utun_pcb *pcb = unitinfo;
120
121 /*
122 * - verify the crypto context args passed from user-land.
123 * - check the size of the argument buffer.
124 * - check the direction (IN or OUT)
125 * - check the type (IPSec or DTLS)
126 * - ensure that the crypto context *is* already valid (don't release invalid context).
127 * - we have only one context per direction and type.
128 * - ensure that the crypto context has no crypto material.
129 * - any error should be equivalent to noop.
130 */
131 if (len < UTUN_CRYPTO_ARGS_HDR_SIZE) {
132 return EMSGSIZE;
133 } else {
134 utun_crypto_args_t *crypto_args = (__typeof__(crypto_args))data;
135
136 if (crypto_args->ver == 0 || crypto_args->ver >= UTUN_CRYPTO_ARGS_VER_MAX) {
137 printf("%s: ver check failed %d\n", __FUNCTION__, crypto_args->ver);
138 return EINVAL;
139 }
140 if (crypto_args->type == 0 || crypto_args->type >= UTUN_CRYPTO_TYPE_MAX) {
141 printf("%s: type check failed %d\n", __FUNCTION__, crypto_args->type);
142 return EINVAL;
143 }
144 if (len < UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args)) {
145 printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
146 (int)len, (int)UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args));
147 return EINVAL;
148 }
149 if (crypto_args->args_ulen != sizeof(crypto_args->u)) {
150 printf("%s: compatibility mode\n", __FUNCTION__);
151 }
152
153 if (crypto_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
154 utun_ctl_disable_crypto_ipsec(pcb);
155 } else {
156 // unsupported
157 return EPROTONOSUPPORT;
158 }
159 }
160 pcb->utun_flags &= ~(UTUN_FLAGS_CRYPTO | UTUN_FLAGS_CRYPTO_STOP_DATA_TRAFFIC);
161 return 0;
162}
163
164errno_t
165utun_ctl_config_crypto_keys (__unused kern_ctl_ref kctlref,
166 __unused u_int32_t unit,
167 __unused void *unitinfo,
168 __unused int opt,
169 void *data,
170 size_t len)
171{
172 struct utun_pcb *pcb = unitinfo;
173
174 /*
175 * - verify the crypto material args passed from user-land.
176 * - check the size of the argument buffer.
177 * - check the direction (IN or OUT)
178 * - check the type (IPSec or DTLS)
179 * - crypto material direction and type must match the associated crypto context's.
180 * - we can have a list of crypto materials per context.
181 * - ensure that the crypto context is already valid (don't add crypto material to invalid context).
182 * - any error should be equivalent to noop.
183 */
184 if (len < UTUN_CRYPTO_KEYS_ARGS_HDR_SIZE) {
185 return EMSGSIZE;
186 } else {
187 int idx;
188 utun_crypto_keys_args_t *crypto_keys_args = (__typeof__(crypto_keys_args))data;
189 utun_crypto_ctx_t *crypto_ctx;
190 utun_crypto_keys_t *crypto_keys = NULL;
191
192 if (crypto_keys_args->ver == 0 || crypto_keys_args->ver >= UTUN_CRYPTO_KEYS_ARGS_VER_MAX) {
193 printf("%s: ver check failed %d\n", __FUNCTION__, crypto_keys_args->ver);
194 return EINVAL;
195 }
196 if (crypto_keys_args->dir == 0 || crypto_keys_args->dir >= UTUN_CRYPTO_DIR_MAX) {
197 printf("%s: dir check failed %d\n", __FUNCTION__, crypto_keys_args->dir);
198 return EINVAL;
199 }
200 if (crypto_keys_args->type == 0 || crypto_keys_args->type >= UTUN_CRYPTO_TYPE_MAX) {
201 printf("%s: type check failed %d\n", __FUNCTION__, crypto_keys_args->type);
202 return EINVAL;
203 }
204 if (len < UTUN_CRYPTO_KEYS_ARGS_TOTAL_SIZE(crypto_keys_args)) {
205 printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
206 (int)len, (int)UTUN_CRYPTO_KEYS_ARGS_TOTAL_SIZE(crypto_keys_args));
207 return EINVAL;
208 }
209 idx = UTUN_CRYPTO_DIR_TO_IDX(crypto_keys_args->dir);
210 crypto_ctx = &pcb->utun_crypto_ctx[idx];
211 if (!crypto_ctx->valid) {
212 return EBADF;
213 }
214 if (crypto_keys_args->type != crypto_ctx->type) {
215 // can't add keymat to context with different crypto type
216 return ENOENT;
217 }
218 crypto_keys = utun_alloc(sizeof(*crypto_keys));
219 if (!crypto_keys) {
220 return ENOBUFS;
221 }
222 bzero(crypto_keys, sizeof(*crypto_keys));
223 if (crypto_keys_args->args_ulen != sizeof(crypto_keys_args->u)) {
224 printf("%s: compatibility mode\n", __FUNCTION__);
225 }
226
227 // branch-off for ipsec vs. dtls
228 if (crypto_keys_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
229 errno_t err;
230 if ((err = utun_ctl_config_crypto_keys_ipsec(pcb, crypto_keys_args, crypto_keys))) {
231 utun_free(crypto_keys);
232 return err;
233 }
234 } else {
235 // unsupported
236 utun_free(crypto_keys);
237 return EPROTONOSUPPORT;
238 }
239 crypto_keys->type = crypto_keys_args->type;
240 LIST_INSERT_HEAD(&crypto_ctx->keys_listhead, crypto_keys, chain);
241 crypto_keys->valid = 1;
242 }
243
244 return 0;
245}
246
247errno_t
248utun_ctl_unconfig_crypto_keys (__unused kern_ctl_ref kctlref,
249 __unused u_int32_t unit,
250 __unused void *unitinfo,
251 __unused int opt,
252 void *data,
253 size_t len)
254{
255 struct utun_pcb *pcb = unitinfo;
256
257 /*
258 * - verify the crypto material args passed from user-land.
259 * - check the size of the argument buffer.
260 * - check the direction (IN or OUT)
261 * - check the type (IPSec or DTLS)
262 * - crypto material direction and type must match the associated crypto context's.
263 * - we can have a list of crypto materials per context.
264 * - ensure that the crypto context is already valid (don't add crypto material to invalid context).
265 * - any error should be equivalent to noop.
266 */
267 if (len < UTUN_CRYPTO_KEYS_ARGS_HDR_SIZE) {
268 return EMSGSIZE;
269 } else {
270 int idx;
271 utun_crypto_keys_args_t *crypto_keys_args = (__typeof__(crypto_keys_args))data;
272 utun_crypto_ctx_t *crypto_ctx;
273 utun_crypto_keys_t *cur_crypto_keys, *nxt_crypto_keys;
274
275 if (crypto_keys_args->ver == 0 || crypto_keys_args->ver >= UTUN_CRYPTO_KEYS_ARGS_VER_MAX) {
276 printf("%s: ver check failed %d\n", __FUNCTION__, crypto_keys_args->ver);
277 return EINVAL;
278 }
279 if (crypto_keys_args->dir == 0 || crypto_keys_args->dir >= UTUN_CRYPTO_DIR_MAX) {
280 printf("%s: dir check failed %d\n", __FUNCTION__, crypto_keys_args->dir);
281 return EINVAL;
282 }
283 if (crypto_keys_args->type == 0 || crypto_keys_args->type >= UTUN_CRYPTO_TYPE_MAX) {
284 printf("%s: type check failed %d\n", __FUNCTION__, crypto_keys_args->type);
285 return EINVAL;
286 }
287 if (len < UTUN_CRYPTO_KEYS_ARGS_TOTAL_SIZE(crypto_keys_args)) {
288 printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
289 (int)len, (int)UTUN_CRYPTO_KEYS_ARGS_TOTAL_SIZE(crypto_keys_args));
290 return EINVAL;
291 }
292 idx = UTUN_CRYPTO_DIR_TO_IDX(crypto_keys_args->dir);
293 crypto_ctx = &pcb->utun_crypto_ctx[idx];
294 if (!crypto_ctx->valid) {
295 return EBADF;
296 }
297 if (crypto_keys_args->type != crypto_ctx->type) {
298 // can't add keymat to context with different crypto type
299 return ENOENT;
300 }
301 if (crypto_keys_args->args_ulen != sizeof(crypto_keys_args->u)) {
302 printf("%s: compatibility mode\n", __FUNCTION__);
303 }
304
305 // traverse crypto materials looking for the right one
306 for (cur_crypto_keys = (__typeof__(cur_crypto_keys))LIST_FIRST(&crypto_ctx->keys_listhead);
307 cur_crypto_keys != NULL;
308 cur_crypto_keys = nxt_crypto_keys) {
309 nxt_crypto_keys = (__typeof__(nxt_crypto_keys))LIST_NEXT(cur_crypto_keys, chain);
310 // branch-off for ipsec vs. dtls
311 if (crypto_keys_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
312 if (crypto_keys_args->u.ipsec_v1.spi == cur_crypto_keys->state.u.ipsec.spi) {
313 errno_t err;
314 if ((err = utun_ctl_unconfig_crypto_keys_ipsec(crypto_keys_args, cur_crypto_keys))) {
315 return err;
316 }
317 LIST_REMOVE(cur_crypto_keys, chain);
318 bzero(cur_crypto_keys, sizeof(*cur_crypto_keys));
319 utun_free(cur_crypto_keys);
320 return 0;
321 }
322 } else {
323 // unsupported
324 return EPROTONOSUPPORT;
325 }
326 }
327 // TODO: if there is no SA left, ensure utun can't decrypt/encrypt packets directly. it should rely on the vpnplugin for that.
328 }
329
330 return 0;
331}
332
333errno_t
334utun_ctl_generate_crypto_keys_idx (__unused kern_ctl_ref kctlref,
335 __unused u_int32_t unit,
336 __unused void *unitinfo,
337 __unused int opt,
338 void *data,
339 size_t *len)
340{
341 struct utun_pcb *pcb = unitinfo;
342
343 /*
344 * - verify the crypto material index args passed from user-land.
345 * - check the size of the argument buffer.
346 * - check the direction (IN or OUT)
347 * - check the type (IPSec or DTLS)
348 * - crypto material direction and type must match the associated crypto context's.
349 * - we can have a list of crypto materials per context.
350 * - any error should be equivalent to noop.
351 */
352 if (*len < UTUN_CRYPTO_KEYS_IDX_ARGS_HDR_SIZE) {
353 return EMSGSIZE;
354 } else {
355 int idx;
356 utun_crypto_keys_idx_args_t *crypto_keys_idx_args = (__typeof__(crypto_keys_idx_args))data;
357 utun_crypto_ctx_t *crypto_ctx;
358
359 if (crypto_keys_idx_args->ver == 0 || crypto_keys_idx_args->ver >= UTUN_CRYPTO_KEYS_ARGS_VER_MAX) {
360 printf("%s: ver check failed %d\n", __FUNCTION__, crypto_keys_idx_args->ver);
361 return EINVAL;
362 }
363 if (crypto_keys_idx_args->dir == 0 || crypto_keys_idx_args->dir >= UTUN_CRYPTO_DIR_MAX) {
364 printf("%s: dir check failed %d\n", __FUNCTION__, crypto_keys_idx_args->dir);
365 return EINVAL;
366 }
367 if (crypto_keys_idx_args->type == 0 || crypto_keys_idx_args->type >= UTUN_CRYPTO_TYPE_MAX) {
368 printf("%s: type check failed %d\n", __FUNCTION__, crypto_keys_idx_args->type);
369 return EINVAL;
370 }
371 if (*len < UTUN_CRYPTO_KEYS_IDX_ARGS_TOTAL_SIZE(crypto_keys_idx_args)) {
372 printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
373 (int)*len, (int)UTUN_CRYPTO_KEYS_IDX_ARGS_TOTAL_SIZE(crypto_keys_idx_args));
374 return EINVAL;
375 }
376 idx = UTUN_CRYPTO_DIR_TO_IDX(crypto_keys_idx_args->dir);
377 crypto_ctx = &pcb->utun_crypto_ctx[idx];
378 if (!crypto_ctx->valid) {
379 return EBADF;
380 }
381 if (crypto_keys_idx_args->type != crypto_ctx->type) {
382 // can't add keymat to context with different crypto type
383 return ENOENT;
384 }
385 if (crypto_keys_idx_args->args_ulen != sizeof(crypto_keys_idx_args->u)) {
386 printf("%s: compatibility mode\n", __FUNCTION__);
387 }
388
389 // traverse crypto materials looking for the right one
390 // branch-off for ipsec vs. dtls
391 if (crypto_keys_idx_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
392 errno_t err;
393 if ((err = utun_ctl_generate_crypto_keys_idx_ipsec(crypto_keys_idx_args))) {
394 return err;
395 }
396 } else {
397 // unsupported
398 return EPROTONOSUPPORT;
399 }
400 }
401
402 return 0;
403}
404
405errno_t
406utun_ctl_stop_crypto_data_traffic (__unused kern_ctl_ref kctlref,
407 __unused u_int32_t unit,
408 __unused void *unitinfo,
409 __unused int opt,
410 void *data,
411 size_t len)
412{
413 struct utun_pcb *pcb = unitinfo;
414
415 /*
416 * - verify the crypto context args passed from user-land.
417 * - check the size of the argument buffer.
418 * - check the direction (IN or OUT)
419 * - check the type (IPSec or DTLS)
420 * - ensure that the crypto context *is* already valid (don't release invalid context).
421 * - we have only one context per direction and type.
422 * - ensure that the crypto context has no crypto material.
423 * - any error should be equivalent to noop.
424 */
425 if (len < UTUN_CRYPTO_ARGS_HDR_SIZE) {
426 return EMSGSIZE;
427 } else {
428 utun_crypto_args_t *crypto_args = (__typeof__(crypto_args))data;
429
430 if (crypto_args->ver == 0 || crypto_args->ver >= UTUN_CRYPTO_ARGS_VER_MAX) {
431 printf("%s: ver check failed %d\n", __FUNCTION__, crypto_args->ver);
432 return EINVAL;
433 }
434 if (crypto_args->type == 0 || crypto_args->type >= UTUN_CRYPTO_TYPE_MAX) {
435 printf("%s: type check failed %d\n", __FUNCTION__, crypto_args->type);
436 return EINVAL;
437 }
438 if (len < UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args)) {
439 printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
440 (int)len, (int)UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args));
441 return EINVAL;
442 }
443 if (crypto_args->args_ulen != sizeof(crypto_args->u)) {
444 printf("%s: compatibility mode\n", __FUNCTION__);
445 }
446
447 if ((pcb->utun_flags & UTUN_FLAGS_CRYPTO) == 0) {
448 printf("%s: crypto is already disabled\n", __FUNCTION__);
449 return EINVAL;
450 }
451
452 if (crypto_args->type != UTUN_CRYPTO_TYPE_IPSEC) {
453 // unsupported
454 return EPROTONOSUPPORT;
455 }
456 }
457 pcb->utun_flags |= UTUN_FLAGS_CRYPTO_STOP_DATA_TRAFFIC;
458 return 0;
459}
460
461errno_t
462utun_ctl_start_crypto_data_traffic (__unused kern_ctl_ref kctlref,
463 __unused u_int32_t unit,
464 __unused void *unitinfo,
465 __unused int opt,
466 void *data,
467 size_t len)
468{
469 struct utun_pcb *pcb = unitinfo;
470
471 /*
472 * - verify the crypto context args passed from user-land.
473 * - check the size of the argument buffer.
474 * - check the direction (IN or OUT)
475 * - check the type (IPSec or DTLS)
476 * - ensure that the crypto context *is* already valid (don't release invalid context).
477 * - we have only one context per direction and type.
478 * - ensure that the crypto context has no crypto material.
479 * - any error should be equivalent to noop.
480 */
481 if (len < UTUN_CRYPTO_ARGS_HDR_SIZE) {
482 return EMSGSIZE;
483 } else {
484 utun_crypto_args_t *crypto_args = (__typeof__(crypto_args))data;
485
486 if (crypto_args->ver == 0 || crypto_args->ver >= UTUN_CRYPTO_ARGS_VER_MAX) {
487 printf("%s: ver check failed %d\n", __FUNCTION__, crypto_args->ver);
488 return EINVAL;
489 }
490 if (crypto_args->type == 0 || crypto_args->type >= UTUN_CRYPTO_TYPE_MAX) {
491 printf("%s: type check failed %d\n", __FUNCTION__, crypto_args->type);
492 return EINVAL;
493 }
494 if (len < UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args)) {
495 printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
496 (int)len, (int)UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args));
497 return EINVAL;
498 }
499 if (crypto_args->args_ulen != sizeof(crypto_args->u)) {
500 printf("%s: compatibility mode\n", __FUNCTION__);
501 }
502
503 if ((pcb->utun_flags & UTUN_FLAGS_CRYPTO) == 0) {
504 printf("%s: crypto is already disabled\n", __FUNCTION__);
505 return EINVAL;
506 }
507
508 if (crypto_args->type != UTUN_CRYPTO_TYPE_IPSEC) {
509 // unsupported
510 return EPROTONOSUPPORT;
511 }
512 }
513 pcb->utun_flags &= ~UTUN_FLAGS_CRYPTO_STOP_DATA_TRAFFIC;
514 return 0;
515}
516
517int
518utun_pkt_crypto_output (struct utun_pcb *pcb, mbuf_t *m)
519{
520 int idx = UTUN_CRYPTO_DIR_TO_IDX(UTUN_CRYPTO_DIR_OUT);
521 if (!pcb->utun_crypto_ctx[idx].valid) {
522 printf("%s: context is invalid %d\n", __FUNCTION__, pcb->utun_crypto_ctx[idx].valid);
523 return -1;
524 }
525 if (pcb->utun_crypto_ctx[idx].type == UTUN_CRYPTO_TYPE_IPSEC) {
526 return(utun_pkt_ipsec_output(pcb, m));
527 } else {
528 // unsupported
529 printf("%s: type is invalid %d\n", __FUNCTION__, pcb->utun_crypto_ctx[idx].type);
530 }
531 return -1;
532}