2 * Copyright (c) 2011 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
31 #include <sys/systm.h>
33 #include <net/if_types.h>
34 #include <net/if_utun.h>
36 #include <net/if_utun_crypto.h>
37 #include <net/if_utun_crypto_ipsec.h>
40 utun_cleanup_crypto (struct utun_pcb
*pcb
)
42 utun_cleanup_all_crypto_ipsec(pcb
);
43 // utun_cleanup_all_crypto_dtls(pcb);
44 pcb
->utun_flags
&= ~UTUN_FLAGS_CRYPTO
;
48 utun_ctl_enable_crypto (__unused kern_ctl_ref kctlref
,
49 __unused u_int32_t unit
,
50 __unused
void *unitinfo
,
55 struct utun_pcb
*pcb
= unitinfo
;
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.
66 if (len
< UTUN_CRYPTO_ARGS_HDR_SIZE
) {
70 utun_crypto_args_t
*crypto_args
= (__typeof__(crypto_args
))data
;
71 utun_crypto_ctx_t
*crypto_ctx
;
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
);
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
);
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
));
86 if (crypto_args
->args_ulen
!= sizeof(crypto_args
->u
)) {
87 printf("%s: compatibility mode\n", __FUNCTION__
);
89 if (crypto_args
->type
== UTUN_CRYPTO_TYPE_IPSEC
) {
90 utun_ctl_enable_crypto_ipsec(pcb
, crypto_args
);
93 return EPROTONOSUPPORT
;
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
) {
101 crypto_ctx
->type
= crypto_args
->type
;
102 LIST_INIT(&crypto_ctx
->keys_listhead
);
103 crypto_ctx
->valid
= 1;
105 // data traffic is stopped by default
106 pcb
->utun_flags
|= (UTUN_FLAGS_CRYPTO
| UTUN_FLAGS_CRYPTO_STOP_DATA_TRAFFIC
);
112 utun_ctl_disable_crypto (__unused kern_ctl_ref kctlref
,
113 __unused u_int32_t unit
,
114 __unused
void *unitinfo
,
119 struct utun_pcb
*pcb
= unitinfo
;
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.
131 if (len
< UTUN_CRYPTO_ARGS_HDR_SIZE
) {
134 utun_crypto_args_t
*crypto_args
= (__typeof__(crypto_args
))data
;
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
);
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
);
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
));
149 if (crypto_args
->args_ulen
!= sizeof(crypto_args
->u
)) {
150 printf("%s: compatibility mode\n", __FUNCTION__
);
153 if (crypto_args
->type
== UTUN_CRYPTO_TYPE_IPSEC
) {
154 utun_ctl_disable_crypto_ipsec(pcb
);
157 return EPROTONOSUPPORT
;
160 pcb
->utun_flags
&= ~(UTUN_FLAGS_CRYPTO
| UTUN_FLAGS_CRYPTO_STOP_DATA_TRAFFIC
);
165 utun_ctl_config_crypto_keys (__unused kern_ctl_ref kctlref
,
166 __unused u_int32_t unit
,
167 __unused
void *unitinfo
,
172 struct utun_pcb
*pcb
= unitinfo
;
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.
184 if (len
< UTUN_CRYPTO_KEYS_ARGS_HDR_SIZE
) {
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
;
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
);
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
);
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
);
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
));
209 idx
= UTUN_CRYPTO_DIR_TO_IDX(crypto_keys_args
->dir
);
210 crypto_ctx
= &pcb
->utun_crypto_ctx
[idx
];
211 if (!crypto_ctx
->valid
) {
214 if (crypto_keys_args
->type
!= crypto_ctx
->type
) {
215 // can't add keymat to context with different crypto type
218 crypto_keys
= utun_alloc(sizeof(*crypto_keys
));
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__
);
227 // branch-off for ipsec vs. dtls
228 if (crypto_keys_args
->type
== UTUN_CRYPTO_TYPE_IPSEC
) {
230 if ((err
= utun_ctl_config_crypto_keys_ipsec(pcb
, crypto_keys_args
, crypto_keys
))) {
231 utun_free(crypto_keys
);
236 utun_free(crypto_keys
);
237 return EPROTONOSUPPORT
;
239 crypto_keys
->type
= crypto_keys_args
->type
;
240 LIST_INSERT_HEAD(&crypto_ctx
->keys_listhead
, crypto_keys
, chain
);
241 crypto_keys
->valid
= 1;
248 utun_ctl_unconfig_crypto_keys (__unused kern_ctl_ref kctlref
,
249 __unused u_int32_t unit
,
250 __unused
void *unitinfo
,
255 struct utun_pcb
*pcb
= unitinfo
;
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.
267 if (len
< UTUN_CRYPTO_KEYS_ARGS_HDR_SIZE
) {
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
;
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
);
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
);
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
);
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
));
292 idx
= UTUN_CRYPTO_DIR_TO_IDX(crypto_keys_args
->dir
);
293 crypto_ctx
= &pcb
->utun_crypto_ctx
[idx
];
294 if (!crypto_ctx
->valid
) {
297 if (crypto_keys_args
->type
!= crypto_ctx
->type
) {
298 // can't add keymat to context with different crypto type
301 if (crypto_keys_args
->args_ulen
!= sizeof(crypto_keys_args
->u
)) {
302 printf("%s: compatibility mode\n", __FUNCTION__
);
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
) {
314 if ((err
= utun_ctl_unconfig_crypto_keys_ipsec(crypto_keys_args
, cur_crypto_keys
))) {
317 LIST_REMOVE(cur_crypto_keys
, chain
);
318 bzero(cur_crypto_keys
, sizeof(*cur_crypto_keys
));
319 utun_free(cur_crypto_keys
);
324 return EPROTONOSUPPORT
;
327 // TODO: if there is no SA left, ensure utun can't decrypt/encrypt packets directly. it should rely on the vpnplugin for that.
334 utun_ctl_generate_crypto_keys_idx (__unused kern_ctl_ref kctlref
,
335 __unused u_int32_t unit
,
336 __unused
void *unitinfo
,
341 struct utun_pcb
*pcb
= unitinfo
;
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.
352 if (*len
< UTUN_CRYPTO_KEYS_IDX_ARGS_HDR_SIZE
) {
356 utun_crypto_keys_idx_args_t
*crypto_keys_idx_args
= (__typeof__(crypto_keys_idx_args
))data
;
357 utun_crypto_ctx_t
*crypto_ctx
;
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
);
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
);
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
);
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
));
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
) {
381 if (crypto_keys_idx_args
->type
!= crypto_ctx
->type
) {
382 // can't add keymat to context with different crypto type
385 if (crypto_keys_idx_args
->args_ulen
!= sizeof(crypto_keys_idx_args
->u
)) {
386 printf("%s: compatibility mode\n", __FUNCTION__
);
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
) {
393 if ((err
= utun_ctl_generate_crypto_keys_idx_ipsec(crypto_keys_idx_args
))) {
398 return EPROTONOSUPPORT
;
406 utun_ctl_stop_crypto_data_traffic (__unused kern_ctl_ref kctlref
,
407 __unused u_int32_t unit
,
408 __unused
void *unitinfo
,
413 struct utun_pcb
*pcb
= unitinfo
;
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.
425 if (len
< UTUN_CRYPTO_ARGS_HDR_SIZE
) {
428 utun_crypto_args_t
*crypto_args
= (__typeof__(crypto_args
))data
;
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
);
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
);
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
));
443 if (crypto_args
->args_ulen
!= sizeof(crypto_args
->u
)) {
444 printf("%s: compatibility mode\n", __FUNCTION__
);
447 if ((pcb
->utun_flags
& UTUN_FLAGS_CRYPTO
) == 0) {
448 printf("%s: crypto is already disabled\n", __FUNCTION__
);
452 if (crypto_args
->type
!= UTUN_CRYPTO_TYPE_IPSEC
) {
454 return EPROTONOSUPPORT
;
457 pcb
->utun_flags
|= UTUN_FLAGS_CRYPTO_STOP_DATA_TRAFFIC
;
462 utun_ctl_start_crypto_data_traffic (__unused kern_ctl_ref kctlref
,
463 __unused u_int32_t unit
,
464 __unused
void *unitinfo
,
469 struct utun_pcb
*pcb
= unitinfo
;
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.
481 if (len
< UTUN_CRYPTO_ARGS_HDR_SIZE
) {
484 utun_crypto_args_t
*crypto_args
= (__typeof__(crypto_args
))data
;
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
);
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
);
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
));
499 if (crypto_args
->args_ulen
!= sizeof(crypto_args
->u
)) {
500 printf("%s: compatibility mode\n", __FUNCTION__
);
503 if ((pcb
->utun_flags
& UTUN_FLAGS_CRYPTO
) == 0) {
504 printf("%s: crypto is already disabled\n", __FUNCTION__
);
508 if (crypto_args
->type
!= UTUN_CRYPTO_TYPE_IPSEC
) {
510 return EPROTONOSUPPORT
;
513 pcb
->utun_flags
&= ~UTUN_FLAGS_CRYPTO_STOP_DATA_TRAFFIC
;
518 utun_pkt_crypto_output (struct utun_pcb
*pcb
, mbuf_t
*m
)
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
);
525 if (pcb
->utun_crypto_ctx
[idx
].type
== UTUN_CRYPTO_TYPE_IPSEC
) {
526 return(utun_pkt_ipsec_output(pcb
, m
));
529 printf("%s: type is invalid %d\n", __FUNCTION__
, pcb
->utun_crypto_ctx
[idx
].type
);