]> git.ipfire.org Git - people/stevee/network.git/blob - src/functions/functions.wireless-networks
Fix creating new configs
[people/stevee/network.git] / src / functions / functions.wireless-networks
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 WIRELESS_NETWORK_SUPPORTED_PSK_MODES="WPA2-PSK-SHA256 WPA2-PSK WPA-PSK-SHA256 WPA-PSK"
23
24 WIRELESS_NETWORK_SUPPORTED_MODES="${WIRELESS_NETWORK_SUPPORTED_PSK_MODES} \
25 802.1X WPA-EAP NONE"
26
27 WIRELESS_NETWORK_CONFIG_SETTINGS="ANONYMOUS_IDENTITY EAP_MODES HIDDEN \
28 IDENTITY MODES PASSWORD PRIORITY PSK SSID"
29
30 cli_wireless_network() {
31 case "${1}" in
32 new)
33 wireless_network_new "${@:2}"
34 ;;
35 destroy)
36 wireless_network_destroy "${@:2}"
37 ;;
38 *)
39 local ssid="${1}"
40 local key="${2//-/_}"
41 shift 2
42
43 if ! wireless_network_exists "${ssid}"; then
44 error "No such wireless network: ${ssid}"
45 return ${EXIT_ERROR}
46 fi
47
48 # Convert SSID into usable format
49 local handle="$(wireless_network_hash "${ssid}")"
50
51 case "${key}" in
52 modes|pre_shared_key|priority)
53 wireless_network_${key} "${handle}" "$@"
54 ;;
55 show)
56 wireless_network_show "${handle}"
57 exit $?
58 ;;
59 *)
60 error "Unrecognized argument: ${key}"
61 exit ${EXIT_ERROR}
62 ;;
63 esac
64 ;;
65 esac
66 }
67
68 wireless_network_list() {
69 list_directory "${NETWORK_WIRELESS_NETWORKS_DIR}"
70 }
71
72 wireless_network_list_ssids() {
73 local handle
74 for handle in $(wireless_network_list); do
75 local ${WIRELESS_NETWORK_CONFIG_SETTINGS}
76 if ! wireless_network_read_config "${handle}"; then
77 continue
78 fi
79
80 print "${SSID}"
81 done
82 }
83
84 # This function writes all values to a via ${ssid} specificated wireless network configuration file
85 wireless_network_write_config() {
86 assert [ $# -ge 1 ]
87
88 local handle="${1}"
89
90 local path="${NETWORK_WIRELESS_NETWORKS_DIR}/${handle}/settings"
91
92 if ! settings_write "${path}" ${WIRELESS_NETWORK_CONFIG_SETTINGS}; then
93 log ERROR "Could not write configuration"
94 return ${EXIT_ERROR}
95 fi
96
97 # When we get here the writing of the config file was successful
98 return ${EXIT_OK}
99 }
100
101 # This funtion writes the value for one key to a via ${ssid} specificated
102 # wireless network configuration file
103 wireless_network_write_config_key() {
104 assert [ $# -ge 3 ]
105
106 local handle="${1}"
107 local key="${2}"
108 shift 2
109
110 local value="$@"
111
112 local ${WIRELESS_NETWORK_CONFIG_SETTINGS}
113
114 # Read the config settings
115 if ! wireless_network_read_config "${handle}"; then
116 return ${EXIT_ERROR}
117 fi
118
119 log DEBUG "Set '${key}' to new value '${value}' in wireless network '${SSID}'"
120
121 # Set the key to a new value
122 assign "${key}" "${value}"
123
124 if ! wireless_network_write_config "${handle}"; then
125 return ${EXIT_ERROR}
126 fi
127
128 return ${EXIT_OK}
129 }
130
131 # Reads one or more keys out of a settings file or all if no key is provided.
132 wireless_network_read_config() {
133 assert [ $# -ge 1 ]
134
135 local handle="${1}"
136 shift
137
138 local args
139 if [ $# -eq 0 ] && [ -n "${WIRELESS_NETWORK_CONFIG_SETTINGS}" ]; then
140 list_append args ${WIRELESS_NETWORK_CONFIG_SETTINGS}
141 else
142 list_append args "$@"
143 fi
144
145 local path="${NETWORK_WIRELESS_NETWORKS_DIR}/${handle}/settings"
146
147 if ! settings_read "${path}" ${args}; then
148 log ERROR "Could not read settings for wireless network ${handle}"
149 return ${EXIT_ERROR}
150 fi
151 }
152
153 # This function checks if a wireless network exists
154 # Returns True when yes and false when not
155 wireless_network_exists() {
156 local ssid="${1}"
157
158 local handle="$(wireless_network_hash "${ssid}")"
159 assert isset handle
160
161 # We cannot use wireless_network_read_config here beacuse we would end in a loop
162 local SSID
163 if ! settings_read "${NETWORK_WIRELESS_NETWORKS_DIR}/${handle}/settings" SSID; then
164 return ${EXIT_FALSE}
165 fi
166
167 if [ "${SSID}" = "${ssid}" ]; then
168 return ${EXIT_TRUE}
169 else
170 return ${EXIT_FALSE}
171 fi
172 }
173
174 wireless_network_hash() {
175 assert [ $# -eq 1 ]
176
177 local string="${1}"
178
179 local hash=$(echo -n "${string}" | md5sum )
180 hash=${hash%% -}
181
182 local path="${NETWORK_WIRELESS_NETWORKS_DIR}/*${hash}"
183
184 if [ -d "${path}" ]; then
185 basename "${path}"
186 else
187 local normalized=$(normalize "${string}")
188 normalized=${normalized%-}
189 echo "${normalized}-${hash}"
190 fi
191 }
192
193 wireless_network_new() {
194 if [ $# -gt 1 ]; then
195 error "Too many arguments"
196 return ${EXIT_ERROR}
197 fi
198
199 local ssid="${1}"
200
201 if ! isset ssid; then
202 error "Please provide a SSID"
203 return ${EXIT_ERROR}
204 fi
205
206 # Check for duplicates
207 if wireless_network_exists "${ssid}"; then
208 error "The wireless network ${ssid} already exists"
209 return ${EXIT_ERROR}
210 fi
211
212 local handle="$(wireless_network_hash "${ssid}")"
213 assert isset handle
214
215 log DEBUG "Creating wireless network '${ssid}'"
216
217 if ! mkdir -p "${NETWORK_WIRELESS_NETWORKS_DIR}/${handle}"; then
218 log ERROR "Could not create config directory for wireless network ${ssid}"
219 return ${EXIT_ERROR}
220 fi
221
222 local ${WIRELESS_NETWORK_CONFIG_SETTINGS}
223 MODES="${WIRELESS_NETWORK_SUPPORTED_MODES}"
224 SSID="${ssid}"
225 PRIORITY=0
226
227 if ! wireless_network_write_config "${handle}"; then
228 log ERROR "Could not write new config file"
229 return ${EXIT_ERROR}
230 fi
231 }
232
233 # Deletes a wireless network
234 wireless_network_destroy() {
235 local ssid="${1}"
236
237 if ! wireless_network_exists "${ssid}"; then
238 error "No such wireless network: ${ssid}"
239 return ${EXIT_ERROR}
240 fi
241
242 local handle="$(wireless_network_hash "${ssid}")"
243 assert isset handle
244
245 if ! rm -rf "${NETWORK_WIRELESS_NETWORKS_DIR}/${handle}"; then
246 error "Could not delete the wireless network"
247 return ${EXIT_ERROR}
248 fi
249
250 log INFO "Successfully destroyed wireless network ${ssid}"
251 return ${EXIT_OK}
252 }
253
254 wireless_networks_mode_is_valid() {
255 assert [ $# -eq 1 ]
256 local mode=${1}
257
258 if isoneof mode ${WIRELESS_NETWORK_SUPPORTED_MODES}; then
259 return ${EXIT_TRUE}
260 else
261 return ${EXIT_FALSE}
262 fi
263
264 }
265
266 # WIRELESS_NETWORK_SUPPORTED_MODES
267 wireless_network_modes() {
268 if [ ! $# -ge 2 ]; then
269 log ERROR "Not enough arguments"
270 return ${EXIT_ERROR}
271 fi
272 local handle="${1}"
273 shift
274
275 if [ $# -eq 0 ]; then
276 log ERROR "You must pass at least one value after mode"
277 return ${EXIT_ERROR}
278 fi
279
280 local ${WIRELESS_NETWORK_CONFIG_SETTINGS}
281 if ! wireless_network_read_config "${handle}"; then
282 error "Could not read configuration"
283 return ${EXIT_ERROR}
284 fi
285
286 # Remove duplicated entries to proceed the list safely
287 MODES="$(list_unique ${MODES})"
288
289 local modes_added
290 local modes_removed
291 local modes_set
292
293 while [ $# -gt 0 ]; do
294 local arg="${1}"
295
296 case "${arg}" in
297 +*)
298 list_append modes_added "${arg:1}"
299 ;;
300 -*)
301 list_append modes_removed "${arg:1}"
302 ;;
303 [A-Z0-9]*)
304 list_append modes_set "${arg}"
305 ;;
306 *)
307 error "Invalid argument: ${arg}"
308 return ${EXIT_ERROR}
309 ;;
310 esac
311 shift
312 done
313
314 # Check if the user is trying a mixed operation
315 if ! list_is_empty modes_set && (! list_is_empty modes_added || ! list_is_empty modes_removed); then
316 error "You cannot reset the modes list and add or remove modes at the same time"
317 return ${EXIT_ERROR}
318 fi
319
320 # Set new modes list
321 if ! list_is_empty modes_set; then
322 # Check if all modes are valid
323 local mode
324 for mode in ${modes_set}; do
325 if ! wireless_networks_mode_is_valid ${mode}; then
326 error "Unsupported mode: ${mode}"
327 return ${EXIT_ERROR}
328 fi
329 done
330
331 MODES="${modes_set}"
332
333 # Perform incremental updates
334 else
335 local modes
336
337 # Perform all removals
338 for mode in ${modes_removed}; do
339 if ! list_remove MODES ${mode}; then
340 warning "${mode} was not on the list and could not be removed"
341 fi
342 done
343
344 for mode in ${modes_added}; do
345 if wireless_networks_mode_is_valid ${mode}; then
346 if ! list_append_unique MODES ${mode}; then
347 warning "${mode} is already on the modes list"
348 fi
349 else
350 warning "${mode} is unknown or unsupported and could not be added"
351 fi
352 done
353 fi
354
355 # Check if the list contain at least one valid mode
356 if list_is_empty MODES; then
357 error "Cannot save an empty mode list"
358 return ${EXIT_ERROR}
359 fi
360
361 if ! wireless_network_write_config "${handle}"; then
362 log ERROR "Could not write configuration settings"
363 return ${EXIT_ERROR}
364 fi
365 }
366
367 wireless_network_pre_shared_key() {
368 if [ ! $# -eq 2 ]; then
369 log ERROR "Not enough arguments"
370 return ${EXIT_ERROR}
371 fi
372
373 local handle="${1}"
374 local psk="${2}"
375
376 local ${WIRELESS_NETWORK_CONFIG_SETTINGS}
377 if ! wireless_network_read_config "${handle}"; then
378 error "Could not read configuration"
379 return ${EXIT_ERROR}
380 fi
381
382 # Validate the key if encryption mode is known
383 if isset ENCRYPTION_MODE && [ "${ENCRYPTION_MODE}" != "NONE" ]; then
384 if ! wireless_pre_share_key_is_valid "${ENCRYPTION_MODE}" "${psk}"; then
385 error "The pre-shared-key is invalid for this wireless network: ${psk}"
386 return ${EXIT_ERROR}
387 fi
388 fi
389
390 if ! wireless_network_write_config_key "${handle}" "PSK" "${psk}"; then
391 log ERROR "Could not write configuration settings"
392 return ${EXIT_ERROR}
393 fi
394 }
395
396 wireless_networks_priority_is_valid() {
397 assert [ $# -eq 1 ]
398
399 local priority=${1}
400
401 if ! isinteger priority || [ ! ${priority} -ge 0 ] || [ ! ${priority} -le 999 ]; then
402 return ${EXIT_FALSE}
403 fi
404
405 return ${EXIT_TRUE}
406 }
407
408 wireless_network_priority() {
409 if [ ! $# -eq 2 ]; then
410 log ERROR "Not enough arguments"
411 return ${EXIT_ERROR}
412 fi
413
414 local handle="${1}"
415 local priority=${2}
416
417 if ! wireless_networks_priority_is_valid ${priority}; then
418 error "The priority must be an integer greater or eqal zero and and less then 1000"
419 return ${EXIT_ERROR}
420 fi
421
422 if ! wireless_network_write_config_key "${handle}" "PRIORITY" "${priority}"; then
423 log ERROR "Could not write configuration settings"
424 return ${EXIT_ERROR}
425 fi
426 }
427
428 wireless_networks_write_wpa_supplicant_configuration() {
429 local device="${1}"
430
431 local file="${WPA_SUPPLICANT_CONF_DIR}/${device}.conf"
432
433 # Ensure we can write the file
434 make_parent_directory "${file}"
435
436 (
437 # Write a config header
438 wpa_supplicant_config_header
439
440 wireless_networks_to_wpa_supplicant
441 ) > ${file}
442 }
443
444 wireless_networks_to_wpa_supplicant() {
445 local handle
446 for handle in $(wireless_network_list); do
447 wireless_network_to_wpa_supplicant "${handle}"
448 done
449 }
450
451 wireless_network_to_wpa_supplicant() {
452 local handle="${1}"
453
454 local ${WIRELESS_NETWORK_CONFIG_SETTINGS}
455 if ! wireless_network_read_config "${handle}"; then
456 error "Could not read configuration for ${handle}"
457 return ${EXIT_ERROR}
458 fi
459
460 local auth_alg
461 local group
462 local key_mgmt
463 local pairwise
464 local proto
465
466 local mode
467 for mode in ${WIRELESS_NETWORK_SUPPORTED_MODES}; do
468 # Skip any disabled modes
469 if isset MODES && ! list_match "${mode}" ${MODES}; then
470 continue
471 fi
472
473 case "${mode}" in
474 # WPA2 (802.11i)
475 WPA2-PSK|WPA2-PSK-SHA256)
476 list_append_unique auth_alg "OPEN"
477 list_append_unique key_mgmt "${mode/WPA2/WPA}"
478 list_append_unique proto "RSN"
479
480 local p
481 for p in CCMP TKIP; do
482 list_append_unique pairwise "${p}"
483 done
484
485 local g
486 for g in CCMP TKIP WEP104 WEP40; do
487 list_append_unique group "${g}"
488 done
489 ;;
490
491 # WPA
492 WPA-PSK|WPA-PSK-SHA256)
493 list_append_unique auth_alg "OPEN"
494 list_append_unique key_mgmt "${mode}"
495 list_append_unique proto "WPA"
496
497 local p
498 for p in CCMP TKIP; do
499 list_append_unique pairwise "${p}"
500 done
501
502 local g
503 for g in CCMP TKIP WEP104 WEP40; do
504 list_append_unique group "${g}"
505 done
506 ;;
507
508 # 802.1X
509 802.1X)
510 list_append_unique key_mgmt "IEEE8021X"
511 ;;
512
513 # No encryption. DANGEROUS!
514 NONE)
515 list_append_unique auth_alg "OPEN"
516 list_append_unique key_mgmt "NONE"
517 ;;
518 esac
519 done
520
521 assert isset auth_alg
522 assert isset key_mgmt
523
524 # Certificate Paths
525 local ca_cert_path="${NETWORK_WIRELESS_NETWORKS_DIR}/${handle}/ca.pem"
526 local client_cert_path="${NETWORK_WIRELESS_NETWORKS_DIR}/${handle}/client.pem"
527 local client_key_path="${NETWORK_WIRELESS_NETWORKS_DIR}/${handle}/client.key"
528
529 print_indent 0 "# ${SSID}"
530 print_indent 0 "network={"
531 print_indent 1 "ssid=\"${SSID}\""
532
533 # Actively scan for hidden networks
534 if enabled HIDDEN; then
535 print_indent 1 "scan_ssid=1"
536 fi
537
538 # Priority
539 if isinteger PRIORITY; then
540 print_indent 1 "priority=${PRIORITY}"
541 fi
542 print
543
544 # Authentication
545 print_indent 1 "# Authentication"
546 print_indent 1 "auth_alg=${auth_alg}"
547 print_indent 1 "key_mgmt=${key_mgmt}"
548
549 local i
550 for i in proto pairwise group; do
551 print_indent 1 "${i}=${!i}"
552 done
553 print
554
555 # PSK
556 if isset PSK; then
557 print_indent 1 "# Pre Shared Key"
558 print_indent 1 "psk=\"${PSK}\""
559 fi
560
561 if isset EAP_MODES; then
562 print_indent 1 "# EAP"
563 print_indent 1 "eap=${EAP_MODES}"
564 print
565 fi
566
567 if isset IDENTITY; then
568 print_indent 1 "# Credentials"
569 print_indent 1 "identity=\"${IDENTITY}\""
570
571 if isset PASSWORD; then
572 print_indent 1 "password=\"${PASSWORD}\""
573 fi
574
575 if isset ANONYMOUS_IDENTITY; then
576 print_indent 1 "anonymous_identity=\"${ANONYMOUS_IDENTITY}\""
577 fi
578 print
579 fi
580
581 # Client Certificate
582 if file_exists "${client_cert_path}" && file_exists "${client_key_path}"; then
583 print_indent 1 "# Client Certificate"
584 print_indent 1 "client_cert=\"${client_cert_path}\""
585 print_indent 1 "private_key=\"${client_key_path}\""
586 print
587 fi
588
589 # Validate server certificates
590 if file_exists "${ca_cert_path}"; then
591 print_indent 1 "ca_cert=\"${ca_cert_path}\""
592
593 elif isset CA_BUNDLE; then
594 print_indent 1 "ca_cert=\"${CA_BUNDLE}\""
595 fi
596
597 print_indent 0 "}"
598 print
599 }