]> git.saurik.com Git - apple/xnu.git/blob - bsd/net/if_utun_crypto.c
xnu-3247.10.11.tar.gz
[apple/xnu.git] / bsd / net / if_utun_crypto.c
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 #include <net/if_utun_crypto_dtls.h>
39
40 void
41 utun_ctl_init_crypto (void)
42 {
43 utun_ctl_init_crypto_dtls();
44 }
45
46 void
47 utun_cleanup_crypto (struct utun_pcb *pcb)
48 {
49 #if IPSEC
50 utun_cleanup_all_crypto_ipsec(pcb);
51 #endif
52 utun_cleanup_all_crypto_dtls(pcb);
53 pcb->utun_flags &= ~UTUN_FLAGS_CRYPTO;
54 }
55
56 errno_t
57 utun_ctl_enable_crypto (__unused kern_ctl_ref kctlref,
58 __unused u_int32_t unit,
59 __unused void *unitinfo,
60 __unused int opt,
61 void *data,
62 size_t len)
63 {
64 struct utun_pcb *pcb = unitinfo;
65
66 /*
67 * - verify the crypto context args passed from user-land.
68 * - check the size of the argument buffer.
69 * - check the direction (IN or OUT)
70 * - check the type (IPSec or DTLS)
71 * - ensure that the crypto context is *not* already valid (don't recreate already valid context).
72 * - we have only one context per direction and type.
73 * - any error should be equivalent to noop.
74 */
75 if (len < UTUN_CRYPTO_ARGS_HDR_SIZE) {
76 return EMSGSIZE;
77 } else {
78 int idx;
79 utun_crypto_args_t *crypto_args = (__typeof__(crypto_args))data;
80 utun_crypto_ctx_t *crypto_ctx;
81
82 if (crypto_args->ver == 0 || crypto_args->ver >= UTUN_CRYPTO_ARGS_VER_MAX) {
83 printf("%s: ver check failed %d\n", __FUNCTION__, crypto_args->ver);
84 return EINVAL;
85 }
86 if (crypto_args->type == 0 || crypto_args->type >= UTUN_CRYPTO_TYPE_MAX) {
87 printf("%s: type check failed %d\n", __FUNCTION__, crypto_args->type);
88 return EINVAL;
89 }
90 if (len < UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args)) {
91 printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
92 (int)len, (int)UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args));
93 return EINVAL;
94 }
95 if (crypto_args->args_ulen != sizeof(crypto_args->u)) {
96 printf("%s: compatibility mode\n", __FUNCTION__);
97 }
98
99 #if IPSEC
100 if (crypto_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
101 utun_ctl_enable_crypto_ipsec(pcb, crypto_args);
102 } else
103 #endif
104 if (crypto_args->type == UTUN_CRYPTO_TYPE_DTLS) {
105 utun_ctl_enable_crypto_dtls(pcb, crypto_args);
106 } else {
107 // unsupported
108 return EPROTONOSUPPORT;
109 }
110 for (idx = 0; idx < UTUN_CRYPTO_DIR_TO_IDX(UTUN_CRYPTO_DIR_MAX); idx++) {
111 crypto_ctx = &pcb->utun_crypto_ctx[idx];
112 if (crypto_ctx->valid) {
113 return EBADF;
114 }
115
116 crypto_ctx->type = crypto_args->type;
117 LIST_INIT(&crypto_ctx->keys_listhead);
118 LIST_INIT(&crypto_ctx->framer_listheads[UTUN_CRYPTO_INNER_TYPE_TO_IDX(UTUN_CRYPTO_INNER_TYPE_IPv4)]);
119 LIST_INIT(&crypto_ctx->framer_listheads[UTUN_CRYPTO_INNER_TYPE_TO_IDX(UTUN_CRYPTO_INNER_TYPE_IPv6)]);
120 crypto_ctx->valid = 1;
121 printf("%s: initialized framer lists\n", __FUNCTION__);
122 }
123 // data traffic is stopped by default
124 pcb->utun_flags |= (UTUN_FLAGS_CRYPTO | UTUN_FLAGS_CRYPTO_STOP_DATA_TRAFFIC);
125 return 0;
126 }
127 }
128
129 errno_t
130 utun_ctl_disable_crypto (__unused kern_ctl_ref kctlref,
131 __unused u_int32_t unit,
132 __unused void *unitinfo,
133 __unused int opt,
134 void *data,
135 size_t len)
136 {
137 struct utun_pcb *pcb = unitinfo;
138
139 /*
140 * - verify the crypto context args passed from user-land.
141 * - check the size of the argument buffer.
142 * - check the direction (IN or OUT)
143 * - check the type (IPSec or DTLS)
144 * - ensure that the crypto context *is* already valid (don't release invalid context).
145 * - we have only one context per direction and type.
146 * - ensure that the crypto context has no crypto material.
147 * - any error should be equivalent to noop.
148 */
149 if (len < UTUN_CRYPTO_ARGS_HDR_SIZE) {
150 return EMSGSIZE;
151 } else {
152 utun_crypto_args_t *crypto_args = (__typeof__(crypto_args))data;
153
154 if (crypto_args->ver == 0 || crypto_args->ver >= UTUN_CRYPTO_ARGS_VER_MAX) {
155 printf("%s: ver check failed %d\n", __FUNCTION__, crypto_args->ver);
156 return EINVAL;
157 }
158 if (crypto_args->type == 0 || crypto_args->type >= UTUN_CRYPTO_TYPE_MAX) {
159 printf("%s: type check failed %d\n", __FUNCTION__, crypto_args->type);
160 return EINVAL;
161 }
162 if (len < UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args)) {
163 printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
164 (int)len, (int)UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args));
165 return EINVAL;
166 }
167 if (crypto_args->args_ulen != sizeof(crypto_args->u)) {
168 printf("%s: compatibility mode\n", __FUNCTION__);
169 }
170
171 #if IPSEC
172 if (crypto_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
173 utun_ctl_disable_crypto_ipsec(pcb);
174 } else
175 #endif
176 if (crypto_args->type == UTUN_CRYPTO_TYPE_DTLS) {
177 utun_ctl_disable_crypto_dtls(pcb);
178 } else {
179 // unsupported
180 return EPROTONOSUPPORT;
181 }
182 }
183 pcb->utun_flags &= ~(UTUN_FLAGS_CRYPTO | UTUN_FLAGS_CRYPTO_STOP_DATA_TRAFFIC);
184 return 0;
185 }
186
187 errno_t
188 utun_ctl_config_crypto_keys (__unused kern_ctl_ref kctlref,
189 __unused u_int32_t unit,
190 __unused void *unitinfo,
191 __unused int opt,
192 void *data,
193 size_t len)
194 {
195 struct utun_pcb *pcb = unitinfo;
196
197 /*
198 * - verify the crypto material args passed from user-land.
199 * - check the size of the argument buffer.
200 * - check the direction (IN or OUT)
201 * - check the type (IPSec only)
202 * - crypto material direction and type must match the associated crypto context's.
203 * - we can have a list of crypto materials per context.
204 * - ensure that the crypto context is already valid (don't add crypto material to invalid context).
205 * - any error should be equivalent to noop.
206 */
207 if (len < UTUN_CRYPTO_KEYS_ARGS_HDR_SIZE) {
208 return EMSGSIZE;
209 } else {
210 int idx;
211 utun_crypto_keys_args_t *crypto_keys_args = (__typeof__(crypto_keys_args))data;
212 utun_crypto_ctx_t *crypto_ctx;
213 utun_crypto_keys_t *crypto_keys = NULL;
214
215 if (crypto_keys_args->ver == 0 || crypto_keys_args->ver >= UTUN_CRYPTO_KEYS_ARGS_VER_MAX) {
216 printf("%s: ver check failed %d\n", __FUNCTION__, crypto_keys_args->ver);
217 return EINVAL;
218 }
219 if (crypto_keys_args->dir == 0 || crypto_keys_args->dir >= UTUN_CRYPTO_DIR_MAX) {
220 printf("%s: dir check failed %d\n", __FUNCTION__, crypto_keys_args->dir);
221 return EINVAL;
222 }
223 if (crypto_keys_args->type == 0 || crypto_keys_args->type >= UTUN_CRYPTO_TYPE_MAX) {
224 printf("%s: type check failed %d\n", __FUNCTION__, crypto_keys_args->type);
225 return EINVAL;
226 }
227 if (len < UTUN_CRYPTO_KEYS_ARGS_TOTAL_SIZE(crypto_keys_args)) {
228 printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
229 (int)len, (int)UTUN_CRYPTO_KEYS_ARGS_TOTAL_SIZE(crypto_keys_args));
230 return EINVAL;
231 }
232 idx = UTUN_CRYPTO_DIR_TO_IDX(crypto_keys_args->dir);
233 crypto_ctx = &pcb->utun_crypto_ctx[idx];
234 if (!crypto_ctx->valid) {
235 return EBADF;
236 }
237 if (crypto_keys_args->type != crypto_ctx->type) {
238 // can't add keymat to context with different crypto type
239 return ENOENT;
240 }
241 crypto_keys = utun_alloc(sizeof(*crypto_keys));
242 if (!crypto_keys) {
243 return ENOBUFS;
244 }
245 bzero(crypto_keys, sizeof(*crypto_keys));
246 if (crypto_keys_args->args_ulen != sizeof(crypto_keys_args->u)) {
247 printf("%s: compatibility mode\n", __FUNCTION__);
248 }
249
250 // branch-off for ipsec vs. dtls
251 #if IPSEC
252 if (crypto_keys_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
253 errno_t err;
254 if ((err = utun_ctl_config_crypto_keys_ipsec(pcb, crypto_keys_args, crypto_keys))) {
255 utun_free(crypto_keys);
256 return err;
257 }
258 } else
259 #endif
260 {
261 // unsupported
262 utun_free(crypto_keys);
263 return EPROTONOSUPPORT;
264 }
265 crypto_keys->type = crypto_keys_args->type;
266 LIST_INSERT_HEAD(&crypto_ctx->keys_listhead, crypto_keys, chain);
267 crypto_keys->valid = 1;
268 }
269
270 return 0;
271 }
272
273 errno_t
274 utun_ctl_unconfig_crypto_keys (__unused kern_ctl_ref kctlref,
275 __unused u_int32_t unit,
276 __unused void *unitinfo,
277 __unused int opt,
278 void *data,
279 size_t len)
280 {
281 struct utun_pcb *pcb = unitinfo;
282
283 /*
284 * - verify the crypto material args passed from user-land.
285 * - check the size of the argument buffer.
286 * - check the direction (IN or OUT)
287 * - check the type (IPSec only)
288 * - crypto material direction and type must match the associated crypto context's.
289 * - we can have a list of crypto materials per context.
290 * - ensure that the crypto context is already valid (don't add crypto material to invalid context).
291 * - any error should be equivalent to noop.
292 */
293 if (len < UTUN_CRYPTO_KEYS_ARGS_HDR_SIZE) {
294 return EMSGSIZE;
295 } else {
296 int idx;
297 utun_crypto_keys_args_t *crypto_keys_args = (__typeof__(crypto_keys_args))data;
298 utun_crypto_ctx_t *crypto_ctx;
299 utun_crypto_keys_t *cur_crypto_keys, *nxt_crypto_keys;
300
301 if (crypto_keys_args->ver == 0 || crypto_keys_args->ver >= UTUN_CRYPTO_KEYS_ARGS_VER_MAX) {
302 printf("%s: ver check failed %d\n", __FUNCTION__, crypto_keys_args->ver);
303 return EINVAL;
304 }
305 if (crypto_keys_args->dir == 0 || crypto_keys_args->dir >= UTUN_CRYPTO_DIR_MAX) {
306 printf("%s: dir check failed %d\n", __FUNCTION__, crypto_keys_args->dir);
307 return EINVAL;
308 }
309 if (crypto_keys_args->type == 0 || crypto_keys_args->type >= UTUN_CRYPTO_TYPE_MAX) {
310 printf("%s: type check failed %d\n", __FUNCTION__, crypto_keys_args->type);
311 return EINVAL;
312 }
313 if (len < UTUN_CRYPTO_KEYS_ARGS_TOTAL_SIZE(crypto_keys_args)) {
314 printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
315 (int)len, (int)UTUN_CRYPTO_KEYS_ARGS_TOTAL_SIZE(crypto_keys_args));
316 return EINVAL;
317 }
318 idx = UTUN_CRYPTO_DIR_TO_IDX(crypto_keys_args->dir);
319 crypto_ctx = &pcb->utun_crypto_ctx[idx];
320 if (!crypto_ctx->valid) {
321 return EBADF;
322 }
323 if (crypto_keys_args->type != crypto_ctx->type) {
324 // can't add keymat to context with different crypto type
325 return ENOENT;
326 }
327 if (crypto_keys_args->args_ulen != sizeof(crypto_keys_args->u)) {
328 printf("%s: compatibility mode\n", __FUNCTION__);
329 }
330
331 // traverse crypto materials looking for the right one
332 for (cur_crypto_keys = (__typeof__(cur_crypto_keys))LIST_FIRST(&crypto_ctx->keys_listhead);
333 cur_crypto_keys != NULL;
334 cur_crypto_keys = nxt_crypto_keys) {
335 nxt_crypto_keys = (__typeof__(nxt_crypto_keys))LIST_NEXT(cur_crypto_keys, chain);
336 // branch-off for ipsec vs. dtls
337 #if IPSEC
338 if (crypto_keys_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
339 if (crypto_keys_args->u.ipsec_v1.spi == cur_crypto_keys->state.u.ipsec.spi) {
340 errno_t err;
341 if ((err = utun_ctl_unconfig_crypto_keys_ipsec(crypto_keys_args, cur_crypto_keys))) {
342 return err;
343 }
344 LIST_REMOVE(cur_crypto_keys, chain);
345 bzero(cur_crypto_keys, sizeof(*cur_crypto_keys));
346 utun_free(cur_crypto_keys);
347 return 0;
348 }
349 } else
350 #endif
351 {
352 // unsupported
353 return EPROTONOSUPPORT;
354 }
355 }
356 // TODO: if there is no SA left, ensure utun can't decrypt/encrypt packets directly. it should rely on the vpnplugin for that.
357 }
358
359 return 0;
360 }
361
362 errno_t
363 utun_ctl_config_crypto_framer (__unused kern_ctl_ref kctlref,
364 __unused u_int32_t unit,
365 __unused void *unitinfo,
366 __unused int opt,
367 void *data,
368 size_t len)
369 {
370 struct utun_pcb *pcb = unitinfo;
371
372 /*
373 * - verify the crypto material args passed from user-land.
374 * - check the size of the argument buffer.
375 * - check the direction (IN or OUT)
376 * - check the type (DTLS only)
377 * - crypto material direction and type must match the associated crypto context's.
378 * - we can have a list of crypto materials per context.
379 * - ensure that the crypto context is already valid (don't add crypto material to invalid context).
380 * - any error should be equivalent to noop.
381 */
382 if (len < UTUN_CRYPTO_FRAMER_ARGS_HDR_SIZE) {
383 return EMSGSIZE;
384 } else {
385 int idx;
386 utun_crypto_framer_args_t *framer_args = (__typeof__(framer_args))data;
387 utun_crypto_ctx_t *crypto_ctx;
388
389 if (framer_args->ver == 0 || framer_args->ver >= UTUN_CRYPTO_FRAMER_ARGS_VER_MAX) {
390 printf("%s: ver check failed %d\n", __FUNCTION__, (int)framer_args->ver);
391 return EINVAL;
392 }
393 if (framer_args->dir == 0 || framer_args->dir >= UTUN_CRYPTO_DIR_MAX) {
394 printf("%s: dir check failed %d\n", __FUNCTION__, (int)framer_args->dir);
395 return EINVAL;
396 }
397 if (framer_args->type == 0 || framer_args->type >= UTUN_CRYPTO_TYPE_MAX) {
398 printf("%s: type check failed %d\n", __FUNCTION__, (int)framer_args->type);
399 return EINVAL;
400 }
401 if (len < UTUN_CRYPTO_FRAMER_ARGS_TOTAL_SIZE(framer_args)) {
402 printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
403 (int)len, (int)UTUN_CRYPTO_FRAMER_ARGS_TOTAL_SIZE(framer_args));
404 return EINVAL;
405 }
406 idx = UTUN_CRYPTO_DIR_TO_IDX(framer_args->dir);
407 crypto_ctx = &pcb->utun_crypto_ctx[idx];
408 if (!crypto_ctx->valid) {
409 return EBADF;
410 }
411 if (framer_args->type != crypto_ctx->type) {
412 // can't add keymat to context with different crypto type
413 return ENOENT;
414 }
415 if (framer_args->args_ulen != sizeof(framer_args->u)) {
416 printf("%s: compatibility mode\n", __FUNCTION__);
417 // TODO:
418 }
419
420 // branch-off for ipsec vs. dtls
421 if (framer_args->type == UTUN_CRYPTO_TYPE_DTLS) {
422 errno_t err;
423 if ((err = utun_ctl_config_crypto_dtls_framer(crypto_ctx, framer_args))) {
424 return err;
425 }
426 } else {
427 // unsupported
428 return EPROTONOSUPPORT;
429 }
430 }
431
432 return 0;
433 }
434
435 errno_t
436 utun_ctl_unconfig_crypto_framer (__unused kern_ctl_ref kctlref,
437 __unused u_int32_t unit,
438 __unused void *unitinfo,
439 __unused int opt,
440 void *data,
441 size_t len)
442 {
443 struct utun_pcb *pcb = unitinfo;
444
445 /*
446 * - verify the crypto material args passed from user-land.
447 * - check the size of the argument buffer.
448 * - check the direction (IN or OUT)
449 * - check the type (DTLS only)
450 * - crypto material direction and type must match the associated crypto context's.
451 * - we can have a list of crypto materials per context.
452 * - ensure that the crypto context is already valid (don't add crypto material to invalid context).
453 * - any error should be equivalent to noop.
454 */
455 if (len < UTUN_CRYPTO_FRAMER_ARGS_HDR_SIZE) {
456 return EMSGSIZE;
457 } else {
458 int idx;
459 utun_crypto_framer_args_t *framer_args = (__typeof__(framer_args))data;
460 utun_crypto_ctx_t *crypto_ctx;
461
462 if (framer_args->ver == 0 || framer_args->ver >= UTUN_CRYPTO_FRAMER_ARGS_VER_MAX) {
463 printf("%s: ver check failed %d\n", __FUNCTION__, (int)framer_args->ver);
464 return EINVAL;
465 }
466 if (framer_args->dir == 0 || framer_args->dir >= UTUN_CRYPTO_DIR_MAX) {
467 printf("%s: dir check failed %d\n", __FUNCTION__, (int)framer_args->dir);
468 return EINVAL;
469 }
470 if (framer_args->type == 0 || framer_args->type >= UTUN_CRYPTO_TYPE_MAX) {
471 printf("%s: type check failed %d\n", __FUNCTION__, (int)framer_args->type);
472 return EINVAL;
473 }
474 if (len < UTUN_CRYPTO_FRAMER_ARGS_TOTAL_SIZE(framer_args)) {
475 printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
476 (int)len, (int)UTUN_CRYPTO_FRAMER_ARGS_TOTAL_SIZE(framer_args));
477 return EINVAL;
478 }
479 idx = UTUN_CRYPTO_DIR_TO_IDX(framer_args->dir);
480 crypto_ctx = &pcb->utun_crypto_ctx[idx];
481 if (!crypto_ctx->valid) {
482 return EBADF;
483 }
484 if (framer_args->type != crypto_ctx->type) {
485 // can't add keymat to context with different crypto type
486 return ENOENT;
487 }
488 if (framer_args->args_ulen != sizeof(framer_args->u)) {
489 printf("%s: compatibility mode\n", __FUNCTION__);
490 }
491
492 // branch-off for ipsec vs. dtls
493 if (framer_args->type == UTUN_CRYPTO_TYPE_DTLS) {
494 errno_t err;
495 if ((err = utun_ctl_unconfig_crypto_dtls_framer(crypto_ctx, framer_args))) {
496 return err;
497 }
498 } else {
499 // unsupported
500 return EPROTONOSUPPORT;
501 }
502 }
503
504 return 0;
505 }
506
507 errno_t
508 utun_ctl_generate_crypto_keys_idx (__unused kern_ctl_ref kctlref,
509 __unused u_int32_t unit,
510 __unused void *unitinfo,
511 __unused int opt,
512 void *data,
513 size_t *len)
514 {
515 struct utun_pcb *pcb = unitinfo;
516
517 /*
518 * - verify the crypto material index args passed from user-land.
519 * - check the size of the argument buffer.
520 * - check the direction (IN or OUT)
521 * - check the type (IPSec only)
522 * - crypto material direction and type must match the associated crypto context's.
523 * - we can have a list of crypto materials per context.
524 * - any error should be equivalent to noop.
525 */
526 if (*len < UTUN_CRYPTO_KEYS_IDX_ARGS_HDR_SIZE) {
527 return EMSGSIZE;
528 } else {
529 int idx;
530 utun_crypto_keys_idx_args_t *crypto_keys_idx_args = (__typeof__(crypto_keys_idx_args))data;
531 utun_crypto_ctx_t *crypto_ctx;
532
533 if (crypto_keys_idx_args->ver == 0 || crypto_keys_idx_args->ver >= UTUN_CRYPTO_KEYS_ARGS_VER_MAX) {
534 printf("%s: ver check failed %d\n", __FUNCTION__, crypto_keys_idx_args->ver);
535 return EINVAL;
536 }
537 if (crypto_keys_idx_args->dir == 0 || crypto_keys_idx_args->dir >= UTUN_CRYPTO_DIR_MAX) {
538 printf("%s: dir check failed %d\n", __FUNCTION__, crypto_keys_idx_args->dir);
539 return EINVAL;
540 }
541 if (crypto_keys_idx_args->type == 0 || crypto_keys_idx_args->type >= UTUN_CRYPTO_TYPE_MAX) {
542 printf("%s: type check failed %d\n", __FUNCTION__, crypto_keys_idx_args->type);
543 return EINVAL;
544 }
545 if (*len < UTUN_CRYPTO_KEYS_IDX_ARGS_TOTAL_SIZE(crypto_keys_idx_args)) {
546 printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
547 (int)*len, (int)UTUN_CRYPTO_KEYS_IDX_ARGS_TOTAL_SIZE(crypto_keys_idx_args));
548 return EINVAL;
549 }
550 idx = UTUN_CRYPTO_DIR_TO_IDX(crypto_keys_idx_args->dir);
551 crypto_ctx = &pcb->utun_crypto_ctx[idx];
552 if (!crypto_ctx->valid) {
553 return EBADF;
554 }
555 if (crypto_keys_idx_args->type != crypto_ctx->type) {
556 // can't add keymat to context with different crypto type
557 return ENOENT;
558 }
559 if (crypto_keys_idx_args->args_ulen != sizeof(crypto_keys_idx_args->u)) {
560 printf("%s: compatibility mode\n", __FUNCTION__);
561 }
562
563 // traverse crypto materials looking for the right one
564 // branch-off for ipsec vs. dtls
565 #if IPSEC
566 if (crypto_keys_idx_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
567 errno_t err;
568 if ((err = utun_ctl_generate_crypto_keys_idx_ipsec(crypto_keys_idx_args))) {
569 return err;
570 }
571 } else
572 #endif
573 {
574 // unsupported
575 return EPROTONOSUPPORT;
576 }
577 }
578
579 return 0;
580 }
581
582 errno_t
583 utun_ctl_stop_crypto_data_traffic (__unused kern_ctl_ref kctlref,
584 __unused u_int32_t unit,
585 __unused void *unitinfo,
586 __unused int opt,
587 void *data,
588 size_t len)
589 {
590 struct utun_pcb *pcb = unitinfo;
591
592 /*
593 * - verify the crypto context args passed from user-land.
594 * - check the size of the argument buffer.
595 * - check the direction (IN or OUT)
596 * - check the type (IPSec or DTLS)
597 * - ensure that the crypto context *is* already valid (don't release invalid context).
598 * - we have only one context per direction and type.
599 * - ensure that the crypto context has no crypto material.
600 * - any error should be equivalent to noop.
601 */
602 if (len < UTUN_CRYPTO_ARGS_HDR_SIZE) {
603 return EMSGSIZE;
604 } else {
605 utun_crypto_args_t *crypto_args = (__typeof__(crypto_args))data;
606
607 if (crypto_args->ver == 0 || crypto_args->ver >= UTUN_CRYPTO_ARGS_VER_MAX) {
608 printf("%s: ver check failed %d\n", __FUNCTION__, crypto_args->ver);
609 return EINVAL;
610 }
611 if (crypto_args->type == 0 || crypto_args->type >= UTUN_CRYPTO_TYPE_MAX) {
612 printf("%s: type check failed %d\n", __FUNCTION__, crypto_args->type);
613 return EINVAL;
614 }
615 if (len < UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args)) {
616 printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
617 (int)len, (int)UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args));
618 return EINVAL;
619 }
620 if (crypto_args->args_ulen != sizeof(crypto_args->u)) {
621 printf("%s: compatibility mode\n", __FUNCTION__);
622 }
623
624 if ((pcb->utun_flags & UTUN_FLAGS_CRYPTO) == 0) {
625 printf("%s: crypto is already disabled\n", __FUNCTION__);
626 return EINVAL;
627 }
628
629 if (crypto_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
630 // nothing
631 } else if (crypto_args->type == UTUN_CRYPTO_TYPE_DTLS) {
632 utun_ctl_stop_datatraffic_crypto_dtls(pcb);
633 } else {
634 // unsupported
635 return EPROTONOSUPPORT;
636 }
637 }
638 pcb->utun_flags |= UTUN_FLAGS_CRYPTO_STOP_DATA_TRAFFIC;
639 return 0;
640 }
641
642 errno_t
643 utun_ctl_start_crypto_data_traffic (__unused kern_ctl_ref kctlref,
644 __unused u_int32_t unit,
645 __unused void *unitinfo,
646 __unused int opt,
647 void *data,
648 size_t len)
649 {
650 struct utun_pcb *pcb = unitinfo;
651
652 /*
653 * - verify the crypto context args passed from user-land.
654 * - check the size of the argument buffer.
655 * - check the direction (IN or OUT)
656 * - check the type (IPSec or DTLS)
657 * - ensure that the crypto context *is* already valid (don't release invalid context).
658 * - we have only one context per direction and type.
659 * - ensure that the crypto context has no crypto material.
660 * - any error should be equivalent to noop.
661 */
662 if (len < UTUN_CRYPTO_ARGS_HDR_SIZE) {
663 return EMSGSIZE;
664 } else {
665 utun_crypto_args_t *crypto_args = (__typeof__(crypto_args))data;
666
667 if (crypto_args->ver == 0 || crypto_args->ver >= UTUN_CRYPTO_ARGS_VER_MAX) {
668 printf("%s: ver check failed %d\n", __FUNCTION__, crypto_args->ver);
669 return EINVAL;
670 }
671 if (crypto_args->type == 0 || crypto_args->type >= UTUN_CRYPTO_TYPE_MAX) {
672 printf("%s: type check failed %d\n", __FUNCTION__, crypto_args->type);
673 return EINVAL;
674 }
675 if (len < UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args)) {
676 printf("%s: vlen check failed (%d,%d)\n", __FUNCTION__,
677 (int)len, (int)UTUN_CRYPTO_ARGS_TOTAL_SIZE(crypto_args));
678 return EINVAL;
679 }
680 if (crypto_args->args_ulen != sizeof(crypto_args->u)) {
681 printf("%s: compatibility mode\n", __FUNCTION__);
682 }
683
684 if ((pcb->utun_flags & UTUN_FLAGS_CRYPTO) == 0) {
685 printf("%s: crypto is already disabled\n", __FUNCTION__);
686 return EINVAL;
687 }
688
689 if (crypto_args->type == UTUN_CRYPTO_TYPE_IPSEC) {
690 // nothing
691 } else if (crypto_args->type == UTUN_CRYPTO_TYPE_DTLS) {
692 utun_ctl_start_datatraffic_crypto_dtls(pcb);
693 } else {
694 // unsupported
695 return EPROTONOSUPPORT;
696 }
697 }
698 pcb->utun_flags &= ~UTUN_FLAGS_CRYPTO_STOP_DATA_TRAFFIC;
699 return 0;
700 }
701
702 int
703 utun_pkt_crypto_output (struct utun_pcb *pcb, mbuf_t *m)
704 {
705 int idx = UTUN_CRYPTO_DIR_TO_IDX(UTUN_CRYPTO_DIR_OUT);
706 if (!pcb->utun_crypto_ctx[idx].valid) {
707 printf("%s: context is invalid %d\n", __FUNCTION__, pcb->utun_crypto_ctx[idx].valid);
708 return -1;
709 }
710 #if IPSEC
711 if (pcb->utun_crypto_ctx[idx].type == UTUN_CRYPTO_TYPE_IPSEC) {
712 return(utun_pkt_ipsec_output(pcb, m));
713 } else
714 #endif
715 if (pcb->utun_crypto_ctx[idx].type == UTUN_CRYPTO_TYPE_DTLS) {
716 return(utun_pkt_dtls_output(pcb, m));
717 } else {
718 // unsupported
719 printf("%s: type is invalid %d\n", __FUNCTION__, pcb->utun_crypto_ctx[idx].type);
720 }
721 return -1;
722 }