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}
533