From c724a0a16cd865b700f14ae18bbf9bd16168293a Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 20 Aug 2019 13:10:34 +0300 Subject: [PATCH] EAP peer: Add a concept of a separate machine credential This is an initial step in adding support for configuring separate user and machine credentials. The new wpa_supplicant network profile parameters machine_identity and machine_password are similar to the existing identity and password, but explicitly assigned for the purpose of machine authentication. This commit alone does not change actual EAP peer method behavior as separate commits are needed to determine when there is an explicit request for machine authentication. Furthermore, this is only addressing the username/password credential type, i.e., additional changes following this design approach will be needed for certificate credentials. Signed-off-by: Jouni Malinen --- src/eap_peer/eap.c | 62 ++++++++++++-- src/eap_peer/eap_config.h | 34 ++++++++ src/eap_peer/eap_i.h | 1 + wpa_supplicant/config.c | 162 ++++++++++++++++++++++++++++++++--- wpa_supplicant/config_file.c | 2 + 5 files changed, 242 insertions(+), 19 deletions(-) diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index ada3c6db6..edec4586c 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -266,6 +266,7 @@ SM_STATE(EAP, INITIALIZE) sm->expected_failure = 0; sm->reauthInit = FALSE; sm->erp_seq = (u32) -1; + sm->use_machine_cred = 0; } @@ -1675,6 +1676,11 @@ struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted) identity_len = config->anonymous_identity_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity", identity, identity_len); + } else if (sm->use_machine_cred) { + identity = config->machine_identity; + identity_len = config->machine_identity_len; + wpa_hexdump_ascii(MSG_DEBUG, "EAP: using machine identity", + identity, identity_len); } else { identity = config->identity; identity_len = config->identity_len; @@ -2741,8 +2747,15 @@ struct eap_peer_config * eap_get_config(struct eap_sm *sm) const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len) { struct eap_peer_config *config = eap_get_config(sm); - if (config == NULL) + + if (!config) return NULL; + + if (sm->use_machine_cred) { + *len = config->machine_identity_len; + return config->machine_identity; + } + *len = config->identity_len; return config->identity; } @@ -2752,14 +2765,24 @@ static int eap_get_ext_password(struct eap_sm *sm, struct eap_peer_config *config) { char *name; + const u8 *password; + size_t password_len; - if (config->password == NULL) + if (sm->use_machine_cred) { + password = config->machine_password; + password_len = config->machine_password_len; + } else { + password = config->password; + password_len = config->password_len; + } + + if (!password) return -1; - name = os_zalloc(config->password_len + 1); - if (name == NULL) + name = os_zalloc(password_len + 1); + if (!name) return -1; - os_memcpy(name, config->password, config->password_len); + os_memcpy(name, password, password_len); ext_password_free(sm->ext_pw_buf); sm->ext_pw_buf = ext_password_get(sm->ext_pw, name); @@ -2778,16 +2801,25 @@ static int eap_get_ext_password(struct eap_sm *sm, const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len) { struct eap_peer_config *config = eap_get_config(sm); - if (config == NULL) + + if (!config) return NULL; - if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + if ((sm->use_machine_cred && + (config->flags & EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD)) || + (!sm->use_machine_cred && + (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD))) { if (eap_get_ext_password(sm, config) < 0) return NULL; *len = wpabuf_len(sm->ext_pw_buf); return wpabuf_head(sm->ext_pw_buf); } + if (sm->use_machine_cred) { + *len = config->machine_password_len; + return config->machine_password; + } + *len = config->password_len; return config->password; } @@ -2805,10 +2837,14 @@ const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len) const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash) { struct eap_peer_config *config = eap_get_config(sm); - if (config == NULL) + + if (!config) return NULL; - if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + if ((sm->use_machine_cred && + (config->flags & EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD)) || + (!sm->use_machine_cred && + (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD))) { if (eap_get_ext_password(sm, config) < 0) return NULL; if (hash) @@ -2817,6 +2853,14 @@ const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash) return wpabuf_head(sm->ext_pw_buf); } + if (sm->use_machine_cred) { + *len = config->machine_password_len; + if (hash) + *hash = !!(config->flags & + EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH); + return config->machine_password; + } + *len = config->password_len; if (hash) *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH); diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h index 148c9066d..c2f1ca0d6 100644 --- a/src/eap_peer/eap_config.h +++ b/src/eap_peer/eap_config.h @@ -49,6 +49,20 @@ struct eap_peer_config { u8 *imsi_identity; size_t imsi_identity_len; + /** + * machine_identity - EAP Identity for machine credential + * + * This field is used to set the machine identity or NAI for cases where + * and explicit machine credential (instead of or in addition to a user + * credential (from %identity) is needed. + */ + u8 *machine_identity; + + /** + * machine_identity_len - EAP Identity length for machine credential + */ + size_t machine_identity_len; + /** * password - Password string for EAP * @@ -72,6 +86,20 @@ struct eap_peer_config { */ size_t password_len; + /** + * machine_password - Password string for EAP machine credential + * + * This field is used when machine credential based on username/password + * is needed instead of a user credential (from %password). See + * %password for more details on the format. + */ + u8 *machine_password; + + /** + * machine_password_len - Length of machine credential password field + */ + size_t machine_password_len; + /** * ca_cert - File path to CA certificate file (PEM/DER) * @@ -751,6 +779,8 @@ struct eap_peer_config { #define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0) #define EAP_CONFIG_FLAGS_EXT_PASSWORD BIT(1) +#define EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH BIT(2) +#define EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD BIT(3) /** * flags - Network configuration flags (bitfield) * @@ -760,6 +790,10 @@ struct eap_peer_config { * instead of plaintext password * bit 1 = password is stored in external storage; the value in the * password field is the name of that external entry + * bit 2 = machine password is represented as a 16-byte NtPasswordHash + * value instead of plaintext password + * bit 3 = machine password is stored in external storage; the value in + * the password field is the name of that external entry */ u32 flags; diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h index 7c633236c..8f29d4a26 100644 --- a/src/eap_peer/eap_i.h +++ b/src/eap_peer/eap_i.h @@ -382,6 +382,7 @@ struct eap_sm { unsigned int expected_failure:1; unsigned int ext_cert_check:1; unsigned int waiting_ext_cert_check:1; + unsigned int use_machine_cred:1; struct dl_list erp_keys; /* struct eap_erp_key */ }; diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index fc1ed4f90..cde1e8d35 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -1614,7 +1614,7 @@ static int wpa_config_parse_password(const struct parse_data *data, #ifdef CONFIG_EXT_PASSWORD if (os_strncmp(value, "ext:", 4) == 0) { char *name = os_strdup(value + 4); - if (name == NULL) + if (!name) return -1; bin_clear_free(ssid->eap.password, ssid->eap.password_len); ssid->eap.password = (u8 *) name; @@ -1630,9 +1630,9 @@ static int wpa_config_parse_password(const struct parse_data *data, size_t res_len; tmp = wpa_config_parse_string(value, &res_len); - if (tmp == NULL) { - wpa_printf(MSG_ERROR, "Line %d: failed to parse " - "password.", line); + if (!tmp) { + wpa_printf(MSG_ERROR, + "Line %d: failed to parse password.", line); return -1; } wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name, @@ -1650,13 +1650,14 @@ static int wpa_config_parse_password(const struct parse_data *data, /* NtPasswordHash: hash:<32 hex digits> */ if (os_strlen(value + 5) != 2 * 16) { - wpa_printf(MSG_ERROR, "Line %d: Invalid password hash length " - "(expected 32 hex digits)", line); + wpa_printf(MSG_ERROR, + "Line %d: Invalid password hash length (expected 32 hex digits)", + line); return -1; } hash = os_malloc(16); - if (hash == NULL) + if (!hash) return -1; if (hexstr2bin(value + 5, hash, 16)) { @@ -1683,19 +1684,118 @@ static int wpa_config_parse_password(const struct parse_data *data, } +static int wpa_config_parse_machine_password(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + u8 *hash; + + if (os_strcmp(value, "NULL") == 0) { + if (!ssid->eap.machine_password) + return 1; /* Already unset */ + wpa_printf(MSG_DEBUG, + "Unset configuration string 'machine_password'"); + bin_clear_free(ssid->eap.machine_password, + ssid->eap.machine_password_len); + ssid->eap.machine_password = NULL; + ssid->eap.machine_password_len = 0; + return 0; + } + +#ifdef CONFIG_EXT_PASSWORD + if (os_strncmp(value, "ext:", 4) == 0) { + char *name = os_strdup(value + 4); + + if (!name) + return -1; + bin_clear_free(ssid->eap.machine_password, + ssid->eap.machine_password_len); + ssid->eap.machine_password = (u8 *) name; + ssid->eap.machine_password_len = os_strlen(name); + ssid->eap.flags &= ~EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH; + ssid->eap.flags |= EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD; + return 0; + } +#endif /* CONFIG_EXT_PASSWORD */ + + if (os_strncmp(value, "hash:", 5) != 0) { + char *tmp; + size_t res_len; + + tmp = wpa_config_parse_string(value, &res_len); + if (!tmp) { + wpa_printf(MSG_ERROR, + "Line %d: failed to parse machine_password.", + line); + return -1; + } + wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name, + (u8 *) tmp, res_len); + + bin_clear_free(ssid->eap.machine_password, + ssid->eap.machine_password_len); + ssid->eap.machine_password = (u8 *) tmp; + ssid->eap.machine_password_len = res_len; + ssid->eap.flags &= ~EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH; + ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD; + + return 0; + } + + + /* NtPasswordHash: hash:<32 hex digits> */ + if (os_strlen(value + 5) != 2 * 16) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid machine_password hash length (expected 32 hex digits)", + line); + return -1; + } + + hash = os_malloc(16); + if (!hash) + return -1; + + if (hexstr2bin(value + 5, hash, 16)) { + os_free(hash); + wpa_printf(MSG_ERROR, "Line %d: Invalid machine_password hash", + line); + return -1; + } + + wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16); + + if (ssid->eap.machine_password && + ssid->eap.machine_password_len == 16 && + os_memcmp(ssid->eap.machine_password, hash, 16) == 0 && + (ssid->eap.flags & EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH)) { + bin_clear_free(hash, 16); + return 1; + } + bin_clear_free(ssid->eap.machine_password, + ssid->eap.machine_password_len); + ssid->eap.machine_password = hash; + ssid->eap.machine_password_len = 16; + ssid->eap.flags |= EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH; + ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD; + + return 0; +} + + #ifndef NO_CONFIG_WRITE + static char * wpa_config_write_password(const struct parse_data *data, struct wpa_ssid *ssid) { char *buf; - if (ssid->eap.password == NULL) + if (!ssid->eap.password) return NULL; #ifdef CONFIG_EXT_PASSWORD if (ssid->eap.flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { buf = os_zalloc(4 + ssid->eap.password_len + 1); - if (buf == NULL) + if (!buf) return NULL; os_memcpy(buf, "ext:", 4); os_memcpy(buf + 4, ssid->eap.password, ssid->eap.password_len); @@ -1709,7 +1809,7 @@ static char * wpa_config_write_password(const struct parse_data *data, } buf = os_malloc(5 + 32 + 1); - if (buf == NULL) + if (!buf) return NULL; os_memcpy(buf, "hash:", 5); @@ -1717,6 +1817,44 @@ static char * wpa_config_write_password(const struct parse_data *data, return buf; } + + +static char * wpa_config_write_machine_password(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + char *buf; + + if (!ssid->eap.machine_password) + return NULL; + +#ifdef CONFIG_EXT_PASSWORD + if (ssid->eap.flags & EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD) { + buf = os_zalloc(4 + ssid->eap.machine_password_len + 1); + if (!buf) + return NULL; + os_memcpy(buf, "ext:", 4); + os_memcpy(buf + 4, ssid->eap.machine_password, + ssid->eap.machine_password_len); + return buf; + } +#endif /* CONFIG_EXT_PASSWORD */ + + if (!(ssid->eap.flags & EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH)) { + return wpa_config_write_string( + ssid->eap.machine_password, + ssid->eap.machine_password_len); + } + + buf = os_malloc(5 + 32 + 1); + if (!buf) + return NULL; + + os_memcpy(buf, "hash:", 5); + wpa_snprintf_hex(buf + 5, 32 + 1, ssid->eap.machine_password, 16); + + return buf; +} + #endif /* NO_CONFIG_WRITE */ #endif /* IEEE8021X_EAPOL */ @@ -2249,7 +2387,9 @@ static const struct parse_data ssid_fields[] = { { STR_LENe(identity) }, { STR_LENe(anonymous_identity) }, { STR_LENe(imsi_identity) }, + { STR_LENe(machine_identity) }, { FUNC_KEY(password) }, + { FUNC_KEY(machine_password) }, { STRe(ca_cert) }, { STRe(ca_path) }, { STRe(client_cert) }, @@ -2520,7 +2660,9 @@ static void eap_peer_config_free(struct eap_peer_config *eap) bin_clear_free(eap->identity, eap->identity_len); os_free(eap->anonymous_identity); os_free(eap->imsi_identity); + os_free(eap->machine_identity); bin_clear_free(eap->password, eap->password_len); + bin_clear_free(eap->machine_password, eap->machine_password_len); os_free(eap->ca_cert); os_free(eap->ca_path); os_free(eap->client_cert); diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 91d5caa3f..8d81e361d 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -774,7 +774,9 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) STR(identity); STR(anonymous_identity); STR(imsi_identity); + STR(machine_identity); STR(password); + STR(machine_password); STR(ca_cert); STR(ca_path); STR(client_cert);