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);