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