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
40void
41utun_ctl_init_crypto (void)
42{
43	utun_ctl_init_crypto_dtls();
44}
45
46void
47utun_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
56errno_t
57utun_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
129errno_t
130utun_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
187errno_t
188utun_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
273errno_t
274utun_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
362errno_t
363utun_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
435errno_t
436utun_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
507errno_t
508utun_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
582errno_t
583utun_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
642errno_t
643utun_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
702int
703utun_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}
723