]> git.ipfire.org Git - network.git/blame - src/functions/functions.ipsec
vpn: Move VPN CLI functions into separate files
[network.git] / src / functions / functions.ipsec
CommitLineData
917a1aa0
JS
1#!/bin/bash
2###############################################################################
3# #
4# IPFire.org - A linux based firewall #
5# Copyright (C) 2017 IPFire Network Development Team #
6# #
7# This program is free software: you can redistribute it and/or modify #
8# it under the terms of the GNU General Public License as published by #
9# the Free Software Foundation, either version 3 of the License, or #
10# (at your option) any later version. #
11# #
12# This program is distributed in the hope that it will be useful, #
13# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15# GNU General Public License for more details. #
16# #
17# You should have received a copy of the GNU General Public License #
18# along with this program. If not, see <http://www.gnu.org/licenses/>. #
19# #
20###############################################################################
21
22IPSEC_CONNECTION_CONFIG_SETTINGS="AUTH_MODE INACTIVITY_TIMEOUT LOCAL_ID LOCAL_PREFIX"
23IPSEC_CONNECTION_CONFIG_SETTINGS="${IPSEC_CONNECTION_CONFIG_SETTINGS} MODE PEER PSK"
24IPSEC_CONNECTION_CONFIG_SETTINGS="${IPSEC_CONNECTION_CONFIG_SETTINGS} REMOTE_ID REMOTE_PREFIX"
25IPSEC_CONNECTION_CONFIG_SETTINGS="${IPSEC_CONNECTION_CONFIG_SETTINGS} SECURITY_POLICY"
26
27# Default values
28IPSEC_DEFAULT_MODE="tunnel"
29IPSEC_DEFAULT_AUTH_MODE="psk"
30IPSEC_DEFAULT_INACTIVITY_TIMEOUT="0"
31IPSEC_DEFAULT_SECURITY_POLICY="system"
32
33IPSEC_VALID_MODES="gre-transport tunnel vti"
34IPSEC_VALID_AUTH_MODES="PSK psk"
35
2da98f56
MT
36cli_ipsec() {
37 local action=${1}
38 shift 1
39
40 case "${action}" in
41 connection)
42 cli_ipsec_connection $@
43 ;;
44 *)
45 error "Unrecognized argument: ${action}"
46 exit ${EXIT_ERROR}
47 ;;
48 esac
49}
50
51cli_ipsec_connection() {
52 if ipsec_connection_exists ${1}; then
53 local connection=${1}
54 local key=${2}
55 key=${key//-/_}
56 shift 2
57
58 case "${key}" in
59 authentication|inactivity-timout|local|mode|peer|remote|security-policy)
60 ipsec_connection_${key} ${connection} $@
61 ;;
62 *)
63 error "Unrecognized argument: ${key}"
64 exit ${EXIT_ERROR}
65 ;;
66 esac
67 else
68 local action=${1}
69 shift
70
71 case "${action}" in
72 new)
73 ipsec_connection_new $@
74 ;;
75 destroy)
76 ipsec_connection_destroy $@
77 ;;
78 ""|*)
79 if [ -n "${action}" ]; then
80 error "Unrecognized argument: '${action}'"
81 fi
82 exit ${EXIT_ERROR}
83 ;;
84 esac
85 fi
86}
87
917a1aa0
JS
88# This function writes all values to a via ${connection} specificated VPN IPsec configuration file
89ipsec_connection_write_config() {
90 assert [ $# -ge 1 ]
91
92 local connection="${1}"
93
94 if ! ipsec_connection_exists "${connection}"; then
95 log ERROR "No such VPN IPsec connection: ${connection}"
96 return ${EXIT_ERROR}
97 fi
98
99 local path="$(ipsec_connection_path "${connection}")/settings"
100
101 if ! settings_write "${path}" ${IPSEC_CONNECTION_CONFIG_SETTINGS}; then
102 log ERROR "Could not write configuration settings for VPN IPsec connection ${connection}"
103 return ${EXIT_ERROR}
104 fi
105
106 ipsec_reload ${connection}
107}
108
109# This funtion writes the value for one key to a via ${connection} specificated VPN IPsec connection configuration file
110ipsec_connection_write_config_key() {
111 assert [ $# -ge 3 ]
112
113 local connection=${1}
114 local key=${2}
115 shift 2
116
117 local value="$@"
118
119 if ! ipsec_connection_exists "${connection}"; then
120 log ERROR "No such VPN ipsec connection: ${connection}"
121 return ${EXIT_ERROR}
122 fi
123
124 log DEBUG "Set '${key}' to new value '${value}' in VPN ipsec connection '${connection}'"
125
126 local ${IPSEC_CONNECTION_CONFIG_SETTINGS}
127
128 # Read the config settings
129 if ! ipsec_connection_read_config "${connection}"; then
130 return ${EXIT_ERROR}
131 fi
132
133 # Set the key to a new value
134 assign "${key}" "${value}"
135
136 if ! ipsec_connection_write_config "${connection}"; then
137 return ${EXIT_ERROR}
138 fi
139
140 return ${EXIT_TRUE}
141}
142
143# Reads one or more keys out of a settings file or all if no key is provided.
144ipsec_connection_read_config() {
145 assert [ $# -ge 1 ]
146
147 local connection="${1}"
148 shift 1
149
150 if ! ipsec_connection_exists "${connection}"; then
151 log ERROR "No such VPN IPsec connection : ${connection}"
152 return ${EXIT_ERROR}
153 fi
154
155
156 local args
157 if [ $# -eq 0 ] && [ -n "${IPSEC_CONNECTION_CONFIG_SETTINGS}" ]; then
158 list_append args ${IPSEC_CONNECTION_CONFIG_SETTINGS}
159 else
160 list_append args $@
161 fi
162
163 local path="$(ipsec_connection_path "${connection}")/settings"
164
165 if ! settings_read "${path}" ${args}; then
166 log ERROR "Could not read settings for VPN IPsec connection ${connection}"
167 return ${EXIT_ERROR}
168 fi
169}
170
171# Returns the path to a the directory for given connection
172ipsec_connection_path() {
173 assert [ $# -eq 1 ]
174
175 local connection=${1}
176
177 echo "${NETWORK_CONFIG_DIR}/vpn/ipsec/connection/${connection}"
178}
179
180# This function checks if a vpn ipsec connection exists
181# Returns True when yes and false when not
182ipsec_connection_exists() {
183 assert [ $# -eq 1 ]
184
185 local connection=${1}
186
187 local path=$(ipsec_connection_path "${connection}")
188
189 [ -d "${path}" ] && return ${EXIT_TRUE} || return ${EXIT_FALSE}
190}
191
192# Reloads the connection after config changes
193ipsec_reload() {
194 return ${EXIT_TRUE}
195}
196
197# Handle the cli after authentification
198ipsec_connection_authentication() {
199 if [ ! $# -gt 1 ]; then
200 log ERROR "Not enough arguments"
201 return ${EXIT_ERROR}
202 fi
203
204 local connection=${1}
205 local cmd=${2}
206 shift 2
207
208 case ${cmd} in
209 mode)
210 ipsec_connection_authentication_mode "${connection}" $@
211 ;;
212 pre-shared-key)
213 ipsec_connection_authentication_psk "${connection}" $@
214 ;;
215 *)
216 log ERROR "Unrecognized argument: ${cmd}"
217 return ${EXIT_ERROR}
218 ;;
219 esac
220}
221
222# Set the authentification mode
223ipsec_connection_authentication_mode() {
224 if [ ! $# -eq 2 ]; then
225 log ERROR "Not enough arguments"
226 return ${EXIT_ERROR}
227 fi
228 local connection=${1}
229 local mode=${2}
230
231 if ! isoneof mode ${IPSEC_VALID_AUTH_MODES}; then
232 log ERROR "Auth mode '${mode}' is invalid"
233 return ${EXIT_ERROR}
234 fi
235
236 if ! ipsec_connection_write_config_key "${connection}" "AUTH_MODE" ${mode,,}; then
237 log ERROR "Could not write configuration settings"
238 return ${EXIT_ERROR}
239 fi
240}
241
242# Set the psk
243ipsec_connection_authentication_psk() {
244 if [ ! $# -eq 2]; then
245 log ERROR "Not enough arguments"
246 return ${EXIT_ERROR}
247 fi
248 local connection=${1}
249 local psk=${2}
250
251 # TODO Check if psk is valid
252
253 if ! ipsec_connection_write_config_key "${connection}" "PSK" ${psk}; then
254 log ERROR "Could not write configuration settings"
255 return ${EXIT_ERROR}
256 fi
257
258 return ${EXIT_OK}
259}
260
261# Handle the cli after local
262ipsec_connection_local() {
263 if [ ! $# -ge 2 ]; then
264 log ERROR "Not enough arguments"
265 return ${EXIT_ERROR}
266 fi
267
268 local connection=${1}
269 local cmd=${2}
270 shift 2
271
272 case ${cmd} in
273 id)
274 ipsec_connection_id "${connection}" "LOCAL" $@
275 ;;
276 prefix)
277 ipsec_connection_prefix "${connection}" "LOCAL" $@
278 ;;
279 *)
280 log ERROR "Unrecognized argument: ${cmd}"
281 return ${EXIT_ERROR}
282 ;;
283 esac
284
285 return ${EXIT_OK}
286}
287
288# Set the connection mode
289ipsec_connection_mode() {
290 if [ ! $# -eq 2]; then
291 log ERROR "Not enough arguments"
292 return ${EXIT_ERROR}
293 fi
294 local connection=${1}
295 local mode=${2}
296
297 if ! isoneof mode ${IPSEC_VALID_MODES}; then
298 log ERROR "Mode '${mode}' is invalid"
299 return ${EXIT_ERROR}
300 fi
301
302 if ! ipsec_connection_write_config_key "${connection}" "MODE" ${mode}; then
303 log ERROR "Could not write configuration settings"
304 return ${EXIT_ERROR}
305 fi
306
307 return ${EXIT_OK}
308}
309
310# Set the peer to connect to
311ipsec_connection_peer() {
312 if [ ! $# -eq 2]; then
313 log ERROR "Not enough arguments"
314 return ${EXIT_ERROR}
315 fi
316 local connection=${1}
317 local peer=${2}
318
319 if ! ipsec_connection_check_peer ${peer}; then
320 log ERROR "Peer '${peer}' is invalid"
321 return ${EXIT_ERROR}
322 fi
323
324 if ! ipsec_connection_write_config_key "${connection}" "PEER" ${peer}; then
325 log ERROR "Could not write configuration settings"
326 return ${EXIT_ERROR}
327 fi
328
329 return ${EXIT_OK}
330}
331
332#Set the local or remote id
333ipsec_connection_id() {
334 if [ ! $# -eq 3 ]; then
335 log ERROR "Not enough arguments"
336 return ${EXIT_ERROR}
337 fi
338 local connection=${1}
339 local type=${2}
340 local id=${3}
341
342 if ! ipsec_connection_check_id ${id}; then
343 log ERROR "Id '${id}' is invalid"
344 return ${EXIT_ERROR}
345 fi
346
347 if ! ipsec_connection_write_config_key "${connection}" "${type}_ID" ${id}; then
348 log ERROR "Could not write configuration settings"
349 return ${EXIT_ERROR}
350 fi
351
352 return ${EXIT_OK}
353}
354
355# Set the local or remote prefix
356ipsec_connection_prefix() {
357 if [ ! $# -ge 3 ]; then
358 log ERROR "Not enough arguments"
359 return ${EXIT_ERROR}
360 fi
361 local connection=${1}
362 local type=${2}
363 shift 2
364
365 local _prefix="${type}_PREFIX"
366 local "${_prefix}"
367 if ! ipsec_connection_read_config "${connection}" "${_prefix}"; then
368 return ${EXIT_ERROR}
369 fi
370
371 # Remove duplicated entries to proceed the list safely
372 assign "${_prefix}" "$(list_unique ${!_prefix} )"
373
374 local prefixes_added
375 local prefixes_removed
376 local prefixes_set
377
378 while [ $# -gt 0 ]; do
379 local arg="${1}"
380
381 case "${arg}" in
382 +*)
383 list_append prefixes_added "${arg:1}"
384 ;;
385 -*)
386 list_append prefixes_removed "${arg:1}"
387 ;;
388 [A-Fa-f0-9]*)
389 list_append prefixes_set "${arg}"
390 ;;
391 *)
392 error "Invalid argument: ${arg}"
393 return ${EXIT_ERROR}
394 ;;
395 esac
396 shift
397 done
398
399 # Check if the user is trying a mixed operation
400 if ! list_is_empty prefixes_set && (! list_is_empty prefixes_added || ! list_is_empty prefixes_removed); then
401 error "You cannot reset the prefix list and add or remove prefixes at the same time"
402 return ${EXIT_ERROR}
403 fi
404
405 # Set new prefix list
406 if ! list_is_empty prefixes_set; then
407 # Check if all prefixes are valid
408 local prefix
409 for prefix in ${prefixes_set}; do
410 if ! ip_net_is_valid ${prefix}; then
411 error "Unsupported prefix: ${prefix}"
412 return ${EXIT_ERROR}
413 fi
414 done
415
416 assign "${_prefix}" "${prefixes_set}"
417
418 # Perform incremental updates
419 else
420 local prefix
421
422 # Perform all removals
423 for prefix in ${prefixes_removed}; do
424 if ! list_remove "${_prefix}" ${prefix}; then
425 warning "${prefix} was not on the list and could not be removed"
426 fi
427 done
428
429
430 for prefix in ${prefixes_added}; do
431 if ip_net_is_valid ${prefix}; then
432 if ! list_append_unique "${_prefix}" ${prefix}; then
433 warning "${prefix} is already on the prefix list"
434 fi
435 else
436 warning "${prefix} is not a valiv IP net and could not be added"
437 fi
438 done
439 fi
440
441 # Check if the list contain at least one valid prefix
442 if list_is_empty ${_prefix}; then
443 error "Cannot save an empty prefix list"
444 return ${EXIT_ERROR}
445 fi
446
447 # Save everything
448 if ! ipsec_connection_write_config_key "${connection}" "${_prefix}" ${!_prefix}; then
449 log ERROR "Could not write configuration settings"
450 fi
451
452 return ${EXIT_OK}
453}
454
455# Handle the cli after remote
456ipsec_connection_remote() {
457 if [ ! $# -ge 2 ]; then
458 log ERROR "Not enough arguments"
459 return ${EXIT_ERROR}
460 fi
461
462 local connection=${1}
463 local cmd=${2}
464 shift 2
465
466 case ${cmd} in
467 id)
468 ipsec_connection_id "${connection}" "REMOTE" $@
469 ;;
470
471 prefix)
472 ipsec_connection_prefix "${connection}" "REMOTE" $@
473 ;;
474 *)
475 log ERROR "Unrecognized argument: ${cmd}"
476 return ${EXIT_ERROR}
477 ;;
478 esac
479
480 return ${EXIT_OK}
481}
482
483# Set the inactivity timeout
484ipsec_connection_inactivity_timeout() {
485 if [ ! $# -ge 2 ]; then
486 log ERROR "Not enough arguments"
487 return ${EXIT_ERROR}
488 fi
489
490 local connection=${1}
491 shift 1
492 local value=$@
493
494 if ! isinteger value; then
495 value=$(parse_time $@)
496 if [ ! $? -eq 0 ]; then
497 log ERROR "Parsing the passed time was not sucessful please check the passed values."
498 return ${EXIT_ERROR}
499 fi
500 fi
501
502 if [ ${value} -le 0 ]; then
503 log ERROR "The passed time value must be in the sum greater zero seconds."
504 return ${EXIT_ERROR}
505 fi
506
507 if ! ipsec_connection_write_config_key "${connection}" "INACTIVITY_TIMEOUT" ${value}; then
508 log ERROR "Could not write configuration settings"
509 return ${EXIT_ERROR}
510 fi
511
512 return ${EXIT_OK}
513}
514
515
516# Set the security policy to use
517ipsec_connection_security_policy() {
518 if [ ! $# -eq 2 ]; then
519 log ERROR "Not enough arguments"
520 return ${EXIT_ERROR}
521 fi
522 local connection=${1}
523 local security_policy=${2}
524
525 if ! vpn_security_policy_exists ${security_policy}; then
526 log ERROR "No such vpn security policy '${security_policy}'"
527 return ${EXIT_ERROR}
528 fi
529
530 if ! ipsec_connection_write_config_key "${connection}" "SECURITY_POLICY" ${security_policy}; then
531 log ERROR "Could not write configuration settings"
532 return ${EXIT_ERROR}
533 fi
534}
535
536# Check if a id is valid
537ipsec_connection_check_id() {
538 assert [ $# -eq 1 ]
539 local id=${1}
540
541 if [[ ${id} =~ ^@[[:alnum:]]+$ ]] || ip_is_valid ${id}; then
542 return ${EXIT_TRUE}
543 else
544 return ${EXIT_FALSE}
545 fi
546}
547
548# Checks if a peer is valid
549ipsec_connection_check_peer() {
550 assert [ $# -eq 1 ]
551 local peer=${1}
552
553 # TODO Accept also FQDNs
554 if ip_is_valid ${peer}; then
555 return ${EXIT_TRUE}
556 else
557 return ${EXIT_FALSE}
558 fi
559}
560
561# This function checks if a VPN IPsec connection name is valid
562# Allowed are only A-Za-z0-9
563ipsec_connection_check_name() {
564 assert [ $# -eq 1 ]
565
566 local connection=${1}
567
568 [[ "${connection}" =~ [^[:alnum:]$] ]]
569}
570
571# Function that creates one VPN IPsec connection
572ipsec_connection_new() {
573 if [ $# -gt 1 ]; then
574 error "Too many arguments"
575 return ${EXIT_ERROR}
576 fi
577
578 local connection="${1}"
579 if ! isset connection; then
580 error "Please provide a connection name"
581 return ${EXIT_ERROR}
582 fi
583
584 # Check for duplicates
585 if ipsec_connection_exists "${connection}"; then
586 error "The VPN IPsec connection ${connection} already exists"
587 return ${EXIT_ERROR}
588 fi
589
590 # Check if the name of the connection is valid
591 if ipsec_connection_check_name "${connection}"; then
592 error "'${connection}' contains illegal characters"
593 return ${EXIT_ERROR}
594 fi
595
596 log DEBUG "Creating VPN IPsec connection ${connection}"
597
598 if ! mkdir -p $(ipsec_connection_path "${connection}"); then
599 log ERROR "Could not create config directory for ${connection}"
600 return ${EXIT_ERROR}
601 fi
602
603 local ${IPSEC_CONNECTION_CONFIG_SETTINGS}
604
605 MODE=${IPSEC_DEFAULT_MODE}
606 AUTH_MODE=${IPSEC_DEFAULT_AUTH_MODE}
607 INACTIVITY_TIMEOUT=${IPSEC_DEFAULT_INACTIVITY_TIMEOUT}
608 SECURITY_POLICY=${IPSEC_DEFAULT_SECURITY_POLICY}
609
610 if ! ipsec_connection_write_config "${connection}"; then
611 log ERROR "Could not write new config file"
612 return ${EXIT_ERROR}
613 fi
614}
615
616# Function that deletes based on the passed parameters one ore more vpn security policies
617ipsec_connection_destroy() {
618 local connection
619 for connection in $@; do
620 if ! ipsec_connection_exists "${connection}"; then
621 log ERROR "The VPN IPsec connection ${connection} does not exist."
622 continue
623 fi
624
625 log DEBUG "Deleting VPN IPsec connection ${connection}"
626 if ! rm -rf $(ipsec_connection_path "${connection}"); then
627 log ERROR "Deleting the VPN IPsec connection ${connection} was not sucessful"
628 return ${EXIT_ERROR}
629 fi
630 done
631}