]> git.ipfire.org Git - people/ms/network.git/blob - src/functions/functions.vpn-security-policies
security-policies: Show description for each cipher instead of handle
[people/ms/network.git] / src / functions / functions.vpn-security-policies
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
22 VPN_SECURITY_POLICIES_CONFIG_SETTINGS="CIPHER COMPRESSION GROUP_TYPE INTEGRITY KEY_EXCHANGE LIFETIME PFS"
23 VPN_SECURITY_POLICIES_READONLY="system"
24
25 VPN_DEFAULT_SECURITY_POLICY="system"
26
27 declare -A VPN_SUPPORTED_CIPHERS=(
28 # 3DES-CBC
29 [3DES-CBC]="168 bit 3DES-EDE-CBC"
30
31 # AES-CBC
32 [AES256-CBC]="256 bit AES-CBC"
33 [AES192-CBC]="192 bit AES-CBC"
34 [AES128-CBC]="128 bit AES-CBC"
35
36 # AES-CTR
37 [AES256-CTR]="256 bit AES-COUNTER"
38 [AES192-CTR]="192 bit AES-COUNTER"
39 [AES128-CTR]="128 bit AES-COUNTER"
40
41 # AES-GCM
42 [AES256-GCM128]="256 bit AES-GCM with 128 bit ICV"
43 [AES192-GCM128]="192 bit AES-GCM with 128 bit ICV"
44 [AES128-GCM128]="128 bit AES-GCM with 128 bit ICV"
45 [AES256-GCM96]="256 bit AES-GCM with 96 bit ICV"
46 [AES192-GCM96]="192 bit AES-GCM with 96 bit ICV"
47 [AES128-GCM96]="128 bit AES-GCM with 96 bit ICV"
48 [AES256-GCM64]="256 bit AES-GCM with 64 bit ICV"
49 [AES192-GCM64]="192 bit AES-GCM with 64 bit ICV"
50 [AES128-GCM64]="128 bit AES-GCM with 64 bit ICV"
51
52 # AES-CCM
53 [AES256-CCM128]="256 bit AES-CCM with 128 bit ICV"
54 [AES192-CCM128]="192 bit AES-CCM with 128 bit ICV"
55 [AES128-CCM128]="128 bit AES-CCM with 128 bit ICV"
56 [AES256-CCM96]="256 bit AES-CCM with 96 bit ICV"
57 [AES192-CCM96]="192 bit AES-CCM with 96 bit ICV"
58 [AES128-CCM96]="128 bit AES-CCM with 96 bit ICV"
59 [AES256-CCM64]="256 bit AES-CCM with 64 bit ICV"
60 [AES192-CCM64]="192 bit AES-CCM with 64 bit ICV"
61 [AES128-CCM64]="128 bit AES-CCM with 64 bit ICV"
62
63 # CAMELLIA-CBC
64 [CAMELLIA256-CBC]="256 bit CAMELLIA-CBC"
65 [CAMELLIA192-CBC]="192 bit CAMELLIA-CBC"
66 [CAMELLIA128-CBC]="128 bit CAMELLIA-CBC"
67
68 # CAMELLIA-CTR
69 [CAMELLIA256-CTR]="256 bit CAMELLIA-COUNTER"
70 [CAMELLIA192-CTR]="192 bit CAMELLIA-COUNTER"
71 [CAMELLIA128-CTR]="128 bit CAMELLIA-COUNTER"
72
73 # CAMELLIA-GCM
74 [CAMELLIA256-GCM128]="256 bit CAMELLIA-GCM with 128 bit ICV"
75 [CAMELLIA192-GCM128]="192 bit CAMELLIA-GCM with 128 bit ICV"
76 [CAMELLIA128-GCM128]="128 bit CAMELLIA-GCM with 128 bit ICV"
77 [CAMELLIA256-GCM96]="256 bit CAMELLIA-GCM with 96 bit ICV"
78 [CAMELLIA192-GCM96]="192 bit CAMELLIA-GCM with 96 bit ICV"
79 [CAMELLIA128-GCM96]="128 bit CAMELLIA-GCM with 96 bit ICV"
80 [CAMELLIA256-GCM64]="256 bit CAMELLIA-GCM with 64 bit ICV"
81 [CAMELLIA192-GCM64]="192 bit CAMELLIA-GCM with 64 bit ICV"
82 [CAMELLIA128-GCM64]="128 bit CAMELLIA-GCM with 64 bit ICV"
83
84 # CAMELLIA-CCM
85 [CAMELLIA256-CCM128]="256 bit CAMELLIA-CCM with 128 bit ICV"
86 [CAMELLIA192-CCM128]="192 bit CAMELLIA-CCM with 128 bit ICV"
87 [CAMELLIA128-CCM128]="128 bit CAMELLIA-CCM with 128 bit ICV"
88 [CAMELLIA256-CCM96]="256 bit CAMELLIA-CCM with 96 bit ICV"
89 [CAMELLIA192-CCM96]="192 bit CAMELLIA-CCM with 96 bit ICV"
90 [CAMELLIA128-CCM96]="128 bit CAMELLIA-CCM with 96 bit ICV"
91 [CAMELLIA256-CCM64]="256 bit CAMELLIA-CCM with 64 bit ICV"
92 [CAMELLIA192-CCM64]="192 bit CAMELLIA-CCM with 64 bit ICV"
93 [CAMELLIA128-CCM64]="128 bit CAMELLIA-CCM with 64 bit ICV"
94 )
95
96 VPN_SUPPORTED_INTEGRITY="SHA512 SHA256 SHA128"
97 VPN_SUPPORTED_GROUP_TYPES="MODP8192 MODP4096"
98
99 # This functions checks if a policy is readonly
100 # returns true when yes and false when no
101 vpn_security_policies_check_readonly() {
102 if isoneof name ${VPN_SECURITY_POLICIES_READONLY}; then
103 return ${EXIT_TRUE}
104 else
105 return ${EXIT_FALSE}
106 fi
107 }
108
109 # This function writes all values to a via ${name} specificated vpn security policy configuration file
110 vpn_security_policies_write_config() {
111 assert [ $# -ge 1 ]
112
113 local name="${1}"
114
115 if ! vpn_security_policy_exists "${name}"; then
116 log ERROR "No such vpn security policy: ${name}"
117 return ${EXIT_ERROR}
118 fi
119
120 if vpn_security_policies_check_readonly "${name}"; then
121 log ERROR "The ${name} vpn security policy cannot be changed."
122 return ${EXIT_ERROR}
123 fi
124
125 local path="$(vpn_security_policies_path "${name}")"
126 if [ ! -w ${path} ]; then
127 log ERROR "${path} is not writeable"
128 return ${EXIT_ERROR}
129 fi
130
131 if ! settings_write "${path}" ${VPN_SECURITY_POLICIES_CONFIG_SETTINGS}; then
132 log ERROR "Could not write configuration settings for vpn security policy ${name}"
133 return ${EXIT_ERROR}
134 fi
135
136 # TODO everytime we successfully write a config we should call some trigger to take the changes into effect
137 }
138
139 # This funtion writes the value for one key to a via ${name} specificated vpn security policy configuration file
140 vpn_security_policies_write_config_key() {
141 assert [ $# -ge 3 ]
142
143 local name=${1}
144 local key=${2}
145 shift 2
146
147 local value="$@"
148
149 if ! vpn_security_policy_exists "${name}"; then
150 log ERROR "No such vpn security policy: ${name}"
151 return ${EXIT_ERROR}
152 fi
153
154 log DEBUG "Set '${key}' to new value '${value}' in vpn security policy ${name}"
155
156 local ${VPN_SECURITY_POLICIES_CONFIG_SETTINGS}
157
158 # Read the config settings
159 if ! vpn_security_policies_read_config "${name}"; then
160 return ${EXIT_ERROR}
161 fi
162
163 # Set the key to a new value
164 assign "${key}" "${value}"
165
166 if ! vpn_security_policies_write_config "${name}"; then
167 return ${EXIT_ERROR}
168 fi
169
170 return ${EXIT_TRUE}
171 }
172
173 # Reads one or more keys out of a settings file or all if no key is provided.
174 vpn_security_policies_read_config() {
175 assert [ $# -ge 1 ]
176
177 local name="${1}"
178 shift 1
179
180 if ! vpn_security_policy_exists "${name}"; then
181 log ERROR "No such vpn security policy: ${name}"
182 return ${EXIT_ERROR}
183 fi
184
185
186 local args
187 if [ $# -eq 0 ] && [ -n "${VPN_SECURITY_POLICIES_CONFIG_SETTINGS}" ]; then
188 list_append args ${VPN_SECURITY_POLICIES_CONFIG_SETTINGS}
189 else
190 list_append args $@
191 fi
192
193 local path="$(vpn_security_policies_path ${name})"
194
195 if ! settings_read "${path}" ${args}; then
196 log ERROR "Could not read settings for vpn security policy ${name}"
197 return ${EXIT_ERROR}
198 fi
199 }
200
201 # Returns the path to a the configuration fora given name
202 vpn_security_policies_path() {
203 assert [ $# -eq 1 ]
204
205 local name=${1}
206
207 if vpn_security_policies_check_readonly "${name}"; then
208 echo "${NETWORK_SHARE_DIR}/vpn/security-policies/${name}"
209 else
210 echo "${NETWORK_CONFIG_DIR}/vpn/security-policies/${name}"
211 fi
212 }
213
214 # Print the content of a vpn security policy configuration file in a nice way
215 vpn_security_policies_show() {
216 assert [ $# -eq 1 ]
217
218 local name=${1}
219
220 local ${VPN_SECURITY_POLICIES_CONFIG_SETTINGS}
221 if ! vpn_security_policies_read_config ${name}; then
222 return ${EXIT_ERROR}
223 fi
224
225 cli_print_fmt1 0 "Security Policy: ${name}"
226 cli_space
227
228 # This could be done in a loop but a loop is much more complicated
229 # because we print 'Group Types' but the variable is named 'GROUP_TYPES'
230 cli_print_fmt1 1 "Ciphers:"
231
232 local cipher
233 for cipher in ${CIPHER}; do
234 cli_print_fmt1 2 "${VPN_SUPPORTED_CIPHERS[${cipher}]-${cipher}}"
235 done
236 cli_space
237
238 cli_print_fmt1 1 "Integrity:"
239 cli_print_fmt1 2 "${INTEGRITY}"
240 cli_space
241
242 cli_print_fmt1 1 "Group Types:"
243 cli_print_fmt1 2 "${GROUP_TYPE}"
244 cli_space
245
246 cli_print_fmt1 1 "Key Exchange:" "${KEY_EXCHANGE}"
247
248 # Key Lifetime
249 if isinteger LIFETIME && [ ${LIFETIME} -gt 0 ]; then
250 cli_print_fmt1 1 "Key Lifetime:" "$(format_time ${LIFETIME})"
251 else
252 log ERROR "The value for Key Lifetime is not a valid integer greater zero."
253 fi
254
255 # PFS
256 if enabled PFS; then
257 cli_print_fmt1 1 "Perfect Forward Secrecy:" "enabled"
258 else
259 cli_print_fmt1 1 "Perfect Forward Secrecy:" "disabled"
260 fi
261 cli_space
262
263 # Compression
264 if enabled COMPRESSION; then
265 cli_print_fmt1 1 "Compression:" "enabled"
266 else
267 cli_print_fmt1 1 "Compression:" "disabled"
268 fi
269 cli_space
270 }
271
272 # This function checks if a vpn security policy exists
273 # Returns True when yes and false when not
274 vpn_security_policy_exists() {
275 assert [ $# -eq 1 ]
276
277 local name=${1}
278
279 local path=$(vpn_security_policies_path "${name}")
280
281 [ -f ${path} ] && return ${EXIT_TRUE} || return ${EXIT_FALSE}
282 }
283
284
285 # This function parses the parameters for the 'cipher' command
286 vpn_security_policies_cipher(){
287 local name=${1}
288 shift
289
290 if [ $# -eq 0 ]; then
291 log ERROR "You must pass at least one value after cipher"
292 return ${EXIT_ERROR}
293 fi
294
295 local CIPHER
296 if ! vpn_security_policies_read_config ${name} "CIPHER"; then
297 return ${EXIT_ERROR}
298 fi
299
300 # Remove duplicated entries to proceed the list safely
301 CIPHER="$(list_unique ${CIPHER})"
302
303 while [ $# -gt 0 ]; do
304 case "${1}" in
305 -*)
306 value=${1#-}
307 # Check if the cipher is in the list of ciphers and
308 # check if the list has after removing this cipher at least one valid value
309 if list_match ${value} ${CIPHER}; then
310 list_remove CIPHER ${value}
311 else
312 # We do not break here because this error does not break the processing of the next maybe valid values.
313 log ERROR "Can not remove ${value} from the list of Ciphers because ${value} is not in the list."
314 fi
315 ;;
316 +*)
317 value=${1#+}
318 # Check if the Ciphers is in the list of supported ciphers.
319 if ! isoneof value ${!VPN_SUPPORTED_CIPHERS[@]}; then
320 # We do not break here because this error does not break the processing of the next maybe valid values.
321 log ERROR "${value} is not a supported cipher and can thats why not added to the list of ciphers."
322 else
323 if list_match ${value} ${CIPHER}; then
324 log WARNING "${value} is already in the list of ciphers of this policy."
325 else
326 list_append CIPHER ${value}
327 fi
328 fi
329 ;;
330 esac
331 shift
332 done
333
334 # Check if the list contain at least one valid cipher
335 if [ $(list_length ${CIPHER}) -ge 1 ]; then
336 if ! vpn_security_policies_write_config_key ${name} "CIPHER" ${CIPHER}; then
337 log ERROR "The changes for the vpn security policy ${name} could not be written."
338 fi
339 else
340 log ERROR "After proceding all ciphers the list is empty and thats why no changes are written."
341 return ${EXIT_ERROR}
342 fi
343 }
344
345 # This function parses the parameters for the 'compression' command
346 vpn_security_policies_compression(){
347 local name=${1}
348 local value=${2}
349
350 # Check if we get only one argument after compression <name>
351 if [ ! $# -eq 2 ]; then
352 log ERROR "The number of arguments do not match. Only one argument after compression is allowed."
353 return ${EXIT_ERROR}
354 fi
355
356 if ! isbool value; then
357 # We suggest only two values to avoid overburding the user.
358 log ERROR "Invalid Argument ${value}"
359 return ${EXIT_ERROR}
360 fi
361
362 vpn_security_policies_write_config_key "${name}" "COMPRESSION" "${value}"
363 }
364
365 # This function parses the parameters for the 'group-type' command
366 vpn_security_policies_group_type(){
367 local name=${1}
368 shift
369
370 if [ $# -eq 0 ]; then
371 log ERROR "You must pass at least one value after group-type"
372 return ${EXIT_ERROR}
373 fi
374
375 local GROUP_TYPE
376 if ! vpn_security_policies_read_config ${name} "GROUP_TYPE"; then
377 return ${EXIT_ERROR}
378 fi
379
380 # Remove duplicated entries to proceed the list safely
381 GROUP_TYPE="$(list_unique ${GROUP_TYPE})"
382
383 while [ $# -gt 0 ]; do
384 case "${1}" in
385 -*)
386 value=${1#-}
387 # Check if the group type is in the list of group types and
388 # check if the list has after removing this group type at leatst one valid value
389 if list_match ${value} ${GROUP_TYPE}; then
390 list_remove GROUP_TYPE ${value}
391 else
392 # We do not break here because this error does not break the processing of the next maybe valid values.
393 log ERROR "Can not remove ${value} from the list of group types because ${value} is not in the list."
394 fi
395 ;;
396 +*)
397 value=${1#+}
398 # Check if the group type is in the list of supported group types.
399 if ! isoneof value ${VPN_SUPPORTED_GROUP_TYPES}; then
400 # We do not break here because the processing of other maybe valid values are indepent from this error.
401 log ERROR "${value} is not a supported group type and can thats why not added to the list of group types."
402 else
403 if list_match ${value} ${GROUP_TYPE}; then
404 log WARNING "${value} is already in the list of group-types of this policy."
405 else
406 list_append GROUP_TYPE ${value}
407 fi
408 fi
409 ;;
410 esac
411 shift
412 done
413
414 # Check if the list contain at least one valid group-type
415 if [ $(list_length ${GROUP_TYPE}) -ge 1 ]; then
416 if ! vpn_security_policies_write_config_key ${name} "GROUP_TYPE" ${GROUP_TYPE}; then
417 log ERROR "The changes for the vpn security policy ${name} could not be written."
418 fi
419 else
420 log ERROR "After proceding all group types the list is empty and thats why no changes are written."
421 return ${EXIT_ERROR}
422 fi
423 }
424
425 # This function parses the parameters for the 'integrity' command
426 vpn_security_policies_integrity(){
427 local name=${1}
428 shift
429
430 if [ $# -eq 0 ]; then
431 log ERROR "You must pass at least one value after integrity."
432 return ${EXIT_ERROR}
433 fi
434
435 local INTEGRITY
436 if ! vpn_security_policies_read_config ${name} "INTEGRITY"; then
437 return ${EXIT_ERROR}
438 fi
439
440 # Remove duplicated entries to proceed the list safely
441 INTEGRITY="$(list_unique ${INTEGRITY})"
442
443 while [ $# -gt 0 ]; do
444 case "${1}" in
445 -*)
446 value=${1#-}
447 # Check if the integrity hash is in the list of integrity hashes and
448 # check if the list has after removing this integrity hash at least one valid value
449 if list_match ${value} ${INTEGRITY}; then
450 list_remove INTEGRITY ${value}
451 else
452 # We do not break here because the processing of other maybe valid values are indepent from this error.
453 log ERROR "Can not remove ${value} from the list of integrity hashes because ${value} is not in the list."
454 fi
455 ;;
456 +*)
457 value=${1#+}
458 # Check if the Ciphers is in the list of supported integrity hashes.
459 if ! isoneof value ${VPN_SUPPORTED_INTEGRITY}; then
460 # We do not break here because the processing of other maybe valid values are indepent from this error.
461 log ERROR "${value} is not a supported integrity hash and can thats why not added to the list of integrity hashes."
462 else
463 if list_match ${value} ${INTEGRITY}; then
464 log WARNING "${value} is already in the list of integrety hashes of this policy."
465 else
466 list_append INTEGRITY ${value}
467 fi
468 fi
469 ;;
470 esac
471 shift
472 done
473
474 # Check if the list contain at least one valid group-type
475 if [ $(list_length ${INTEGRITY}) -ge 1 ]; then
476 if ! vpn_security_policies_write_config_key ${name} "INTEGRITY" ${INTEGRITY}; then
477 log ERROR "The changes for the vpn security policy ${name} could not be written."
478 fi
479 else
480 log ERROR "After proceding all integrity hashes the list is empty and thats why no changes are written."
481 return ${EXIT_ERROR}
482 fi
483 }
484
485 # This function parses the parameters for the 'key-exchange' command
486 vpn_security_policies_key_exchange() {
487 local name=${1}
488 local value=${2}
489
490 # Check if we get only one argument after key-exchange <name>
491 if [ ! $# -eq 2 ]; then
492 log ERROR "The number of arguments do not match. Only argument after key-exchange is allowed."
493 return ${EXIT_ERROR}
494 fi
495
496 if ! isoneof value "ikev1" "ikev2" "IKEV1" "IKEV2"; then
497 log ERROR "Invalid Argument ${value}"
498 return ${EXIT_ERROR}
499 fi
500
501 vpn_security_policies_write_config_key "${name}" "KEY_EXCHANGE" "${value,,}"
502 }
503
504 # This function parses the parameters for the 'lifetime' command.
505 vpn_security_policies_lifetime(){
506 local name=${1}
507 shift
508
509 local value=$@
510
511 # Check if we get only one argument after lifetime <name>
512 if [ ! $# -ge 1 ]; then
513 log ERROR "The number of arguments do not match you must provide at least one integer value or a valid time with the format <hours>h <minutes>m <seconds>s"
514 return ${EXIT_ERROR}
515 fi
516
517 if ! isinteger value; then
518 value=$(parse_time $@)
519 if [ ! $? -eq 0 ]; then
520 log ERROR "Parsing the passed time was not sucessful please check the passed values."
521 return ${EXIT_ERROR}
522 fi
523 fi
524
525 if [ ${value} -le 0 ]; then
526 log ERROR "The passed time value must be in the sum greater zero seconds."
527 return ${EXIT_ERROR}
528 fi
529
530 vpn_security_policies_write_config_key "${name}" "LIFETIME" "${value}"
531 }
532
533 # This function parses the parameters for the 'pfs' command
534 vpn_security_policies_pfs(){
535 local name=${1}
536 local value=${2}
537
538 # Check if we get only one argument after pfs <name>
539 if [ ! $# -eq 2 ]; then
540 log ERROR "The number of arguments do not match. Only argument after pfs is allowed."
541 return ${EXIT_ERROR}
542 fi
543
544 if [ ! $# -eq 2 ] || ! isbool value; then
545 # We suggest only two values to avoid overburding the user.
546 log ERROR "Invalid Argument ${value}"
547 return ${EXIT_ERROR}
548 fi
549
550 vpn_security_policies_write_config_key "${name}" "PFS" "${value}"
551 }
552
553 # This function checks if a vpn security policy name is valid
554 # Allowed are only A-Za-z0-9
555 vpn_security_policies_check_name() {
556 assert [ $# -eq 1 ]
557
558 local name=${1}
559
560 [[ ${name} =~ [^[:alnum:]$] ]]
561 }
562
563 # Function that creates based on the paramters one ore more new vpn security policies
564 vpn_security_policies_new() {
565 if [ $# -gt 1 ]; then
566 error "Too many arguments"
567 return ${EXIT_ERROR}
568 fi
569
570 local name="${1}"
571 if ! isset name; then
572 error "Please provide a name"
573 return ${EXIT_ERROR}
574 fi
575
576 # Check for duplicates
577 if vpn_security_policy_exists "${name}"; then
578 error "The VPN security policy with name ${name} already exists"
579 return ${EXIT_ERROR}
580 fi
581
582 # Check if name is valid
583 if vpn_security_policies_check_name "${name}"; then
584 error "'${name}' contains illegal characters"
585 return ${EXIT_ERROR}
586 fi
587
588 # Check if we have a read-only policy with the same name
589 if vpn_security_policies_check_readonly "${name}"; then
590 error "The VPN security policy ${name} is read-only"
591 return ${EXIT_ERROR}
592 fi
593
594 # Check if our source policy exists
595 if ! vpn_security_policy_exists "${VPN_DEFAULT_SECURITY_POLICY}"; then
596 error "Default VPN Security Policy '${VPN_DEFAULT_SECURITY_POLICY}' does not exist"
597 return ${EXIT_ERROR}
598 fi
599
600 log DEBUG "Creating VPN Security Policy ${name}"
601
602 if copy "$(vpn_security_policies_path "${VPN_DEFAULT_SECURITY_POLICY}")"
603 "$(vpn_security_policies_path ${name})"; then
604 log INFO "VPN Security Policy ${name} successfully created"
605 else
606 log ERROR "Could not create VPN Security Policy ${name}"
607 return ${EXIT_ERROR}
608 fi
609
610 # Show the newly created policy
611 vpn_security_policies_show "${name}"
612 }
613
614 # Function that deletes based on the passed parameters one ore more vpn security policies
615 vpn_security_policies_destroy() {
616 local name
617 for name in $@; do
618 if ! vpn_security_policy_exists ${name}; then
619 log ERROR "The vpn security policy ${name} does not exist."
620 continue
621 fi
622
623 if vpn_security_policies_check_readonly ${name}; then
624 log ERROR "The vpn security policy ${name} cannot be deleted."
625 continue
626 fi
627
628 log DEBUG "Deleting vpn security policy ${name}"
629 settings_remove $(vpn_security_policies_path ${name})
630 done
631 }