diff --git a/include/zephyr/net/wifi_credentials.h b/include/zephyr/net/wifi_credentials.h index 0241930e97d..8b00d67b199 100644 --- a/include/zephyr/net/wifi_credentials.h +++ b/include/zephyr/net/wifi_credentials.h @@ -11,18 +11,19 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + /** + * @brief Library that provides a way to store and load Wi-Fi credentials. * @defgroup wifi_credentials Wi-Fi credentials library * @ingroup networking * @since 4.0 * @version 0.1.0 * @{ - * @brief Library that provides a way to store and load Wi-Fi credentials. */ -#ifdef __cplusplus -extern "C" { -#endif /* this entry contains a BSSID */ #define WIFI_CREDENTIALS_FLAG_BSSID BIT(0) @@ -32,11 +33,18 @@ extern "C" { #define WIFI_CREDENTIALS_FLAG_2_4GHz BIT(2) /* this entry can use the 5 GHz band */ #define WIFI_CREDENTIALS_FLAG_5GHz BIT(3) +/* this entry can use the 6 GHz band */ +#define WIFI_CREDENTIALS_FLAG_6GHz BIT(4) /* this entry requires management frame protection */ -#define WIFI_CREDENTIALS_FLAG_MFP_REQUIRED BIT(4) +#define WIFI_CREDENTIALS_FLAG_MFP_REQUIRED BIT(5) /* this entry disables management frame protection */ -#define WIFI_CREDENTIALS_FLAG_MFP_DISABLED BIT(5) +#define WIFI_CREDENTIALS_FLAG_MFP_DISABLED BIT(6) +/* this entry has anonymous identity configured */ +#define WIFI_CREDENTIALS_FLAG_ANONYMOUS_IDENTITY BIT(7) +/* this entry has key password configured */ +#define WIFI_CREDENTIALS_FLAG_KEY_PASSWORD BIT(8) +/* Maximum length of the password */ #define WIFI_CREDENTIALS_MAX_PASSWORD_LEN \ MAX(WIFI_PSK_MAX_LEN, CONFIG_WIFI_CREDENTIALS_SAE_PASSWORD_LENGTH) @@ -49,13 +57,38 @@ extern "C" { * */ struct wifi_credentials_header { - enum wifi_security_type type; /**< Wi-Fi security type */ - char ssid[WIFI_SSID_MAX_LEN]; /**< SSID (Service Set Identifier) */ - size_t ssid_len; /**< Length of the SSID */ - uint32_t flags; /**< Flags for controlling detail settings */ - uint32_t timeout; /**< Timeout for connecting to the network */ - uint8_t bssid[WIFI_MAC_ADDR_LEN]; /**< BSSID (Basic Service Set Identifier) */ - uint8_t channel; /**< Channel on which the network operates */ + /** Wi-Fi security type */ + enum wifi_security_type type; + + /** SSID (Service Set Identifier) */ + char ssid[WIFI_SSID_MAX_LEN]; + + /** Length of the SSID */ + size_t ssid_len; + + /** Flags for controlling detail settings */ + uint32_t flags; + + /** Timeout for connecting to the network */ + uint32_t timeout; + + /** BSSID (Basic Service Set Identifier) */ + uint8_t bssid[WIFI_MAC_ADDR_LEN]; + + /** Channel on which the network operates */ + uint8_t channel; + + /** Anonymous identifier (Limited to 16 bytes due to settings subsystem overflow) */ + char anon_id[16]; + + /** Length of the Anonymous identifier */ + uint8_t aid_length; + + /** Password/PSK (Limited to 16 bytes due to settings subsystem overflow) */ + char key_passwd[16]; + + /** Length of the Password */ + uint8_t key_passwd_length; }; /** @@ -67,9 +100,14 @@ struct wifi_credentials_header { * */ struct wifi_credentials_personal { - struct wifi_credentials_header header; /**< Header */ - char password[WIFI_CREDENTIALS_MAX_PASSWORD_LEN]; /**< Password/PSK */ - size_t password_len; /**< Length of the password */ + /** Header */ + struct wifi_credentials_header header; + + /** Password/PSK */ + char password[WIFI_CREDENTIALS_MAX_PASSWORD_LEN]; + + /** Length of the password */ + size_t password_len; }; /** @@ -77,14 +115,29 @@ struct wifi_credentials_personal { * @note This functionality is not yet implemented. */ struct wifi_credentials_enterprise { - struct wifi_credentials_header header; /**< Header */ - size_t identity_len; /**< Length of the identity */ - size_t anonymous_identity_len; /**< Length of the anonymous identity */ - size_t password_len; /**< Length of the password */ - size_t ca_cert_len; /**< Length of the CA certificate */ - size_t client_cert_len; /**< Length of the client certificate */ - size_t private_key_len; /**< Length of the private key */ - size_t private_key_pw_len; /**< Length of the private key password */ + /** Header */ + struct wifi_credentials_header header; + + /** Length of the identity */ + size_t identity_len; + + /** Length of the anonymous identity */ + size_t anonymous_identity_len; + + /** Length of the password */ + size_t password_len; + + /** Length of the CA certificate */ + size_t ca_cert_len; + + /** Length of the client certificate */ + size_t client_cert_len; + + /** Length of the private key */ + size_t private_key_len; + + /** Length of the private key password */ + size_t private_key_pw_len; }; /** @@ -194,6 +247,7 @@ int wifi_credentials_delete_all(void); /** * @brief Callback type for wifi_credentials_for_each_ssid. + * * @param[in] cb_arg arguments for the callback function. Appropriate cb_arg is * transferred by wifi_credentials_for_each_ssid. * @param[in] ssid SSID diff --git a/subsys/net/lib/wifi_credentials/Kconfig b/subsys/net/lib/wifi_credentials/Kconfig index 2fc002f00b8..f50e210b080 100644 --- a/subsys/net/lib/wifi_credentials/Kconfig +++ b/subsys/net/lib/wifi_credentials/Kconfig @@ -57,6 +57,8 @@ config WIFI_CREDENTIALS_SHELL bool "Shell commands to manage Wi-Fi credentials" default y depends on SHELL + select SHELL_GETOPT + select GETOPT_LONG depends on !WIFI_CREDENTIALS_BACKEND_NONE config WIFI_CREDENTIALS_CONNECT_STORED diff --git a/subsys/net/lib/wifi_credentials/wifi_credentials_shell.c b/subsys/net/lib/wifi_credentials/wifi_credentials_shell.c index c9652ba390c..0e37046e494 100644 --- a/subsys/net/lib/wifi_credentials/wifi_credentials_shell.c +++ b/subsys/net/lib/wifi_credentials/wifi_credentials_shell.c @@ -15,12 +15,14 @@ #include #include +#include #include #include #include #include +#define MAX_BANDS_STR_LEN 64 #define MACSTR "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx" static void print_network_info(void *cb_arg, const char *ssid, size_t ssid_len) @@ -88,183 +90,183 @@ static void print_network_info(void *cb_arg, const char *ssid, size_t ssid_len) shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, "\n"); } -static enum wifi_security_type parse_sec_type(const char *s) -{ - if (strcmp("OPEN", s) == 0) { - return WIFI_SECURITY_TYPE_NONE; - } - - if (strcmp("WPA2-PSK", s) == 0) { - return WIFI_SECURITY_TYPE_PSK; - } - - if (strcmp("WPA2-PSK-SHA256", s) == 0) { - return WIFI_SECURITY_TYPE_PSK_SHA256; - } - - if (strcmp("WPA3-SAE", s) == 0) { - return WIFI_SECURITY_TYPE_SAE; - } - - if (strcmp("WPA-PSK", s) == 0) { - return WIFI_SECURITY_TYPE_WPA_PSK; - } - - return WIFI_SECURITY_TYPE_UNKNOWN; -} - -static enum wifi_frequency_bands parse_band(const char *s) -{ - if (strcmp("2.4GHz", s) == 0) { - return WIFI_FREQ_BAND_2_4_GHZ; - } - - if (strcmp("5GHz", s) == 0) { - return WIFI_FREQ_BAND_5_GHZ; - } - - if (strcmp("6GHz", s) == 0) { - return WIFI_FREQ_BAND_6_GHZ; - } - - return WIFI_FREQ_BAND_UNKNOWN; -} - static int cmd_add_network(const struct shell *sh, size_t argc, char *argv[]) { - int ret; - - if (argc < 3) { - goto help; - } - - if (strnlen(argv[1], WIFI_SSID_MAX_LEN + 1) > WIFI_SSID_MAX_LEN) { - shell_error(sh, "SSID too long"); - goto help; - } - - struct wifi_credentials_personal creds = { - .header.ssid_len = strlen(argv[1]), - .header.type = parse_sec_type(argv[2]), - }; - - memcpy(creds.header.ssid, argv[1], creds.header.ssid_len); - - if (creds.header.type == WIFI_SECURITY_TYPE_UNKNOWN) { - shell_error(sh, "Cannot parse security type"); - goto help; - } - - size_t arg_idx = 3; - - if (creds.header.type == WIFI_SECURITY_TYPE_PSK || - creds.header.type == WIFI_SECURITY_TYPE_PSK_SHA256 || - creds.header.type == WIFI_SECURITY_TYPE_SAE || - creds.header.type == WIFI_SECURITY_TYPE_WPA_PSK) { - /* parse passphrase */ - if (argc < 4) { - shell_error(sh, "Missing password"); - goto help; - } - creds.password_len = strlen(argv[3]); - if (creds.password_len < WIFI_PSK_MIN_LEN) { - shell_error(sh, "Passphrase should be minimum %d characters", - WIFI_PSK_MIN_LEN); - goto help; - } - if ((creds.password_len > CONFIG_WIFI_CREDENTIALS_SAE_PASSWORD_LENGTH && - creds.header.type == WIFI_SECURITY_TYPE_SAE) || - (creds.password_len > WIFI_PSK_MAX_LEN && - creds.header.type != WIFI_SECURITY_TYPE_SAE)) { - shell_error(sh, "Password is too long for this security type"); - goto help; - } - memcpy(creds.password, argv[3], creds.password_len); - ++arg_idx; - } + int opt; + int opt_index = 0; + struct getopt_state *state; + static const struct option long_options[] = { + {"ssid", required_argument, 0, 's'}, {"passphrase", required_argument, 0, 'p'}, + {"key-mgmt", required_argument, 0, 'k'}, {"ieee-80211w", required_argument, 0, 'w'}, + {"bssid", required_argument, 0, 'm'}, {"band", required_argument, 0, 'b'}, + {"channel", required_argument, 0, 'c'}, {"timeout", required_argument, 0, 't'}, + {"identity", required_argument, 0, 'a'}, {"key-passwd", required_argument, 0, 'K'}, + {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; + char *endptr; + bool secure_connection = false; + uint8_t band; + struct wifi_credentials_personal creds = {0}; - if (arg_idx < argc) { - /* look for bssid */ - ret = sscanf(argv[arg_idx], MACSTR, &creds.header.bssid[0], &creds.header.bssid[1], - &creds.header.bssid[2], &creds.header.bssid[3], &creds.header.bssid[4], - &creds.header.bssid[5]); - if (ret == 6) { + const uint8_t all_bands[] = {WIFI_FREQ_BAND_2_4_GHZ, WIFI_FREQ_BAND_5_GHZ, + WIFI_FREQ_BAND_6_GHZ}; + bool found = false; + char bands_str[MAX_BANDS_STR_LEN] = {0}; + size_t offset = 0; + long channel; + long mfp = WIFI_MFP_OPTIONAL; + + while ((opt = getopt_long(argc, argv, "s:p:k:w:b:c:m:t:a:K:h", long_options, &opt_index)) != + -1) { + state = getopt_state_get(); + switch (opt) { + case 's': + creds.header.ssid_len = strlen(state->optarg); + if (creds.header.ssid_len > WIFI_SSID_MAX_LEN) { + shell_warn(sh, "SSID too long (max %d characters)\n", + WIFI_SSID_MAX_LEN); + return -EINVAL; + } + memcpy(creds.header.ssid, state->optarg, creds.header.ssid_len); + break; + case 'k': + creds.header.type = atoi(state->optarg); + if (creds.header.type) { + secure_connection = true; + } + break; + case 'p': + creds.password_len = strlen(state->optarg); + if (creds.password_len < WIFI_PSK_MIN_LEN) { + shell_warn(sh, "Passphrase should be minimum %d characters\n", + WIFI_PSK_MIN_LEN); + return -EINVAL; + } + if (creds.password_len > WIFI_PSK_MAX_LEN) { + shell_warn(sh, "Passphrase too long (max %d characters)\n", + WIFI_PSK_MAX_LEN); + return -EINVAL; + } + memcpy(creds.password, state->optarg, creds.password_len); + break; + case 'c': + channel = strtol(state->optarg, &endptr, 10); + if (*endptr != '\0') { + shell_error(sh, "Invalid channel: %s\n", state->optarg); + return -EINVAL; + } + + for (band = 0; band < ARRAY_SIZE(all_bands); band++) { + offset += snprintf(bands_str + offset, sizeof(bands_str) - offset, + "%s%s", band ? "," : "", + wifi_band_txt(all_bands[band])); + if (offset >= sizeof(bands_str)) { + shell_error(sh, + "Failed to parse channel: %ld: " + "band string too long\n", + channel); + return -EINVAL; + } + + if (wifi_utils_validate_chan(all_bands[band], channel)) { + found = true; + break; + } + } + + if (!found) { + shell_error(sh, "Invalid channel: %ld, checked bands: %s\n", + channel, bands_str); + return -EINVAL; + } + + creds.header.channel = channel; + break; + case 'b': + switch (atoi(state->optarg)) { + case 2: + creds.header.flags |= WIFI_CREDENTIALS_FLAG_2_4GHz; + break; + case 5: + creds.header.flags |= WIFI_CREDENTIALS_FLAG_5GHz; + break; + case 6: + creds.header.flags |= WIFI_CREDENTIALS_FLAG_6GHz; + break; + default: + shell_error(sh, "Invalid band: %d\n", atoi(state->optarg)); + return -EINVAL; + } + break; + case 'w': + if (creds.header.type == WIFI_SECURITY_TYPE_NONE || + creds.header.type == WIFI_SECURITY_TYPE_WPA_PSK) { + shell_error(sh, "MFP not supported for security type %s", + wifi_security_txt(creds.header.type)); + return -ENOTSUP; + } + mfp = strtol(state->optarg, &endptr, 10); + if (*endptr != '\0') { + shell_error(sh, "Invalid IEEE 802.11w value: %s", state->optarg); + return -EINVAL; + } + if (mfp == WIFI_MFP_DISABLE) { + creds.header.flags |= WIFI_CREDENTIALS_FLAG_MFP_DISABLED; + } else if (mfp == WIFI_MFP_REQUIRED) { + creds.header.flags |= WIFI_CREDENTIALS_FLAG_MFP_REQUIRED; + } else if (mfp > 2) { + shell_error(sh, "Invalid IEEE 802.11w value: %s", + state->optarg); + return -EINVAL; + } + break; + case 'm': + if (net_bytes_from_str(creds.header.bssid, sizeof(creds.header.bssid), + state->optarg) < 0) { + shell_warn(sh, "Invalid MAC address\n"); + return -EINVAL; + } creds.header.flags |= WIFI_CREDENTIALS_FLAG_BSSID; - ++arg_idx; + break; + case 'a': + creds.header.aid_length = strlen(state->optarg); + if (creds.header.aid_length > WIFI_ENT_IDENTITY_MAX_LEN) { + shell_warn(sh, "anon_id too long (max %d characters)\n", + WIFI_ENT_IDENTITY_MAX_LEN); + return -EINVAL; + } + memcpy(creds.header.anon_id, state->optarg, creds.header.aid_length); + creds.header.flags |= WIFI_CREDENTIALS_FLAG_ANONYMOUS_IDENTITY; + break; + case 'K': + creds.header.key_passwd_length = strlen(state->optarg); + if (creds.header.key_passwd_length > WIFI_ENT_PSWD_MAX_LEN) { + shell_warn(sh, "key_passwd too long (max %d characters)\n", + WIFI_ENT_PSWD_MAX_LEN); + return -EINVAL; + } + memcpy(creds.header.key_passwd, state->optarg, + creds.header.key_passwd_length); + creds.header.flags |= WIFI_CREDENTIALS_FLAG_KEY_PASSWORD; + break; + case 'h': + shell_help(sh); + return -ENOEXEC; + default: + shell_error(sh, "Invalid option %c\n", state->optopt); + return -EINVAL; } } - - if (arg_idx < argc) { - /* look for band */ - enum wifi_frequency_bands band = parse_band(argv[arg_idx]); - - if (band == WIFI_FREQ_BAND_2_4_GHZ) { - creds.header.flags |= WIFI_CREDENTIALS_FLAG_2_4GHz; - ++arg_idx; - } - if (band == WIFI_FREQ_BAND_5_GHZ) { - creds.header.flags |= WIFI_CREDENTIALS_FLAG_5GHz; - ++arg_idx; - } + if (creds.password_len > 0 && !secure_connection) { + shell_warn(sh, "Passphrase provided without security configuration\n"); } - if (arg_idx < argc) { - /* look for channel */ - char *end; - - creds.header.channel = strtol(argv[arg_idx], &end, 10); - if (*end == '\0') { - ++arg_idx; - } - } - - if (arg_idx < argc) { - /* look for favorite flag */ - if (strncmp("favorite", argv[arg_idx], strlen("favorite")) == 0) { - creds.header.flags |= WIFI_CREDENTIALS_FLAG_FAVORITE; - ++arg_idx; - } - } - - if (arg_idx < argc) { - /* look for mfp_disabled flag */ - if (strncmp("mfp_disabled", argv[arg_idx], strlen("mfp_disabled")) == 0) { - creds.header.flags |= WIFI_CREDENTIALS_FLAG_MFP_DISABLED; - ++arg_idx; - } else if (strncmp("mfp_required", argv[arg_idx], strlen("mfp_required")) == 0) { - creds.header.flags |= WIFI_CREDENTIALS_FLAG_MFP_REQUIRED; - ++arg_idx; - } - } - - if (arg_idx < argc) { - /* look for timeout */ - char *end; - - creds.header.timeout = strtol(argv[arg_idx], &end, 10); - if (*end == '\0') { - ++arg_idx; - } - } - - if (arg_idx != argc) { - for (size_t i = arg_idx; i < argc; ++i) { - shell_warn(sh, "Unparsed arg: [%s]", argv[i]); - } + if (creds.header.ssid_len == 0) { + shell_error(sh, "SSID not provided\n"); + shell_help(sh); + return -EINVAL; } return wifi_credentials_set_personal_struct(&creds); -help: - shell_print(sh, "Usage: wifi_cred add \"network name\"" - " {OPEN, WPA2-PSK, WPA2-PSK-SHA256, WPA3-SAE, WPA-PSK}" - " [psk/password]" - " [bssid]" - " [{2.4GHz, 5GHz}]" - " [channel]" - " [favorite]" - " [mfp_disabled|mfp_required]" - " [timeout]"); - return -EINVAL; } static int cmd_delete_network(const struct shell *sh, size_t argc, char *argv[]) @@ -309,8 +311,35 @@ static int cmd_auto_connect(const struct shell *sh, size_t argc, char *argv[]) SHELL_STATIC_SUBCMD_SET_CREATE(sub_wifi_cred, SHELL_CMD_ARG(add, NULL, - "Add network to storage.\n", - cmd_add_network, 0, 0), + "Add network to storage.\n" + "<-s --ssid \"\">: SSID.\n" + "[-c --channel]: Channel that needs to be scanned for connection. 0:any channel.\n" + "[-b, --band] 0: any band (2:2.4GHz, 5:5GHz, 6:6GHz]\n" + "[-p, --passphrase]: Passphrase (valid only for secure SSIDs)\n" + "[-k, --key-mgmt]: Key Management type (valid only for secure SSIDs)\n" + "0:None, 1:WPA2-PSK, 2:WPA2-PSK-256, 3:SAE-HNP, 4:SAE-H2E, 5:SAE-AUTO, 6:WAPI," + " 7:EAP-TLS, 8:WEP, 9: WPA-PSK, 10: WPA-Auto-Personal, 11: DPP\n" + "12: EAP-PEAP-MSCHAPv2, 13: EAP-PEAP-GTC, 14: EAP-TTLS-MSCHAPv2,\n" + "15: EAP-PEAP-TLS, 20: SAE-EXT-KEY\n" + "[-w, --ieee-80211w]: MFP (optional: needs security type to be specified)\n" + ": 0:Disable, 1:Optional, 2:Required.\n" + "[-m, --bssid]: MAC address of the AP (BSSID).\n" + "[-t, --timeout]: Timeout for the connection attempt (in seconds).\n" + "[-a, --anon-id]: Anonymous identity for enterprise mode.\n" + "[-K, --key1-pwd for eap phase1 or --key2-pwd for eap phase2]:\n" + "Private key passwd for enterprise mode. Default no password for private key.\n" + "[-S, --wpa3-enterprise]: WPA3 enterprise mode:\n" + "Default 0: Not WPA3 enterprise mode.\n" + "1:Suite-b mode, 2:Suite-b-192-bit mode, 3:WPA3-enterprise-only mode.\n" + "[-T, --TLS-cipher]: 0:TLS-NONE, 1:TLS-ECC-P384, 2:TLS-RSA-3K.\n" + "[-V, --eap-version]: 0 or 1. Default 1: eap version 1.\n" + "[-I, --eap-id1]: Client Identity. Default no eap identity.\n" + "[-P, --eap-pwd1]: Client Password.\n" + "Default no password for eap user.\n" + "[-R, --ieee-80211r]: Use IEEE80211R fast BSS transition connect." + "[-h, --help]: Print out the help for the add network command.\n", + cmd_add_network, + 2, 12), SHELL_CMD_ARG(delete, NULL, "Delete network from storage.\n", cmd_delete_network,