]> git.ipfire.org Git - people/ms/network.git/blame - src/functions/functions.ipsec
ipsec: Fix another bash syntax error
[people/ms/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
cf8685a1 99 local path="${NETWORK_IPSEC_CONNS_DIR}/${connection}/settings"
917a1aa0
JS
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
cf8685a1 163 local path="${NETWORK_IPSEC_CONNS_DIR}/${connection}/settings"
917a1aa0
JS
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
917a1aa0
JS
171# This function checks if a vpn ipsec connection exists
172# Returns True when yes and false when not
173ipsec_connection_exists() {
174 assert [ $# -eq 1 ]
175
176 local connection=${1}
177
cf8685a1 178 local path="${NETWORK_IPSEC_CONNS_DIR}/${connection}"
917a1aa0
JS
179
180 [ -d "${path}" ] && return ${EXIT_TRUE} || return ${EXIT_FALSE}
181}
182
183# Reloads the connection after config changes
184ipsec_reload() {
185 return ${EXIT_TRUE}
186}
187
188# Handle the cli after authentification
189ipsec_connection_authentication() {
190 if [ ! $# -gt 1 ]; then
191 log ERROR "Not enough arguments"
192 return ${EXIT_ERROR}
193 fi
194
195 local connection=${1}
196 local cmd=${2}
197 shift 2
198
199 case ${cmd} in
200 mode)
201 ipsec_connection_authentication_mode "${connection}" $@
202 ;;
203 pre-shared-key)
204 ipsec_connection_authentication_psk "${connection}" $@
205 ;;
206 *)
207 log ERROR "Unrecognized argument: ${cmd}"
208 return ${EXIT_ERROR}
209 ;;
210 esac
211}
212
213# Set the authentification mode
214ipsec_connection_authentication_mode() {
215 if [ ! $# -eq 2 ]; then
216 log ERROR "Not enough arguments"
217 return ${EXIT_ERROR}
218 fi
219 local connection=${1}
220 local mode=${2}
221
222 if ! isoneof mode ${IPSEC_VALID_AUTH_MODES}; then
223 log ERROR "Auth mode '${mode}' is invalid"
224 return ${EXIT_ERROR}
225 fi
226
227 if ! ipsec_connection_write_config_key "${connection}" "AUTH_MODE" ${mode,,}; then
228 log ERROR "Could not write configuration settings"
229 return ${EXIT_ERROR}
230 fi
231}
232
233# Set the psk
234ipsec_connection_authentication_psk() {
235 if [ ! $# -eq 2]; then
236 log ERROR "Not enough arguments"
237 return ${EXIT_ERROR}
238 fi
239 local connection=${1}
240 local psk=${2}
241
242 # TODO Check if psk is valid
243
244 if ! ipsec_connection_write_config_key "${connection}" "PSK" ${psk}; then
245 log ERROR "Could not write configuration settings"
246 return ${EXIT_ERROR}
247 fi
248
249 return ${EXIT_OK}
250}
251
252# Handle the cli after local
253ipsec_connection_local() {
254 if [ ! $# -ge 2 ]; then
255 log ERROR "Not enough arguments"
256 return ${EXIT_ERROR}
257 fi
258
259 local connection=${1}
260 local cmd=${2}
261 shift 2
262
263 case ${cmd} in
264 id)
265 ipsec_connection_id "${connection}" "LOCAL" $@
266 ;;
267 prefix)
268 ipsec_connection_prefix "${connection}" "LOCAL" $@
269 ;;
270 *)
271 log ERROR "Unrecognized argument: ${cmd}"
272 return ${EXIT_ERROR}
273 ;;
274 esac
275
276 return ${EXIT_OK}
277}
278
279# Set the connection mode
280ipsec_connection_mode() {
5bdbc2ee 281 if [ ! $# -eq 2 ]; then
917a1aa0
JS
282 log ERROR "Not enough arguments"
283 return ${EXIT_ERROR}
284 fi
285 local connection=${1}
286 local mode=${2}
287
288 if ! isoneof mode ${IPSEC_VALID_MODES}; then
289 log ERROR "Mode '${mode}' is invalid"
290 return ${EXIT_ERROR}
291 fi
292
293 if ! ipsec_connection_write_config_key "${connection}" "MODE" ${mode}; then
294 log ERROR "Could not write configuration settings"
295 return ${EXIT_ERROR}
296 fi
297
298 return ${EXIT_OK}
299}
300
301# Set the peer to connect to
302ipsec_connection_peer() {
0b962a64 303 if [ ! $# -eq 2 ]; then
917a1aa0
JS
304 log ERROR "Not enough arguments"
305 return ${EXIT_ERROR}
306 fi
307 local connection=${1}
308 local peer=${2}
309
310 if ! ipsec_connection_check_peer ${peer}; then
311 log ERROR "Peer '${peer}' is invalid"
312 return ${EXIT_ERROR}
313 fi
314
315 if ! ipsec_connection_write_config_key "${connection}" "PEER" ${peer}; then
316 log ERROR "Could not write configuration settings"
317 return ${EXIT_ERROR}
318 fi
319
320 return ${EXIT_OK}
321}
322
323#Set the local or remote id
324ipsec_connection_id() {
325 if [ ! $# -eq 3 ]; then
326 log ERROR "Not enough arguments"
327 return ${EXIT_ERROR}
328 fi
329 local connection=${1}
330 local type=${2}
331 local id=${3}
332
333 if ! ipsec_connection_check_id ${id}; then
334 log ERROR "Id '${id}' is invalid"
335 return ${EXIT_ERROR}
336 fi
337
338 if ! ipsec_connection_write_config_key "${connection}" "${type}_ID" ${id}; then
339 log ERROR "Could not write configuration settings"
340 return ${EXIT_ERROR}
341 fi
342
343 return ${EXIT_OK}
344}
345
346# Set the local or remote prefix
347ipsec_connection_prefix() {
348 if [ ! $# -ge 3 ]; then
349 log ERROR "Not enough arguments"
350 return ${EXIT_ERROR}
351 fi
352 local connection=${1}
353 local type=${2}
354 shift 2
355
356 local _prefix="${type}_PREFIX"
357 local "${_prefix}"
358 if ! ipsec_connection_read_config "${connection}" "${_prefix}"; then
359 return ${EXIT_ERROR}
360 fi
361
362 # Remove duplicated entries to proceed the list safely
363 assign "${_prefix}" "$(list_unique ${!_prefix} )"
364
365 local prefixes_added
366 local prefixes_removed
367 local prefixes_set
368
369 while [ $# -gt 0 ]; do
370 local arg="${1}"
371
372 case "${arg}" in
373 +*)
374 list_append prefixes_added "${arg:1}"
375 ;;
376 -*)
377 list_append prefixes_removed "${arg:1}"
378 ;;
379 [A-Fa-f0-9]*)
380 list_append prefixes_set "${arg}"
381 ;;
382 *)
383 error "Invalid argument: ${arg}"
384 return ${EXIT_ERROR}
385 ;;
386 esac
387 shift
388 done
389
390 # Check if the user is trying a mixed operation
391 if ! list_is_empty prefixes_set && (! list_is_empty prefixes_added || ! list_is_empty prefixes_removed); then
392 error "You cannot reset the prefix list and add or remove prefixes at the same time"
393 return ${EXIT_ERROR}
394 fi
395
396 # Set new prefix list
397 if ! list_is_empty prefixes_set; then
398 # Check if all prefixes are valid
399 local prefix
400 for prefix in ${prefixes_set}; do
401 if ! ip_net_is_valid ${prefix}; then
402 error "Unsupported prefix: ${prefix}"
403 return ${EXIT_ERROR}
404 fi
405 done
406
407 assign "${_prefix}" "${prefixes_set}"
408
409 # Perform incremental updates
410 else
411 local prefix
412
413 # Perform all removals
414 for prefix in ${prefixes_removed}; do
415 if ! list_remove "${_prefix}" ${prefix}; then
416 warning "${prefix} was not on the list and could not be removed"
417 fi
418 done
419
420
421 for prefix in ${prefixes_added}; do
422 if ip_net_is_valid ${prefix}; then
423 if ! list_append_unique "${_prefix}" ${prefix}; then
424 warning "${prefix} is already on the prefix list"
425 fi
426 else
427 warning "${prefix} is not a valiv IP net and could not be added"
428 fi
429 done
430 fi
431
432 # Check if the list contain at least one valid prefix
433 if list_is_empty ${_prefix}; then
434 error "Cannot save an empty prefix list"
435 return ${EXIT_ERROR}
436 fi
437
438 # Save everything
439 if ! ipsec_connection_write_config_key "${connection}" "${_prefix}" ${!_prefix}; then
440 log ERROR "Could not write configuration settings"
441 fi
442
443 return ${EXIT_OK}
444}
445
446# Handle the cli after remote
447ipsec_connection_remote() {
448 if [ ! $# -ge 2 ]; then
449 log ERROR "Not enough arguments"
450 return ${EXIT_ERROR}
451 fi
452
453 local connection=${1}
454 local cmd=${2}
455 shift 2
456
457 case ${cmd} in
458 id)
459 ipsec_connection_id "${connection}" "REMOTE" $@
460 ;;
461
462 prefix)
463 ipsec_connection_prefix "${connection}" "REMOTE" $@
464 ;;
465 *)
466 log ERROR "Unrecognized argument: ${cmd}"
467 return ${EXIT_ERROR}
468 ;;
469 esac
470
471 return ${EXIT_OK}
472}
473
474# Set the inactivity timeout
475ipsec_connection_inactivity_timeout() {
476 if [ ! $# -ge 2 ]; then
477 log ERROR "Not enough arguments"
478 return ${EXIT_ERROR}
479 fi
480
481 local connection=${1}
482 shift 1
483 local value=$@
484
485 if ! isinteger value; then
486 value=$(parse_time $@)
487 if [ ! $? -eq 0 ]; then
488 log ERROR "Parsing the passed time was not sucessful please check the passed values."
489 return ${EXIT_ERROR}
490 fi
491 fi
492
493 if [ ${value} -le 0 ]; then
494 log ERROR "The passed time value must be in the sum greater zero seconds."
495 return ${EXIT_ERROR}
496 fi
497
498 if ! ipsec_connection_write_config_key "${connection}" "INACTIVITY_TIMEOUT" ${value}; then
499 log ERROR "Could not write configuration settings"
500 return ${EXIT_ERROR}
501 fi
502
503 return ${EXIT_OK}
504}
505
506
507# Set the security policy to use
508ipsec_connection_security_policy() {
509 if [ ! $# -eq 2 ]; then
510 log ERROR "Not enough arguments"
511 return ${EXIT_ERROR}
512 fi
513 local connection=${1}
514 local security_policy=${2}
515
516 if ! vpn_security_policy_exists ${security_policy}; then
517 log ERROR "No such vpn security policy '${security_policy}'"
518 return ${EXIT_ERROR}
519 fi
520
521 if ! ipsec_connection_write_config_key "${connection}" "SECURITY_POLICY" ${security_policy}; then
522 log ERROR "Could not write configuration settings"
523 return ${EXIT_ERROR}
524 fi
525}
526
527# Check if a id is valid
528ipsec_connection_check_id() {
529 assert [ $# -eq 1 ]
530 local id=${1}
531
532 if [[ ${id} =~ ^@[[:alnum:]]+$ ]] || ip_is_valid ${id}; then
533 return ${EXIT_TRUE}
534 else
535 return ${EXIT_FALSE}
536 fi
537}
538
539# Checks if a peer is valid
540ipsec_connection_check_peer() {
541 assert [ $# -eq 1 ]
542 local peer=${1}
543
544 # TODO Accept also FQDNs
545 if ip_is_valid ${peer}; then
546 return ${EXIT_TRUE}
547 else
548 return ${EXIT_FALSE}
549 fi
550}
551
552# This function checks if a VPN IPsec connection name is valid
553# Allowed are only A-Za-z0-9
554ipsec_connection_check_name() {
555 assert [ $# -eq 1 ]
556
557 local connection=${1}
558
559 [[ "${connection}" =~ [^[:alnum:]$] ]]
560}
561
562# Function that creates one VPN IPsec connection
563ipsec_connection_new() {
564 if [ $# -gt 1 ]; then
565 error "Too many arguments"
566 return ${EXIT_ERROR}
567 fi
568
569 local connection="${1}"
570 if ! isset connection; then
571 error "Please provide a connection name"
572 return ${EXIT_ERROR}
573 fi
574
575 # Check for duplicates
576 if ipsec_connection_exists "${connection}"; then
577 error "The VPN IPsec connection ${connection} already exists"
578 return ${EXIT_ERROR}
579 fi
580
581 # Check if the name of the connection is valid
582 if ipsec_connection_check_name "${connection}"; then
583 error "'${connection}' contains illegal characters"
584 return ${EXIT_ERROR}
585 fi
586
587 log DEBUG "Creating VPN IPsec connection ${connection}"
588
cf8685a1 589 if ! mkdir -p "${NETWORK_IPSEC_CONNS_DIR}/${connection}"; then
917a1aa0
JS
590 log ERROR "Could not create config directory for ${connection}"
591 return ${EXIT_ERROR}
592 fi
593
594 local ${IPSEC_CONNECTION_CONFIG_SETTINGS}
595
596 MODE=${IPSEC_DEFAULT_MODE}
597 AUTH_MODE=${IPSEC_DEFAULT_AUTH_MODE}
598 INACTIVITY_TIMEOUT=${IPSEC_DEFAULT_INACTIVITY_TIMEOUT}
599 SECURITY_POLICY=${IPSEC_DEFAULT_SECURITY_POLICY}
600
601 if ! ipsec_connection_write_config "${connection}"; then
602 log ERROR "Could not write new config file"
603 return ${EXIT_ERROR}
604 fi
605}
606
607# Function that deletes based on the passed parameters one ore more vpn security policies
608ipsec_connection_destroy() {
609 local connection
610 for connection in $@; do
611 if ! ipsec_connection_exists "${connection}"; then
612 log ERROR "The VPN IPsec connection ${connection} does not exist."
613 continue
614 fi
615
616 log DEBUG "Deleting VPN IPsec connection ${connection}"
cf8685a1 617 if ! rm -rf "${NETWORK_IPSEC_CONNS_DIR}/${connection}"; then
917a1aa0
JS
618 log ERROR "Deleting the VPN IPsec connection ${connection} was not sucessful"
619 return ${EXIT_ERROR}
620 fi
621 done
622}