diff options
Diffstat (limited to 'src/wps')
-rw-r--r-- | src/wps/.gitignore | 1 | ||||
-rw-r--r-- | src/wps/httpread.c | 7 | ||||
-rw-r--r-- | src/wps/wps.h | 21 | ||||
-rw-r--r-- | src/wps/wps_attr_parse.c | 8 | ||||
-rw-r--r-- | src/wps/wps_common.c | 68 | ||||
-rw-r--r-- | src/wps/wps_enrollee.c | 47 | ||||
-rw-r--r-- | src/wps/wps_i.h | 8 | ||||
-rw-r--r-- | src/wps/wps_registrar.c | 234 | ||||
-rw-r--r-- | src/wps/wps_upnp.c | 109 | ||||
-rw-r--r-- | src/wps/wps_upnp_event.c | 4 | ||||
-rw-r--r-- | src/wps/wps_upnp_i.h | 6 | ||||
-rw-r--r-- | src/wps/wps_upnp_ssdp.c | 45 | ||||
-rw-r--r-- | src/wps/wps_upnp_web.c | 2 |
13 files changed, 453 insertions, 107 deletions
diff --git a/src/wps/.gitignore b/src/wps/.gitignore deleted file mode 100644 index a438335..0000000 --- a/src/wps/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.d diff --git a/src/wps/httpread.c b/src/wps/httpread.c index 313b468..0d7165e 100644 --- a/src/wps/httpread.c +++ b/src/wps/httpread.c @@ -206,7 +206,8 @@ static int httpread_hdr_option_analyze( h->got_content_length = 1; return 0; } - if (word_eq(hbp, "TRANSFER_ENCODING:")) { + if (word_eq(hbp, "TRANSFER_ENCODING:") || + word_eq(hbp, "TRANSFER-ENCODING:")) { while (isgraph(*hbp)) hbp++; while (*hbp == ' ' || *hbp == '\t') @@ -214,7 +215,7 @@ static int httpread_hdr_option_analyze( /* There should (?) be no encodings of interest * other than chunked... */ - if (os_strncmp(hbp, "CHUNKED", 7)) { + if (word_eq(hbp, "CHUNKED")) { h->chunked = 1; h->in_chunk_data = 0; /* ignore possible ;<parameters> */ @@ -513,6 +514,8 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) * consists of chunks each with a header, ending with * an ending header. */ + if (nread == 0) + goto get_more; if (!h->got_body) { /* Here to get (more of) body */ /* ensure we have enough room for worst case for body diff --git a/src/wps/wps.h b/src/wps/wps.h index e0f2b2d..faf32c4 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -42,7 +42,7 @@ struct upnp_wps_device_sm; * @key_idx: Key index * @key: Key * @key_len: Key length in octets - * @mac_addr: MAC address of the peer + * @mac_addr: MAC address of the Credential receiver * @cred_attr: Unparsed Credential attribute data (used only in cred_cb()); * this may be %NULL, if not used * @cred_attr_len: Length of cred_attr in octets @@ -266,6 +266,11 @@ struct wps_registrar_config { * to be set with a suitable Credential and skip_cred_build being used. */ int disable_auto_conf; + + /** + * static_wep_only - Whether the BSS supports only static WEP + */ + int static_wep_only; }; @@ -291,7 +296,17 @@ enum wps_event { /** * WPS_EV_PWD_AUTH_FAIL - Password authentication failed */ - WPS_EV_PWD_AUTH_FAIL + WPS_EV_PWD_AUTH_FAIL, + + /** + * WPS_EV_PBC_OVERLAP - PBC session overlap detected + */ + WPS_EV_PBC_OVERLAP, + + /** + * WPS_EV_PBC_TIMEOUT - PBC walktime expired before protocol run start + */ + WPS_EV_PBC_TIMEOUT }; /** @@ -500,7 +515,7 @@ wps_registrar_init(struct wps_context *wps, const struct wps_registrar_config *cfg); void wps_registrar_deinit(struct wps_registrar *reg); int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, - const u8 *pin, size_t pin_len); + const u8 *pin, size_t pin_len, int timeout); int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid); int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid); int wps_registrar_button_pushed(struct wps_registrar *reg); diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c index 25ff251..f50ae39 100644 --- a/src/wps/wps_attr_parse.c +++ b/src/wps/wps_attr_parse.c @@ -381,6 +381,14 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type, attr->eap_identity = pos; attr->eap_identity_len = len; break; + case ATTR_AP_SETUP_LOCKED: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked " + "length %u", len); + return -1; + } + attr->ap_setup_locked = pos; + break; default: wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x " "len=%u", type, len); diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c index 48af303..4b45f00 100644 --- a/src/wps/wps_common.c +++ b/src/wps/wps_common.c @@ -128,56 +128,6 @@ int wps_derive_keys(struct wps_data *wps) } -int wps_derive_mgmt_keys(struct wps_data *wps) -{ - u8 nonces[2 * WPS_NONCE_LEN]; - u8 keys[WPS_MGMTAUTHKEY_LEN + WPS_MGMTENCKEY_LEN]; - u8 hash[SHA256_MAC_LEN]; - const u8 *addr[2]; - size_t len[2]; - const char *auth_label = "WFA-WLAN-Management-MgmtAuthKey"; - const char *enc_label = "WFA-WLAN-Management-MgmtEncKey"; - - /* MgmtAuthKey || MgmtEncKey = - * kdf(EMSK, N1 || N2 || "WFA-WLAN-Management-Keys", 384) */ - os_memcpy(nonces, wps->nonce_e, WPS_NONCE_LEN); - os_memcpy(nonces + WPS_NONCE_LEN, wps->nonce_r, WPS_NONCE_LEN); - wps_kdf(wps->emsk, nonces, sizeof(nonces), "WFA-WLAN-Management-Keys", - keys, sizeof(keys)); - os_memcpy(wps->mgmt_auth_key, keys, WPS_MGMTAUTHKEY_LEN); - os_memcpy(wps->mgmt_enc_key, keys + WPS_MGMTAUTHKEY_LEN, - WPS_MGMTENCKEY_LEN); - - addr[0] = nonces; - len[0] = sizeof(nonces); - - /* MgmtEncKeyID = first 128 bits of - * SHA-256(N1 || N2 || "WFA-WLAN-Management-MgmtAuthKey") */ - addr[1] = (const u8 *) auth_label; - len[1] = os_strlen(auth_label); - sha256_vector(2, addr, len, hash); - os_memcpy(wps->mgmt_auth_key_id, hash, WPS_MGMT_KEY_ID_LEN); - - /* MgmtEncKeyID = first 128 bits of - * SHA-256(N1 || N2 || "WFA-WLAN-Management-MgmtEncKey") */ - addr[1] = (const u8 *) enc_label; - len[1] = os_strlen(enc_label); - sha256_vector(2, addr, len, hash); - os_memcpy(wps->mgmt_enc_key_id, hash, WPS_MGMT_KEY_ID_LEN); - - wpa_hexdump_key(MSG_DEBUG, "WPS: MgmtAuthKey", - wps->mgmt_auth_key, WPS_MGMTAUTHKEY_LEN); - wpa_hexdump(MSG_DEBUG, "WPS: MgmtAuthKeyID", - wps->mgmt_auth_key_id, WPS_MGMT_KEY_ID_LEN); - wpa_hexdump_key(MSG_DEBUG, "WPS: MgmtEncKey", - wps->mgmt_enc_key, WPS_MGMTENCKEY_LEN); - wpa_hexdump(MSG_DEBUG, "WPS: MgmtEncKeyID", - wps->mgmt_enc_key_id, WPS_MGMT_KEY_ID_LEN); - - return 0; -} - - void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd, size_t dev_passwd_len) { @@ -335,3 +285,21 @@ void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part) data.pwd_auth_fail.part = part; wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, &data); } + + +void wps_pbc_overlap_event(struct wps_context *wps) +{ + if (wps->event_cb == NULL) + return; + + wps->event_cb(wps->cb_ctx, WPS_EV_PBC_OVERLAP, NULL); +} + + +void wps_pbc_timeout_event(struct wps_context *wps) +{ + if (wps->event_cb == NULL) + return; + + wps->event_cb(wps->cb_ctx, WPS_EV_PBC_TIMEOUT, NULL); +} diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c index 179f7db..5cb3e1e 100644 --- a/src/wps/wps_enrollee.c +++ b/src/wps/wps_enrollee.c @@ -41,7 +41,7 @@ static int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg) state); wpabuf_put_be16(msg, ATTR_WPS_STATE); wpabuf_put_be16(msg, 1); - wpabuf_put_u8(msg, WPS_STATE_NOT_CONFIGURED); + wpabuf_put_u8(msg, state); return 0; } @@ -521,10 +521,6 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk, if (wps_derive_keys(wps) < 0) return -1; - if (wps->request_type == WPS_REQ_WLAN_MANAGER_REGISTRAR && - wps_derive_mgmt_keys(wps) < 0) - return -1; - return 0; } @@ -650,6 +646,21 @@ static int wps_process_cred_e(struct wps_data *wps, const u8 *cred, wps_process_cred(&attr, &wps->cred)) return -1; + if (os_memcmp(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) != + 0) { + wpa_printf(MSG_DEBUG, "WPS: MAC Address in the Credential (" + MACSTR ") does not match with own address (" MACSTR + ")", MAC2STR(wps->cred.mac_addr), + MAC2STR(wps->wps->dev.mac_addr)); + /* + * In theory, this could be consider fatal error, but there are + * number of deployed implementations using other address here + * due to unclarity in the specification. For interoperability + * reasons, allow this to be processed since we do not really + * use the MAC Address information for anything. + */ + } + if (wps->wps->cred_cb) { wps->cred.cred_attr = cred - 4; wps->cred.cred_attr_len = cred_len + 4; @@ -700,6 +711,21 @@ static int wps_process_ap_settings_e(struct wps_data *wps, wpa_printf(MSG_INFO, "WPS: Received new AP configuration from " "Registrar"); + if (os_memcmp(cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) != + 0) { + wpa_printf(MSG_DEBUG, "WPS: MAC Address in the AP Settings (" + MACSTR ") does not match with own address (" MACSTR + ")", MAC2STR(cred.mac_addr), + MAC2STR(wps->wps->dev.mac_addr)); + /* + * In theory, this could be consider fatal error, but there are + * number of deployed implementations using other address here + * due to unclarity in the specification. For interoperability + * reasons, allow this to be processed since we do not really + * use the MAC Address information for anything. + */ + } + if (wps->wps->cred_cb) { cred.cred_attr = wpabuf_head(attrs); cred.cred_attr_len = wpabuf_len(attrs); @@ -1159,6 +1185,17 @@ enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps, "op_code=%d)", (unsigned long) wpabuf_len(msg), op_code); + if (op_code == WSC_UPnP) { + /* Determine the OpCode based on message type attribute */ + struct wps_parse_attr attr; + if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) { + if (*attr.msg_type == WPS_WSC_ACK) + op_code = WSC_ACK; + else if (*attr.msg_type == WPS_WSC_NACK) + op_code = WSC_NACK; + } + } + switch (op_code) { case WSC_MSG: case WSC_UPnP: diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h index 85adf28..3317a2c 100644 --- a/src/wps/wps_i.h +++ b/src/wps/wps_i.h @@ -62,10 +62,6 @@ struct wps_data { u8 authkey[WPS_AUTHKEY_LEN]; u8 keywrapkey[WPS_KEYWRAPKEY_LEN]; u8 emsk[WPS_EMSK_LEN]; - u8 mgmt_auth_key[WPS_MGMTAUTHKEY_LEN]; - u8 mgmt_auth_key_id[WPS_MGMT_KEY_ID_LEN]; - u8 mgmt_enc_key[WPS_MGMTENCKEY_LEN]; - u8 mgmt_enc_key_id[WPS_MGMT_KEY_ID_LEN]; struct wpabuf *last_msg; @@ -146,6 +142,7 @@ struct wps_parse_attr { const u8 *selected_registrar; /* 1 octet (Bool) */ const u8 *request_type; /* 1 octet */ const u8 *response_type; /* 1 octet */ + const u8 *ap_setup_locked; /* 1 octet */ /* variable length fields */ const u8 *manufacturer; @@ -182,7 +179,6 @@ struct wps_parse_attr { void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len, const char *label, u8 *res, size_t res_len); int wps_derive_keys(struct wps_data *wps); -int wps_derive_mgmt_keys(struct wps_data *wps); void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd, size_t dev_passwd_len); struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr, @@ -190,6 +186,8 @@ struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr, void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg); void wps_success_event(struct wps_context *wps); void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part); +void wps_pbc_overlap_event(struct wps_context *wps); +void wps_pbc_timeout_event(struct wps_context *wps); /* wps_attr_parse.c */ int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index f7eebdd..f34c9e9 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -23,6 +23,7 @@ #include "wps_dev_attr.h" #include "wps_upnp.h" +#define WPS_WORKAROUNDS struct wps_uuid_pin { struct wps_uuid_pin *next; @@ -30,7 +31,10 @@ struct wps_uuid_pin { int wildcard_uuid; u8 *pin; size_t pin_len; - int locked; +#define PIN_LOCKED BIT(0) +#define PIN_EXPIRES BIT(1) + int flags; + struct os_time expiration; }; @@ -98,6 +102,9 @@ struct wps_registrar { int disable_auto_conf; int sel_reg_dev_password_id_override; int sel_reg_config_methods_override; + int static_wep_only; + + int force_pbc_overlap; }; @@ -376,6 +383,7 @@ wps_registrar_init(struct wps_context *wps, reg->disable_auto_conf = cfg->disable_auto_conf; reg->sel_reg_dev_password_id_override = -1; reg->sel_reg_config_methods_override = -1; + reg->static_wep_only = cfg->static_wep_only; if (wps_set_ie(reg)) { wps_registrar_deinit(reg); @@ -409,10 +417,11 @@ void wps_registrar_deinit(struct wps_registrar *reg) * @uuid: UUID-E or %NULL for wildcard (any UUID) * @pin: PIN (Device Password) * @pin_len: Length of pin in octets + * @timeout: Time (in seconds) when the PIN will be invalidated; 0 = no timeout * Returns: 0 on success, -1 on failure */ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, - const u8 *pin, size_t pin_len) + const u8 *pin, size_t pin_len, int timeout) { struct wps_uuid_pin *p; @@ -431,20 +440,59 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, os_memcpy(p->pin, pin, pin_len); p->pin_len = pin_len; + if (timeout) { + p->flags |= PIN_EXPIRES; + os_get_time(&p->expiration); + p->expiration.sec += timeout; + } + p->next = reg->pins; reg->pins = p; - wpa_printf(MSG_DEBUG, "WPS: A new PIN configured"); + wpa_printf(MSG_DEBUG, "WPS: A new PIN configured (timeout=%d)", + timeout); wpa_hexdump(MSG_DEBUG, "WPS: UUID", uuid, WPS_UUID_LEN); wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len); reg->selected_registrar = 1; reg->pbc = 0; wps_set_ie(reg); + eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); + eloop_register_timeout(WPS_PBC_WALK_TIME, 0, + wps_registrar_set_selected_timeout, + reg, NULL); return 0; } +static void wps_registrar_expire_pins(struct wps_registrar *reg) +{ + struct wps_uuid_pin *pin, *prev, *del; + struct os_time now; + + os_get_time(&now); + prev = NULL; + pin = reg->pins; + while (pin) { + if ((pin->flags & PIN_EXPIRES) && + os_time_before(&pin->expiration, &now)) { + if (prev == NULL) + reg->pins = pin->next; + else + prev->next = pin->next; + del = pin; + pin = pin->next; + wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID", + del->uuid, WPS_UUID_LEN); + wps_free_pin(del); + continue; + } + prev = pin; + pin = pin->next; + } +} + + /** * wps_registrar_invalidate_pin - Invalidate a PIN for a specific UUID-E * @reg: Registrar data from wps_registrar_init() @@ -481,6 +529,8 @@ static const u8 * wps_registrar_get_pin(struct wps_registrar *reg, { struct wps_uuid_pin *pin; + wps_registrar_expire_pins(reg); + pin = reg->pins; while (pin) { if (!pin->wildcard_uuid && @@ -512,13 +562,13 @@ static const u8 * wps_registrar_get_pin(struct wps_registrar *reg, * Lock the PIN to avoid attacks based on concurrent re-use of the PIN * that could otherwise avoid PIN invalidations. */ - if (pin->locked) { + if (pin->flags & PIN_LOCKED) { wpa_printf(MSG_DEBUG, "WPS: Selected PIN locked - do not " "allow concurrent re-use"); return NULL; } *pin_len = pin->pin_len; - pin->locked = 1; + pin->flags |= PIN_LOCKED; return pin->pin; } @@ -545,7 +595,7 @@ int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid) "wildcard PIN"); return wps_registrar_invalidate_pin(reg, uuid); } - pin->locked = 0; + pin->flags &= ~PIN_LOCKED; return 0; } pin = pin->next; @@ -568,6 +618,7 @@ static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx) struct wps_registrar *reg = eloop_ctx; wpa_printf(MSG_DEBUG, "WPS: PBC timed out - disable PBC mode"); + wps_pbc_timeout_event(reg->wps); wps_registrar_stop_pbc(reg); } @@ -586,9 +637,11 @@ int wps_registrar_button_pushed(struct wps_registrar *reg) if (wps_registrar_pbc_overlap(reg, NULL, NULL)) { wpa_printf(MSG_DEBUG, "WPS: PBC overlap - do not start PBC " "mode"); + wps_pbc_overlap_event(reg->wps); return -1; } wpa_printf(MSG_DEBUG, "WPS: Button pushed - PBC mode started"); + reg->force_pbc_overlap = 0; reg->selected_registrar = 1; reg->pbc = 1; wps_set_ie(reg); @@ -607,6 +660,14 @@ static void wps_registrar_pbc_completed(struct wps_registrar *reg) wps_registrar_stop_pbc(reg); } +static void wps_registrar_pin_completed(struct wps_registrar *reg) +{ + wpa_printf(MSG_DEBUG, "WPS: PIN completed using internal Registrar"); + eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); + reg->selected_registrar = 0; + wps_set_ie(reg); +} + /** * wps_registrar_probe_req_rx - Notify Registrar of Probe Request @@ -648,8 +709,18 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, wpa_printf(MSG_DEBUG, "WPS: Probe Request for PBC received from " MACSTR, MAC2STR(addr)); + if (attr.uuid_e == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Probe Request WPS IE: No " + "UUID-E included"); + return; + } wps_registrar_add_pbc_session(reg, addr, attr.uuid_e); + if (wps_registrar_pbc_overlap(reg, addr, attr.uuid_e)) { + wpa_printf(MSG_DEBUG, "WPS: PBC session overlap detected"); + reg->force_pbc_overlap = 1; + wps_pbc_overlap_event(reg->wps); + } } @@ -777,6 +848,28 @@ static int wps_set_ie(struct wps_registrar *reg) return -1; } + if (reg->static_wep_only) { + /* + * Windows XP and Vista clients can get confused about + * EAP-Identity/Request when they probe the network with + * EAPOL-Start. In such a case, they may assume the network is + * using IEEE 802.1X and prompt user for a certificate while + * the correct (non-WPS) behavior would be to ask for the + * static WEP key. As a workaround, use Microsoft Provisioning + * IE to advertise that legacy 802.1X is not supported. + */ + const u8 ms_wps[7] = { + WLAN_EID_VENDOR_SPECIFIC, 5, + /* Microsoft Provisioning IE (00:50:f2:5) */ + 0x00, 0x50, 0xf2, 5, + 0x00 /* no legacy 802.1X or MS WPS */ + }; + wpa_printf(MSG_DEBUG, "WPS: Add Microsoft Provisioning IE " + "into Beacon/Probe Response frames"); + wpabuf_put_data(beacon, ms_wps, sizeof(ms_wps)); + wpabuf_put_data(probe, ms_wps, sizeof(ms_wps)); + } + ret = wps_cb_set_ie(reg, beacon, probe); wpabuf_free(beacon); wpabuf_free(probe); @@ -1033,8 +1126,10 @@ static int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) } } wps->cred.encr_type = wps->encr_type; - /* Set MAC address in the Credential to be the AP's address (BSSID) */ - os_memcpy(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN); + /* + * Set MAC address in the Credential to be the Enrollee's MAC address + */ + os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN); if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->wps->ap && !wps->wps->registrar->disable_auto_conf) { @@ -1159,14 +1254,15 @@ static struct wpabuf * wps_build_m2(struct wps_data *wps) static struct wpabuf * wps_build_m2d(struct wps_data *wps) { struct wpabuf *msg; - u16 err = WPS_CFG_NO_ERROR; + u16 err = wps->config_error; wpa_printf(MSG_DEBUG, "WPS: Building Message M2D"); msg = wpabuf_alloc(1000); if (msg == NULL) return NULL; - if (wps->wps->ap && wps->wps->ap_setup_locked) + if (wps->wps->ap && wps->wps->ap_setup_locked && + err == WPS_CFG_NO_ERROR) err = WPS_CFG_SETUP_LOCKED; if (wps_build_version(msg) || @@ -1368,8 +1464,18 @@ struct wpabuf * wps_registrar_get_msg(struct wps_data *wps, else wps->wps->upnp_msgs = NULL; msg = p->msg; + switch (p->type) { + case WPS_WSC_ACK: + *op_code = WSC_ACK; + break; + case WPS_WSC_NACK: + *op_code = WSC_NACK; + break; + default: + *op_code = WSC_MSG; + break; + } os_free(p); - *op_code = WSC_MSG; if (wps->ext_reg == 0) wps->ext_reg = 1; return msg; @@ -1654,7 +1760,21 @@ static int wps_process_auth_type_flags(struct wps_data *wps, const u8 *auth) wpa_printf(MSG_DEBUG, "WPS: No match in supported " "authentication types (own 0x%x Enrollee 0x%x)", wps->wps->auth_types, auth_types); +#ifdef WPS_WORKAROUNDS + /* + * Some deployed implementations seem to advertise incorrect + * information in this attribute. For example, Linksys WRT350N + * seems to have a byteorder bug that breaks this negotiation. + * In order to interoperate with existing implementations, + * assume that the Enrollee supports everything we do. + */ + wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee " + "does not advertise supported authentication types " + "correctly"); + wps->auth_type = wps->wps->auth_types; +#else /* WPS_WORKAROUNDS */ return -1; +#endif /* WPS_WORKAROUNDS */ } return 0; @@ -1678,8 +1798,23 @@ static int wps_process_encr_type_flags(struct wps_data *wps, const u8 *encr) wps->encr_type = wps->wps->encr_types & encr_types; if (wps->encr_type == 0) { wpa_printf(MSG_DEBUG, "WPS: No match in supported " - "encryption types"); + "encryption types (own 0x%x Enrollee 0x%x)", + wps->wps->encr_types, encr_types); +#ifdef WPS_WORKAROUNDS + /* + * Some deployed implementations seem to advertise incorrect + * information in this attribute. For example, Linksys WRT350N + * seems to have a byteorder bug that breaks this negotiation. + * In order to interoperate with existing implementations, + * assume that the Enrollee supports everything we do. + */ + wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee " + "does not advertise supported encryption types " + "correctly"); + wps->encr_type = wps->wps->encr_types; +#else /* WPS_WORKAROUNDS */ return -1; +#endif /* WPS_WORKAROUNDS */ } return 0; @@ -1806,11 +1941,15 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, } if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) { - if (wps_registrar_pbc_overlap(wps->wps->registrar, + if (wps->wps->registrar->force_pbc_overlap || + wps_registrar_pbc_overlap(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e)) { wpa_printf(MSG_DEBUG, "WPS: PBC overlap - deny PBC " "negotiation"); wps->state = SEND_M2D; + wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED; + wps_pbc_overlap_event(wps->wps); + wps->wps->registrar->force_pbc_overlap = 1; return WPS_CONTINUE; } wps_registrar_add_pbc_session(wps->wps->registrar, @@ -1836,6 +1975,14 @@ static enum wps_process_res wps_process_m3(struct wps_data *wps, return WPS_CONTINUE; } + if (wps->pbc && wps->wps->registrar->force_pbc_overlap) { + wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC " + "session overlap"); + wps->state = SEND_WSC_NACK; + wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED; + return WPS_CONTINUE; + } + if (wps_process_registrar_nonce(wps, attr->registrar_nonce) || wps_process_authenticator(wps, attr->authenticator, msg) || wps_process_e_hash1(wps, attr->e_hash1) || @@ -1865,6 +2012,14 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps, return WPS_CONTINUE; } + if (wps->pbc && wps->wps->registrar->force_pbc_overlap) { + wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC " + "session overlap"); + wps->state = SEND_WSC_NACK; + wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED; + return WPS_CONTINUE; + } + if (wps_process_registrar_nonce(wps, attr->registrar_nonce) || wps_process_authenticator(wps, attr->authenticator, msg)) { wps->state = SEND_WSC_NACK; @@ -1896,6 +2051,28 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps, } +static void wps_sta_cred_cb(struct wps_data *wps) +{ + /* + * Update credential to only include a single authentication and + * encryption type in case the AP configuration includes more than one + * option. + */ + if (wps->cred.auth_type & WPS_AUTH_WPA2PSK) + wps->cred.auth_type = WPS_AUTH_WPA2PSK; + else if (wps->cred.auth_type & WPS_AUTH_WPAPSK) + wps->cred.auth_type = WPS_AUTH_WPAPSK; + if (wps->cred.encr_type & WPS_ENCR_AES) + wps->cred.encr_type = WPS_ENCR_AES; + else if (wps->cred.encr_type & WPS_ENCR_TKIP) + wps->cred.encr_type = WPS_ENCR_TKIP; + wpa_printf(MSG_DEBUG, "WPS: Update local configuration based on the " + "AP configuration"); + if (wps->wps->cred_cb) + wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred); +} + + static int wps_process_ap_settings_r(struct wps_data *wps, struct wps_parse_attr *attr) { @@ -1908,12 +2085,21 @@ static int wps_process_ap_settings_r(struct wps_data *wps, wpa_printf(MSG_INFO, "WPS: Received old AP configuration from AP"); +#if 0 /* * TODO: Provide access to AP settings and allow changes before sending * out M8. For now, just copy the settings unchanged into M8. */ return 0; +#else + /* + * For now, use the AP PIN only to receive the current AP settings, + * not to reconfigure the AP. + */ + wps_sta_cred_cb(wps); + return 1; +#endif } @@ -1933,6 +2119,14 @@ static enum wps_process_res wps_process_m7(struct wps_data *wps, return WPS_CONTINUE; } + if (wps->pbc && wps->wps->registrar->force_pbc_overlap) { + wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC " + "session overlap"); + wps->state = SEND_WSC_NACK; + wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED; + return WPS_CONTINUE; + } + if (wps_process_registrar_nonce(wps, attr->registrar_nonce) || wps_process_authenticator(wps, attr->authenticator, msg)) { wps->state = SEND_WSC_NACK; @@ -2281,12 +2475,8 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, wps->new_psk = NULL; } - if (!wps->wps->ap) { - wpa_printf(MSG_DEBUG, "WPS: Update local configuration based " - "on the modified AP configuration"); - if (wps->wps->cred_cb) - wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred); - } + if (!wps->wps->ap) + wps_sta_cred_cb(wps); if (wps->new_psk) { if (wps_cb_new_psk(wps->wps->registrar, wps->mac_addr_e, @@ -2304,6 +2494,8 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, wps_registrar_remove_pbc_session(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e); wps_registrar_pbc_completed(wps->wps->registrar); + } else { + wps_registrar_pin_completed(wps->wps->registrar); } wps_success_event(wps->wps); @@ -2333,7 +2525,8 @@ enum wps_process_res wps_registrar_process_msg(struct wps_data *wps, wps_registrar_free_pending_m2(wps->wps); if (wps->wps->wps_upnp && wps->ext_reg && wps->wps->upnp_msgs == NULL && - (op_code == WSC_MSG || op_code == WSC_Done)) { + (op_code == WSC_MSG || op_code == WSC_Done || op_code == WSC_NACK)) + { struct wps_parse_attr attr; int type; if (wps_parse_msg(msg, &attr) < 0 || attr.msg_type == NULL) @@ -2401,7 +2594,6 @@ static void wps_registrar_set_selected_timeout(void *eloop_ctx, * wps_registrar_set_selected_registrar - Notification of SetSelectedRegistrar * @reg: Registrar data from wps_registrar_init() * @msg: Received message from SetSelectedRegistrar - * @msg_len: Length of msg in octets * Returns: 0 on success, -1 on failure * * This function is called when an AP receives a SetSelectedRegistrar UPnP diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c index 6d2de0e..4c6aac2 100644 --- a/src/wps/wps_upnp.c +++ b/src/wps/wps_upnp.c @@ -47,9 +47,6 @@ * -- Needs renaming with module prefix to avoid polluting the debugger * namespace and causing possible collisions with other static fncs * and structure declarations when using the debugger. - * -- Just what should be in the first event message sent after subscription - * for the WLANEvent field? If i pass it empty, Vista replies with OK - * but apparently barfs on the message. * -- The http error code generation is pretty bogus, hopefully noone cares. * * Author: Ted Merrill, Atheros Communications, based upon earlier work @@ -641,6 +638,27 @@ struct subscription * subscription_find(struct upnp_wps_device_sm *sm, } +static struct wpabuf * build_fake_wsc_ack(void) +{ + struct wpabuf *msg = wpabuf_alloc(100); + if (msg == NULL) + return NULL; + wpabuf_put_u8(msg, UPNP_WPS_WLANEVENT_TYPE_EAP); + wpabuf_put_str(msg, "00:00:00:00:00:00"); + wps_build_version(msg); + wps_build_msg_type(msg, WPS_WSC_ACK); + /* Enrollee Nonce */ + wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE); + wpabuf_put_be16(msg, WPS_NONCE_LEN); + wpabuf_put(msg, WPS_NONCE_LEN); + /* Registrar Nonce */ + wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE); + wpabuf_put_be16(msg, WPS_NONCE_LEN); + wpabuf_put(msg, WPS_NONCE_LEN); + return msg; +} + + /* subscription_first_event -- send format/queue event that is automatically * sent on a new subscription. */ @@ -665,6 +683,28 @@ static int subscription_first_event(struct subscription *s) const char *tail = "</e:propertyset>\n"; char txt[10]; + if (s->sm->wlanevent == NULL) { + /* + * There has been no events before the subscription. However, + * UPnP device architecture specification requires all the + * evented variables to be included, so generate a dummy event + * for this particular case using a WSC_ACK and all-zeros + * nonces. The ER (UPnP control point) will ignore this, but at + * least it will learn that WLANEvent variable will be used in + * event notifications in the future. + */ + struct wpabuf *msg; + wpa_printf(MSG_DEBUG, "WPS UPnP: Use a fake WSC_ACK as the " + "initial WLANEvent"); + msg = build_fake_wsc_ack(); + if (msg) { + s->sm->wlanevent = (char *) + base64_encode(wpabuf_head(msg), + wpabuf_len(msg), NULL); + wpabuf_free(msg); + } + } + wlan_event = s->sm->wlanevent; if (wlan_event == NULL || *wlan_event == '\0') { wpa_printf(MSG_DEBUG, "WPS UPnP: WLANEvent not known for " @@ -694,13 +734,13 @@ static int subscription_first_event(struct subscription *s) /** - * subscription_start - Rremember a UPnP control point to send events to. + * subscription_start - Remember a UPnP control point to send events to. * @sm: WPS UPnP state machine from upnp_wps_device_init() - * @callback_urls: malloc' mem given to the subscription + * @callback_urls: Callback URLs * Returns: %NULL on error, or pointer to new subscription structure. */ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, - char *callback_urls) + const char *callback_urls) { struct subscription *s; time_t now = time(NULL); @@ -740,7 +780,6 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, } wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s", s, callback_urls); - os_free(callback_urls); /* Schedule sending this */ event_send_all_later(sm); return s; @@ -832,6 +871,50 @@ fail: } +#ifdef __FreeBSD__ +#include <sys/sysctl.h> +#include <net/route.h> +#include <net/if_dl.h> + +static int eth_get(const char *device, u8 ea[ETH_ALEN]) +{ + struct if_msghdr *ifm; + struct sockaddr_dl *sdl; + u_char *p, *buf; + size_t len; + int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 }; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) + return -1; + if ((buf = os_malloc(len)) == NULL) + return -1; + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { + os_free(buf); + return -1; + } + for (p = buf; p < buf + len; p += ifm->ifm_msglen) { + ifm = (struct if_msghdr *)p; + sdl = (struct sockaddr_dl *)(ifm + 1); + if (ifm->ifm_type != RTM_IFINFO || + (ifm->ifm_addrs & RTA_IFP) == 0) + continue; + if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 || + os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0) + continue; + os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen); + break; + } + os_free(buf); + + if (p >= buf + len) { + errno = ESRCH; + return -1; + } + return 0; +} +#endif /* __FreeBSD__ */ + + /** * get_netif_info - Get hw and IP addresses for network device * @net_if: Selected network interface name @@ -870,6 +953,7 @@ static int get_netif_info(const char *net_if, unsigned *ip_addr, in_addr.s_addr = *ip_addr; os_snprintf(*ip_addr_text, 16, "%s", inet_ntoa(in_addr)); +#ifdef __linux__ os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name)); if (ioctl(sock, SIOCGIFHWADDR, &req) < 0) { wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFHWADDR failed: " @@ -877,6 +961,14 @@ static int get_netif_info(const char *net_if, unsigned *ip_addr, goto fail; } os_memcpy(mac, req.ifr_addr.sa_data, 6); +#elif defined(__FreeBSD__) + if (eth_get(net_if, mac) < 0) { + wpa_printf(MSG_ERROR, "WPS UPnP: Failed to get MAC address"); + goto fail; + } +#else +#error MAC address fetch not implemented +#endif os_snprintf(*mac_addr_text, 18, MACSTR, MAC2STR(req.ifr_addr.sa_data)); close(sock); @@ -914,8 +1006,7 @@ void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) subscription_destroy(s); } - advertisement_state_machine_stop(sm); - /* TODO: send byebye notifications */ + advertisement_state_machine_stop(sm, 1); event_send_stop_all(sm); os_free(sm->wlanevent); diff --git a/src/wps/wps_upnp_event.c b/src/wps/wps_upnp_event.c index f278f35..4122a87 100644 --- a/src/wps/wps_upnp_event.c +++ b/src/wps/wps_upnp_event.c @@ -89,7 +89,7 @@ static void event_clean(struct wps_event_ *e) /* event_delete -- delete single unqueued event * (be sure to dequeue first if need be) */ -void event_delete(struct wps_event_ *e) +static void event_delete(struct wps_event_ *e) { event_clean(e); wpabuf_free(e->data); @@ -432,7 +432,7 @@ static int event_send_start(struct subscription *s) /* event_send_all_later_handler -- actually send events as needed */ -void event_send_all_later_handler(void *eloop_data, void *user_ctx) +static void event_send_all_later_handler(void *eloop_data, void *user_ctx) { struct upnp_wps_device_sm *sm = user_ctx; struct subscription *s; diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h index d4b6569..ba4ec20 100644 --- a/src/wps/wps_upnp_i.h +++ b/src/wps/wps_upnp_i.h @@ -160,7 +160,7 @@ struct upnp_wps_device_sm { /* wps_upnp.c */ void format_date(struct wpabuf *buf); struct subscription * subscription_start(struct upnp_wps_device_sm *sm, - char *callback_urls); + const char *callback_urls); struct subscription * subscription_renew(struct upnp_wps_device_sm *sm, const u8 uuid[UUID_LEN]); void subscription_unlink(struct subscription *s); @@ -172,7 +172,8 @@ int send_wpabuf(int fd, struct wpabuf *buf); /* wps_upnp_ssdp.c */ void msearchreply_state_machine_stop(struct advertisement_state_machine *a); int advertisement_state_machine_start(struct upnp_wps_device_sm *sm); -void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm); +void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm, + int send_byebye); void ssdp_listener_stop(struct upnp_wps_device_sm *sm); int ssdp_listener_start(struct upnp_wps_device_sm *sm); int add_ssdp_network(char *net_if); @@ -185,7 +186,6 @@ void web_listener_stop(struct upnp_wps_device_sm *sm); /* wps_upnp_event.c */ int event_add(struct subscription *s, const struct wpabuf *data); -void event_delete(struct wps_event_ *e); void event_delete_all(struct subscription *s); void event_send_all_later(struct upnp_wps_device_sm *sm); void event_send_stop_all(struct upnp_wps_device_sm *sm); diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c index 6a94c59..c1dc99d 100644 --- a/src/wps/wps_upnp_ssdp.c +++ b/src/wps/wps_upnp_ssdp.c @@ -229,10 +229,41 @@ static void advertisement_state_machine_handler(void *eloop_data, /** * advertisement_state_machine_stop - Stop SSDP advertisements * @sm: WPS UPnP state machine from upnp_wps_device_init() + * @send_byebye: Send byebye advertisement messages immediately */ -void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm) +void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm, + int send_byebye) { + struct advertisement_state_machine *a = &sm->advertisement; + int islast = 0; + struct wpabuf *msg; + struct sockaddr_in dest; + eloop_cancel_timeout(advertisement_state_machine_handler, NULL, sm); + if (!send_byebye || sm->multicast_sd < 0) + return; + + a->type = ADVERTISE_DOWN; + a->state = 0; + a->sm = sm; + + os_memset(&dest, 0, sizeof(dest)); + dest.sin_family = AF_INET; + dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS); + dest.sin_port = htons(UPNP_MULTICAST_PORT); + + while (!islast) { + msg = next_advertisement(a, &islast); + if (msg == NULL) + break; + if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), + 0, (struct sockaddr *) &dest, sizeof(dest)) < 0) { + wpa_printf(MSG_INFO, "WPS UPnP: Advertisement sendto " + "failed: %d (%s)", errno, strerror(errno)); + } + wpabuf_free(msg); + a->state++; + } } @@ -318,7 +349,7 @@ int advertisement_state_machine_start(struct upnp_wps_device_sm *sm) struct advertisement_state_machine *a = &sm->advertisement; int next_timeout_msec; - advertisement_state_machine_stop(sm); + advertisement_state_machine_stop(sm, 0); /* * Start out advertising down, this automatically switches @@ -461,7 +492,7 @@ static void msearchreply_state_machine_start(struct upnp_wps_device_sm *sm, a->type = MSEARCH_REPLY; a->state = 0; a->sm = sm; - os_memcpy(&a->client, client, sizeof(client)); + os_memcpy(&a->client, client, sizeof(*client)); /* Wait time depending on MX value */ next_timeout_msec = (1000 * mx * (os_random() & 0xFF)) >> 8; next_timeout_sec = next_timeout_msec / 1000; @@ -784,6 +815,7 @@ fail: */ int add_ssdp_network(char *net_if) { +#ifdef __linux__ int ret = -1; int sock = -1; struct rtentry rt; @@ -798,11 +830,11 @@ int add_ssdp_network(char *net_if) goto fail; rt.rt_dev = net_if; - sin = (struct sockaddr_in *) &rt.rt_dst; + sin = aliasing_hide_typecast(&rt.rt_dst, struct sockaddr_in); sin->sin_family = AF_INET; sin->sin_port = 0; sin->sin_addr.s_addr = inet_addr(SSDP_TARGET); - sin = (struct sockaddr_in *) &rt.rt_genmask; + sin = aliasing_hide_typecast(&rt.rt_genmask, struct sockaddr_in); sin->sin_family = AF_INET; sin->sin_port = 0; sin->sin_addr.s_addr = inet_addr(SSDP_NETMASK); @@ -826,6 +858,9 @@ fail: close(sock); return ret; +#else /* __linux__ */ + return 0; +#endif /* __linux__ */ } diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c index db47c29..b637454 100644 --- a/src/wps/wps_upnp_web.c +++ b/src/wps/wps_upnp_web.c @@ -1578,7 +1578,6 @@ static void web_connection_parse_subscribe(struct web_connection *c, ret = HTTP_INTERNAL_SERVER_ERROR; goto error; } - callback_urls = NULL; /* is now owned by subscription */ } else { ret = HTTP_PRECONDITION_FAILED; goto error; @@ -1630,6 +1629,7 @@ error: http_put_empty(buf, ret); send_wpabuf(c->sd, buf); wpabuf_free(buf); + os_free(callback_urls); } |