X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fwps%2Fwps_er.c;h=4726e52612ca0302ebd5524a37dcba213c38a511;hb=2d5e0d78e98a8209a53ee8f5539fef751fc23adc;hp=4a6e6f7e277e4872fbfdb31bd3c9aa6b93a10ec8;hpb=7c009db2a652acefea5831bbecfbb88fc5087208;p=libeap.git diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c index 4a6e6f7..4726e52 100644 --- a/src/wps/wps_er.c +++ b/src/wps/wps_er.c @@ -25,85 +25,16 @@ #include "wps_i.h" #include "wps_upnp.h" #include "wps_upnp_i.h" +#include "wps_er.h" -/* TODO: - * send notification of new AP device with wpa_msg - * re-send notifications with wpa_msg if ER re-started (to update wpa_gui-qt4) - * (also re-send SSDP M-SEARCH in this case to find new APs) - * parse UPnP event messages - */ - +static void wps_er_deinit_finish(void *eloop_data, void *user_ctx); static void wps_er_ap_timeout(void *eloop_data, void *user_ctx); static void wps_er_sta_timeout(void *eloop_data, void *user_ctx); - - -struct wps_er_sta { - struct wps_er_sta *next; - struct wps_er_ap *ap; - u8 addr[ETH_ALEN]; - u16 config_methods; - u8 uuid[WPS_UUID_LEN]; - u8 pri_dev_type[8]; - u16 dev_passwd_id; - int m1_received; - char *manufacturer; - char *model_name; - char *model_number; - char *serial_number; - char *dev_name; - struct wps_data *wps; - struct http_client *http; -}; - -struct wps_er_ap { - struct wps_er_ap *next; - struct wps_er *er; - struct wps_er_sta *sta; /* list of STAs/Enrollees using this AP */ - struct in_addr addr; - char *location; - struct http_client *http; - struct wps_data *wps; - - u8 uuid[WPS_UUID_LEN]; - char *friendly_name; - char *manufacturer; - char *manufacturer_url; - char *model_description; - char *model_name; - char *model_number; - char *model_url; - char *serial_number; - char *udn; - char *upc; - - char *scpd_url; - char *control_url; - char *event_sub_url; - - int subscribed; - unsigned int id; - - struct wps_credential *ap_settings; -}; - -struct wps_er { - struct wps_context *wps; - char ifname[17]; - char *mac_addr_text; /* mac addr of network i.f. we use */ - u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */ - char *ip_addr_text; /* IP address of network i.f. we use */ - unsigned ip_addr; /* IP address of network i.f. we use (host order) */ - int multicast_sd; - int ssdp_sd; - struct wps_er_ap *ap; - struct http_server *http_srv; - int http_port; - unsigned int next_ap_id; -}; - - static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg); +static int wps_er_send_get_device_info(struct wps_er_ap *ap, + void (*m1_handler)(struct wps_er_ap *ap, + struct wpabuf *m1)); static void wps_er_sta_event(struct wps_context *wps, struct wps_er_sta *sta, @@ -133,11 +64,10 @@ static void wps_er_sta_event(struct wps_context *wps, struct wps_er_sta *sta, static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr) { - struct wps_er_sta *sta = ap->sta; - while (sta) { + struct wps_er_sta *sta; + dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) { if (os_memcmp(sta->addr, addr, ETH_ALEN) == 0) return sta; - sta = sta->next; } return NULL; } @@ -155,6 +85,7 @@ static void wps_er_sta_free(struct wps_er_sta *sta) os_free(sta->dev_name); http_client_free(sta->http); eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL); + os_free(sta->cred); os_free(sta); } @@ -162,49 +93,33 @@ static void wps_er_sta_free(struct wps_er_sta *sta) static void wps_er_sta_remove_all(struct wps_er_ap *ap) { struct wps_er_sta *prev, *sta; - - sta = ap->sta; - ap->sta = NULL; - - while (sta) { - prev = sta; - sta = sta->next; - wps_er_sta_free(prev); - } + dl_list_for_each_safe(sta, prev, &ap->sta, struct wps_er_sta, list) + wps_er_sta_free(sta); } static struct wps_er_ap * wps_er_ap_get(struct wps_er *er, - struct in_addr *addr) + struct in_addr *addr, const u8 *uuid) { struct wps_er_ap *ap; - for (ap = er->ap; ap; ap = ap->next) { - if (ap->addr.s_addr == addr->s_addr) - break; + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { + if ((addr == NULL || ap->addr.s_addr == addr->s_addr) && + (uuid == NULL || + os_memcmp(uuid, ap->uuid, WPS_UUID_LEN) == 0)) + return ap; } - return ap; -} - - -static struct wps_er_ap * wps_er_ap_get_uuid(struct wps_er *er, const u8 *uuid) -{ - struct wps_er_ap *ap; - for (ap = er->ap; ap; ap = ap->next) { - if (os_memcmp(uuid, ap->uuid, WPS_UUID_LEN) == 0) - break; - } - return ap; + return NULL; } static struct wps_er_ap * wps_er_ap_get_id(struct wps_er *er, unsigned int id) { struct wps_er_ap *ap; - for (ap = er->ap; ap; ap = ap->next) { + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { if (ap->id == id) - break; + return ap; } - return ap; + return NULL; } @@ -228,23 +143,19 @@ static void wps_er_ap_event(struct wps_context *wps, struct wps_er_ap *ap, evap->model_url = ap->model_url; evap->serial_number = ap->serial_number; evap->upc = ap->upc; + evap->pri_dev_type = ap->pri_dev_type; + evap->wps_state = ap->wps_state; + evap->mac_addr = ap->mac_addr; wps->event_cb(wps->cb_ctx, event, &data); } -static void wps_er_ap_free(struct wps_er *er, struct wps_er_ap *ap) +static void wps_er_ap_free(struct wps_er_ap *ap) { - /* TODO: if ap->subscribed, unsubscribe from events if the AP is still - * alive */ - wpa_printf(MSG_DEBUG, "WPS ER: Removing AP entry for %s (%s)", - inet_ntoa(ap->addr), ap->location); - eloop_cancel_timeout(wps_er_ap_timeout, er, ap); - wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_REMOVE); - os_free(ap->location); http_client_free(ap->http); - if (ap->wps) - wps_deinit(ap->wps); + ap->http = NULL; + os_free(ap->location); os_free(ap->friendly_name); os_free(ap->manufacturer); os_free(ap->manufacturer_url); @@ -262,9 +173,128 @@ static void wps_er_ap_free(struct wps_er *er, struct wps_er_ap *ap) os_free(ap->ap_settings); + os_free(ap); +} + + +static void wps_er_ap_unsubscribed(struct wps_er *er, struct wps_er_ap *ap) +{ + wpa_printf(MSG_DEBUG, "WPS ER: Unsubscribed from AP %s (%s)", + inet_ntoa(ap->addr), ap->location); + dl_list_del(&ap->list); + wps_er_ap_free(ap); + + if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing)) { + eloop_cancel_timeout(wps_er_deinit_finish, er, NULL); + wps_er_deinit_finish(er, NULL); + } +} + + +static void wps_er_http_unsubscribe_cb(void *ctx, struct http_client *c, + enum http_client_event event) +{ + struct wps_er_ap *ap = ctx; + + switch (event) { + case HTTP_CLIENT_OK: + wpa_printf(MSG_DEBUG, "WPS ER: Unsubscribed from events"); + ap->subscribed = 0; + break; + case HTTP_CLIENT_FAILED: + case HTTP_CLIENT_INVALID_REPLY: + case HTTP_CLIENT_TIMEOUT: + wpa_printf(MSG_DEBUG, "WPS ER: Failed to unsubscribe from " + "events"); + break; + } + http_client_free(ap->http); + ap->http = NULL; + + /* + * Need to get rid of the AP entry regardless of whether we managed to + * unsubscribe cleanly or not. + */ + wps_er_ap_unsubscribed(ap->er, ap); +} + + +static void wps_er_ap_unsubscribe(struct wps_er *er, struct wps_er_ap *ap) +{ + struct wpabuf *req; + struct sockaddr_in dst; + char *url, *path; + char sid[100]; + + if (ap->event_sub_url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot " + "subscribe"); + goto fail; + } + if (ap->http) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot " + "send subscribe request"); + goto fail; + } + + url = http_client_url_parse(ap->event_sub_url, &dst, &path); + if (url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL"); + goto fail; + } + + req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000); + if (req == NULL) { + os_free(url); + goto fail; + } + uuid_bin2str(ap->sid, sid, sizeof(sid)); + wpabuf_printf(req, + "UNSUBSCRIBE %s HTTP/1.1\r\n" + "HOST: %s:%d\r\n" + "SID: uuid:%s\r\n" + "\r\n", + path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port), sid); + os_free(url); + wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Unsubscription request", + wpabuf_head(req), wpabuf_len(req)); + + ap->http = http_client_addr(&dst, req, 1000, + wps_er_http_unsubscribe_cb, ap); + if (ap->http == NULL) { + wpabuf_free(req); + goto fail; + } + return; + +fail: + /* + * Need to get rid of the AP entry even when we fail to unsubscribe + * cleanly. + */ + wps_er_ap_unsubscribed(ap->er, ap); +} + +static void wps_er_ap_remove_entry(struct wps_er *er, struct wps_er_ap *ap) +{ + wpa_printf(MSG_DEBUG, "WPS ER: Removing AP entry for %s (%s)", + inet_ntoa(ap->addr), ap->location); + eloop_cancel_timeout(wps_er_ap_timeout, er, ap); wps_er_sta_remove_all(ap); + wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_REMOVE); + http_client_free(ap->http); + ap->http = NULL; + if (ap->wps) { + wps_deinit(ap->wps); + ap->wps = NULL; + } - os_free(ap); + dl_list_del(&ap->list); + if (ap->subscribed) { + dl_list_add(&er->ap_unsubscribing, &ap->list); + wps_er_ap_unsubscribe(er, ap); + } else + wps_er_ap_free(ap); } @@ -273,7 +303,42 @@ static void wps_er_ap_timeout(void *eloop_data, void *user_ctx) struct wps_er *er = eloop_data; struct wps_er_ap *ap = user_ctx; wpa_printf(MSG_DEBUG, "WPS ER: AP advertisement timed out"); - wps_er_ap_free(er, ap); + wps_er_ap_remove_entry(er, ap); +} + + +static int wps_er_get_sid(struct wps_er_ap *ap, char *sid) +{ + char *pos; + char txt[100]; + + if (!sid) { + wpa_printf(MSG_DEBUG, "WPS ER: No SID received from %s (%s)", + inet_ntoa(ap->addr), ap->location); + return -1; + } + + pos = os_strstr(sid, "uuid:"); + if (!pos) { + wpa_printf(MSG_DEBUG, "WPS ER: Invalid SID received from " + "%s (%s): '%s'", inet_ntoa(ap->addr), ap->location, + sid); + return -1; + } + + pos += 5; + if (uuid_str2bin(pos, ap->sid) < 0) { + wpa_printf(MSG_DEBUG, "WPS ER: Invalid SID received from " + "%s (%s): '%s'", inet_ntoa(ap->addr), ap->location, + sid); + return -1; + } + + uuid_bin2str(ap->sid, txt, sizeof(txt)); + wpa_printf(MSG_DEBUG, "WPS ER: SID for subscription with %s (%s): %s", + inet_ntoa(ap->addr), ap->location, txt); + + return 0; } @@ -285,6 +350,8 @@ static void wps_er_http_subscribe_cb(void *ctx, struct http_client *c, switch (event) { case HTTP_CLIENT_OK: wpa_printf(MSG_DEBUG, "WPS ER: Subscribed to events"); + ap->subscribed = 1; + wps_er_get_sid(ap, http_client_get_hdr_line(c, "SID")); wps_er_ap_event(ap->er->wps, ap, WPS_EV_ER_AP_ADD); break; case HTTP_CLIENT_FAILED: @@ -329,12 +396,13 @@ static void wps_er_subscribe(struct wps_er_ap *ap) wpabuf_printf(req, "SUBSCRIBE %s HTTP/1.1\r\n" "HOST: %s:%d\r\n" - "CALLBACK: \r\n" + "CALLBACK: \r\n" "NT: upnp:event\r\n" "TIMEOUT: Second-%d\r\n" "\r\n", path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port), - ap->er->ip_addr_text, ap->er->http_port, ap->id, 1800); + ap->er->ip_addr_text, ap->er->http_port, + ap->er->event_id, ap->id, 1800); os_free(url); wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Subscription request", wpabuf_head(req), wpabuf_len(req)); @@ -346,6 +414,31 @@ static void wps_er_subscribe(struct wps_er_ap *ap) } +static void wps_er_ap_get_m1(struct wps_er_ap *ap, struct wpabuf *m1) +{ + struct wps_parse_attr attr; + + if (wps_parse_msg(m1, &attr) < 0) { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse M1"); + return; + } + if (attr.primary_dev_type) + os_memcpy(ap->pri_dev_type, attr.primary_dev_type, 8); + if (attr.wps_state) + ap->wps_state = *attr.wps_state; + if (attr.mac_addr) + os_memcpy(ap->mac_addr, attr.mac_addr, ETH_ALEN); + + wps_er_subscribe(ap); +} + + +static void wps_er_get_device_info(struct wps_er_ap *ap) +{ + wps_er_send_get_device_info(ap, wps_er_ap_get_m1); +} + + static void wps_er_parse_device_description(struct wps_er_ap *ap, struct wpabuf *reply) { @@ -387,7 +480,8 @@ static void wps_er_parse_device_description(struct wps_er_ap *ap, pos = os_strstr(ap->udn, "uuid:"); if (pos) { pos += 5; - uuid_str2bin(pos, ap->uuid); + if (uuid_str2bin(pos, ap->uuid) < 0) + wpa_printf(MSG_DEBUG, "WPS ER: Invalid UUID in UDN"); } ap->upc = xml_get_first_item(data, "UPC"); @@ -412,7 +506,7 @@ static void wps_er_http_dev_desc_cb(void *ctx, struct http_client *c, { struct wps_er_ap *ap = ctx; struct wpabuf *reply; - int subscribe = 0; + int ok = 0; switch (event) { case HTTP_CLIENT_OK: @@ -420,7 +514,7 @@ static void wps_er_http_dev_desc_cb(void *ctx, struct http_client *c, if (reply == NULL) break; wps_er_parse_device_description(ap, reply); - subscribe = 1; + ok = 1; break; case HTTP_CLIENT_FAILED: case HTTP_CLIENT_INVALID_REPLY: @@ -430,17 +524,17 @@ static void wps_er_http_dev_desc_cb(void *ctx, struct http_client *c, } http_client_free(ap->http); ap->http = NULL; - if (subscribe) - wps_er_subscribe(ap); + if (ok) + wps_er_get_device_info(ap); } -static void wps_er_ap_add(struct wps_er *er, struct in_addr *addr, - const char *location, int max_age) +void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr, + const char *location, int max_age) { struct wps_er_ap *ap; - ap = wps_er_ap_get(er, addr); + ap = wps_er_ap_get(er, addr, uuid); if (ap) { /* Update advertisement timeout */ eloop_cancel_timeout(wps_er_ap_timeout, er, ap); @@ -451,6 +545,7 @@ static void wps_er_ap_add(struct wps_er *er, struct in_addr *addr, ap = os_zalloc(sizeof(*ap)); if (ap == NULL) return; + dl_list_init(&ap->sta); ap->er = er; ap->id = ++er->next_ap_id; ap->location = os_strdup(location); @@ -458,10 +553,10 @@ static void wps_er_ap_add(struct wps_er *er, struct in_addr *addr, os_free(ap); return; } - ap->next = er->ap; - er->ap = ap; + dl_list_add(&er->ap, &ap->list); ap->addr.s_addr = addr->s_addr; + os_memcpy(ap->uuid, uuid, WPS_UUID_LEN); eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap); wpa_printf(MSG_DEBUG, "WPS ER: Added AP entry for %s (%s)", @@ -473,21 +568,14 @@ static void wps_er_ap_add(struct wps_er *er, struct in_addr *addr, } -static void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr) +void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr) { - struct wps_er_ap *prev = NULL, *ap = er->ap; - - while (ap) { + struct wps_er_ap *ap; + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { if (ap->addr.s_addr == addr->s_addr) { - if (prev) - prev->next = ap->next; - else - er->ap = ap->next; - wps_er_ap_free(er, ap); + wps_er_ap_remove_entry(er, ap); return; } - prev = ap; - ap = ap->next; } } @@ -495,135 +583,8 @@ static void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr) static void wps_er_ap_remove_all(struct wps_er *er) { struct wps_er_ap *prev, *ap; - - ap = er->ap; - er->ap = NULL; - - while (ap) { - prev = ap; - ap = ap->next; - wps_er_ap_free(er, prev); - } -} - - -static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx) -{ - struct wps_er *er = eloop_ctx; - struct sockaddr_in addr; /* client address */ - socklen_t addr_len; - int nread; - char buf[MULTICAST_MAX_READ], *pos, *pos2, *start; - int wfa = 0, byebye = 0; - int max_age = -1; - char *location = NULL; - - addr_len = sizeof(addr); - nread = recvfrom(sd, buf, sizeof(buf) - 1, 0, - (struct sockaddr *) &addr, &addr_len); - if (nread <= 0) - return; - buf[nread] = '\0'; - - wpa_printf(MSG_DEBUG, "WPS ER: Received SSDP from %s", - inet_ntoa(addr.sin_addr)); - wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Received SSDP contents", - (u8 *) buf, nread); - - if (sd == er->multicast_sd) { - /* Reply to M-SEARCH */ - if (os_strncmp(buf, "HTTP/1.1 200 OK", 15) != 0) - return; /* unexpected response header */ - } else { - /* Unsolicited message (likely NOTIFY or M-SEARCH) */ - if (os_strncmp(buf, "NOTIFY ", 7) != 0) - return; /* only process notifications */ - } - - for (start = buf; start && *start; start = pos) { - pos = os_strchr(start, '\n'); - if (pos) { - if (pos[-1] == '\r') - pos[-1] = '\0'; - *pos++ = '\0'; - } - if (os_strstr(start, "schemas-wifialliance-org:device:" - "WFADevice:1")) - wfa = 1; - if (os_strstr(start, "schemas-wifialliance-org:service:" - "WFAWLANConfig:1")) - wfa = 1; - if (os_strncasecmp(start, "LOCATION:", 9) == 0) { - start += 9; - while (*start == ' ') - start++; - location = start; - } else if (os_strncasecmp(start, "NTS:", 4) == 0) { - if (os_strstr(start, "ssdp:byebye")) - byebye = 1; - } else if (os_strncasecmp(start, "CACHE-CONTROL:", 14) == 0) { - start += 9; - while (*start == ' ') - start++; - pos2 = os_strstr(start, "max-age="); - if (pos2 == NULL) - continue; - pos2 += 8; - max_age = atoi(pos2); - } - } - - if (!wfa) - return; /* Not WPS advertisement/reply */ - - if (byebye) { - wps_er_ap_remove(er, &addr.sin_addr); - return; - } - - if (!location) - return; /* Unknown location */ - - if (max_age < 1) - return; /* No max-age reported */ - - wpa_printf(MSG_DEBUG, "WPS ER: AP discovered: %s " - "(packet source: %s max-age: %d)", - location, inet_ntoa(addr.sin_addr), max_age); - - wps_er_ap_add(er, &addr.sin_addr, location, max_age); -} - - -static void wps_er_send_ssdp_msearch(struct wps_er *er) -{ - struct wpabuf *msg; - struct sockaddr_in dest; - - msg = wpabuf_alloc(500); - if (msg == NULL) - return; - - wpabuf_put_str(msg, - "M-SEARCH * HTTP/1.1\r\n" - "HOST: 239.255.255.250:1900\r\n" - "MAN: \"ssdp:discover\"\r\n" - "MX: 3\r\n" - "ST: urn:schemas-wifialliance-org:device:WFADevice:1" - "\r\n" - "\r\n"); - - 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); - - if (sendto(er->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0, - (struct sockaddr *) &dest, sizeof(dest)) < 0) - wpa_printf(MSG_DEBUG, "WPS ER: M-SEARCH sendto failed: " - "%d (%s)", errno, strerror(errno)); - - wpabuf_free(msg); + dl_list_for_each_safe(ap, prev, &er->ap, struct wps_er_ap, list) + wps_er_ap_remove_entry(er, ap); } @@ -676,22 +637,9 @@ static void wps_er_http_resp_ok(struct http_request *req) static void wps_er_sta_timeout(void *eloop_data, void *user_ctx) { - struct wps_er_sta *prev, *tmp, *sta = eloop_data; + struct wps_er_sta *sta = eloop_data; wpa_printf(MSG_DEBUG, "WPS ER: STA entry timed out"); - tmp = sta->ap->sta; - prev = NULL; - while (tmp) { - if (tmp == sta) - break; - prev = tmp; - tmp = tmp->next; - } - if (tmp) { - if (prev) - prev->next = sta->next; - else - sta->ap->sta = sta->next; - } + dl_list_del(&sta->list); wps_er_sta_free(sta); } @@ -721,8 +669,7 @@ static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap, return NULL; os_memcpy(sta->addr, addr, ETH_ALEN); sta->ap = ap; - sta->next = ap->sta; - ap->sta = sta; + dl_list_add(&ap->sta, &sta->list); new_sta = 1; } @@ -816,6 +763,7 @@ static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap, } wps_er_add_sta_data(ap, addr, &attr, 1); + wps_registrar_probe_req_rx(ap->er->wps->registrar, addr, msg, 0); } @@ -974,6 +922,18 @@ static void wps_er_sta_process(struct wps_er_sta *sta, struct wpabuf *msg, struct wpabuf *next = wps_get_msg(sta->wps, &op_code); if (next) wps_er_sta_send_msg(sta, next); + } else { + wpa_printf(MSG_DEBUG, "WPS ER: Protocol run %s with the " + "enrollee (res=%d)", + res == WPS_DONE ? "succeeded" : "failed", res); + wps_deinit(sta->wps); + sta->wps = NULL; + if (res == WPS_DONE) { + /* Remove the STA entry after short timeout */ + eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL); + eloop_register_timeout(10, 0, wps_er_sta_timeout, sta, + NULL); + } } } @@ -995,6 +955,17 @@ static void wps_er_sta_start(struct wps_er_sta *sta, struct wpabuf *msg) return; sta->wps->er = 1; sta->wps->use_cred = sta->ap->ap_settings; + if (sta->ap->ap_settings) { + os_free(sta->cred); + sta->cred = os_malloc(sizeof(*sta->cred)); + if (sta->cred) { + os_memcpy(sta->cred, sta->ap->ap_settings, + sizeof(*sta->cred)); + sta->cred->cred_attr = NULL; + os_memcpy(sta->cred->mac_addr, sta->addr, ETH_ALEN); + sta->wps->use_cred = sta->cred; + } + } wps_er_sta_process(sta, msg, WSC_MSG); } @@ -1124,7 +1095,19 @@ static void wps_er_http_notify(struct wps_er *er, struct http_request *req) char *uri = http_request_get_uri(req); if (os_strncmp(uri, "/event/", 7) == 0) { - wps_er_http_event(er, req, atoi(uri + 7)); + unsigned int event_id; + char *pos; + event_id = atoi(uri + 7); + if (event_id != er->event_id) { + wpa_printf(MSG_DEBUG, "WPS ER: HTTP event for an " + "unknown event id %u", event_id); + return; + } + pos = os_strchr(uri + 7, '/'); + if (pos == NULL) + return; + pos++; + wps_er_http_event(er, req, atoi(pos)); } else { wpa_printf(MSG_DEBUG, "WPS ER: Unknown HTTP NOTIFY for '%s'", uri); @@ -1169,7 +1152,7 @@ static void wps_er_http_req(void *ctx, struct http_request *req) struct wps_er * -wps_er_init(struct wps_context *wps, const char *ifname) +wps_er_init(struct wps_context *wps, const char *ifname, const char *filter) { struct wps_er *er; struct in_addr addr; @@ -1177,81 +1160,106 @@ wps_er_init(struct wps_context *wps, const char *ifname) er = os_zalloc(sizeof(*er)); if (er == NULL) return NULL; + dl_list_init(&er->ap); + dl_list_init(&er->ap_unsubscribing); er->multicast_sd = -1; er->ssdp_sd = -1; os_strlcpy(er->ifname, ifname, sizeof(er->ifname)); er->wps = wps; - - if (get_netif_info(ifname, - &er->ip_addr, &er->ip_addr_text, - er->mac_addr, &er->mac_addr_text)) { - wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address " - "for %s. Does it have IP address?", ifname); - wps_er_deinit(er); + if (os_get_random((unsigned char *) &er->event_id, + sizeof(er->event_id)) < 0) { + wps_er_deinit(er, NULL, NULL); return NULL; } + /* Limit event_id to < 32 bits to avoid issues with atoi() */ + er->event_id &= 0x0fffffff; - if (add_ssdp_network(ifname)) { - wps_er_deinit(er); - return NULL; + if (filter) { + if (inet_aton(filter, &er->filter_addr) == 0) { + wpa_printf(MSG_INFO, "WPS UPnP: Invalid filter " + "address %s", filter); + wps_er_deinit(er, NULL, NULL); + return NULL; + } + wpa_printf(MSG_DEBUG, "WPS UPnP: Only accepting connections " + "with %s", filter); } - - er->multicast_sd = ssdp_open_multicast_sock(er->ip_addr); - if (er->multicast_sd < 0) { - wps_er_deinit(er); + if (get_netif_info(ifname, &er->ip_addr, &er->ip_addr_text, + er->mac_addr)) { + wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address " + "for %s. Does it have IP address?", ifname); + wps_er_deinit(er, NULL, NULL); return NULL; } - er->ssdp_sd = ssdp_listener_open(); - if (er->ssdp_sd < 0) { - wps_er_deinit(er); - return NULL; - } - if (eloop_register_sock(er->multicast_sd, EVENT_TYPE_READ, - wps_er_ssdp_rx, er, NULL) || - eloop_register_sock(er->ssdp_sd, EVENT_TYPE_READ, - wps_er_ssdp_rx, er, NULL)) { - wps_er_deinit(er); + if (wps_er_ssdp_init(er) < 0) { + wps_er_deinit(er, NULL, NULL); return NULL; } addr.s_addr = er->ip_addr; er->http_srv = http_server_init(&addr, -1, wps_er_http_req, er); if (er->http_srv == NULL) { - wps_er_deinit(er); + wps_er_deinit(er, NULL, NULL); return NULL; } er->http_port = http_server_get_port(er->http_srv); - wpa_printf(MSG_DEBUG, "WPS ER: Start (ifname=%s ip_addr=%s " - "mac_addr=%s)", - er->ifname, er->ip_addr_text, er->mac_addr_text); + wpa_printf(MSG_DEBUG, "WPS ER: Start (ifname=%s ip_addr=%s)", + er->ifname, er->ip_addr_text); + + return er; +} + + +void wps_er_refresh(struct wps_er *er) +{ + struct wps_er_ap *ap; + struct wps_er_sta *sta; + + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { + wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_ADD); + dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) + wps_er_sta_event(er->wps, sta, WPS_EV_ER_ENROLLEE_ADD); + } wps_er_send_ssdp_msearch(er); +} - return er; + +static void wps_er_deinit_finish(void *eloop_data, void *user_ctx) +{ + struct wps_er *er = eloop_data; + void (*deinit_done_cb)(void *ctx); + void *deinit_done_ctx; + + wpa_printf(MSG_DEBUG, "WPS ER: Finishing deinit"); + + deinit_done_cb = er->deinit_done_cb; + deinit_done_ctx = er->deinit_done_ctx; + os_free(er->ip_addr_text); + os_free(er); + + if (deinit_done_cb) + deinit_done_cb(deinit_done_ctx); } -void wps_er_deinit(struct wps_er *er) +void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx) { if (er == NULL) return; http_server_deinit(er->http_srv); wps_er_ap_remove_all(er); - if (er->multicast_sd >= 0) { - eloop_unregister_sock(er->multicast_sd, EVENT_TYPE_READ); - close(er->multicast_sd); - } - if (er->ssdp_sd >= 0) { - eloop_unregister_sock(er->ssdp_sd, EVENT_TYPE_READ); - close(er->ssdp_sd); - } - os_free(er->ip_addr_text); - os_free(er->mac_addr_text); - os_free(er); + wps_er_ssdp_deinit(er); + eloop_register_timeout(dl_list_empty(&er->ap_unsubscribing) ? 0 : 5, 0, + wps_er_deinit_finish, er, NULL); + wpa_printf(MSG_DEBUG, "WPS ER: Finish deinit from timeout"); + er->deinitializing = 1; + er->deinit_done_cb = cb; + er->deinit_done_ctx = ctx; } @@ -1342,25 +1350,48 @@ static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg, } +static int wps_er_build_uuid_r(struct wpabuf *msg, const u8 *uuid_r) +{ +#ifdef CONFIG_WPS2 + wpabuf_put_be16(msg, ATTR_UUID_R); + wpabuf_put_be16(msg, WPS_UUID_LEN); + wpabuf_put_data(msg, uuid_r, WPS_UUID_LEN); +#endif /* CONFIG_WPS2 */ + return 0; +} + + void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, u16 sel_reg_config_methods) { struct wpabuf *msg; struct wps_er_ap *ap; + struct wps_registrar *reg = er->wps->registrar; + const u8 *auth_macs; + size_t count; + + if (er->skip_set_sel_reg) { + wpa_printf(MSG_DEBUG, "WPS ER: Skip SetSelectedRegistrar"); + return; + } msg = wpabuf_alloc(500); if (msg == NULL) return; + auth_macs = wps_authorized_macs(reg, &count); + if (wps_build_version(msg) || wps_er_build_selected_registrar(msg, sel_reg) || wps_er_build_dev_password_id(msg, dev_passwd_id) || - wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods)) { + wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) || + wps_build_wfa_ext(msg, 0, auth_macs, count) || + wps_er_build_uuid_r(msg, er->wps->uuid)) { wpabuf_free(msg); return; } - for (ap = er->ap; ap; ap = ap->next) + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) wps_er_send_set_sel_reg(ap, msg); wpabuf_free(msg); @@ -1372,6 +1403,12 @@ int wps_er_pbc(struct wps_er *er, const u8 *uuid) if (er == NULL || er->wps == NULL) return -1; + if (wps_registrar_pbc_overlap(er->wps->registrar, NULL, NULL)) { + wpa_printf(MSG_DEBUG, "WPS ER: PBC overlap - do not start PBC " + "mode"); + return -1; + } + /* * TODO: Should enable PBC mode only in a single AP based on which AP * the Enrollee (uuid) is using. Now, we may end up enabling multiple @@ -1388,6 +1425,8 @@ int wps_er_pbc(struct wps_er *er, const u8 *uuid) static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred) { struct wps_er_ap *ap = ctx; + union wps_event_data data; + wpa_printf(MSG_DEBUG, "WPS ER: AP Settings received"); os_free(ap->ap_settings); ap->ap_settings = os_malloc(sizeof(*cred)); @@ -1396,7 +1435,11 @@ static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred) ap->ap_settings->cred_attr = NULL; } - /* TODO: send info through ctrl_iface */ + os_memset(&data, 0, sizeof(data)); + data.ap_settings.uuid = ap->uuid; + data.ap_settings.cred = cred; + ap->er->wps->event_cb(ap->er->wps->cb_ctx, WPS_EV_ER_AP_SETTINGS, + &data); } @@ -1490,10 +1533,26 @@ static void wps_er_ap_put_message(struct wps_er_ap *ap, static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg) { enum wps_process_res res; + struct wps_parse_attr attr; + enum wsc_op_code op_code; - res = wps_process_msg(ap->wps, WSC_MSG, msg); + op_code = WSC_MSG; + if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) { + switch (*attr.msg_type) { + case WPS_WSC_ACK: + op_code = WSC_ACK; + break; + case WPS_WSC_NACK: + op_code = WSC_NACK; + break; + case WPS_WSC_DONE: + op_code = WSC_Done; + break; + } + } + + res = wps_process_msg(ap->wps, op_code, msg); if (res == WPS_CONTINUE) { - enum wsc_op_code op_code; struct wpabuf *next = wps_get_msg(ap->wps, &op_code); if (next) { wps_er_ap_put_message(ap, next); @@ -1504,6 +1563,10 @@ static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg) wps_deinit(ap->wps); ap->wps = NULL; } + } else if (res == WPS_DONE) { + wpa_printf(MSG_DEBUG, "WPS ER: Protocol run done"); + wps_deinit(ap->wps); + ap->wps = NULL; } else { wpa_printf(MSG_DEBUG, "WPS ER: Failed to process message from " "AP (res=%d)", res); @@ -1513,25 +1576,13 @@ static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg) } -static void wps_er_ap_learn(struct wps_er_ap *ap, const char *dev_info) +static void wps_er_ap_learn_m1(struct wps_er_ap *ap, struct wpabuf *m1) { - struct wpabuf *info; - enum http_reply_code ret; struct wps_config cfg; - wpa_printf(MSG_DEBUG, "WPS ER: Received GetDeviceInfo response (M1) " - "from the AP"); - info = xml_get_base64_item(dev_info, "NewDeviceInfo", &ret); - if (info == NULL) { - wpa_printf(MSG_DEBUG, "WPS ER: Could not extract " - "NewDeviceInfo from GetDeviceInfo response"); - return; - } - if (ap->wps) { wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in " "progress with this AP"); - wpabuf_free(info); return; } @@ -1539,14 +1590,30 @@ static void wps_er_ap_learn(struct wps_er_ap *ap, const char *dev_info) cfg.wps = ap->er->wps; cfg.registrar = 1; ap->wps = wps_init(&cfg); - if (ap->wps == NULL) { - wpabuf_free(info); + if (ap->wps == NULL) return; - } ap->wps->ap_settings_cb = wps_er_ap_settings_cb; ap->wps->ap_settings_cb_ctx = ap; - wps_er_ap_process(ap, info); + wps_er_ap_process(ap, m1); +} + + +static void wps_er_ap_learn(struct wps_er_ap *ap, const char *dev_info) +{ + struct wpabuf *info; + enum http_reply_code ret; + + wpa_printf(MSG_DEBUG, "WPS ER: Received GetDeviceInfo response (M1) " + "from the AP"); + info = xml_get_base64_item(dev_info, "NewDeviceInfo", &ret); + if (info == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: Could not extract " + "NewDeviceInfo from GetDeviceInfo response"); + return; + } + + ap->m1_handler(ap, info); wpabuf_free(info); } @@ -1585,27 +1652,18 @@ static void wps_er_http_get_dev_info_cb(void *ctx, struct http_client *c, } -int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin, - size_t pin_len) +static int wps_er_send_get_device_info(struct wps_er_ap *ap, + void (*m1_handler)(struct wps_er_ap *ap, + struct wpabuf *m1)) { - struct wps_er_ap *ap; struct wpabuf *buf; char *len_ptr, *body_ptr; struct sockaddr_in dst; char *url, *path; - if (er == NULL) - return -1; - - ap = wps_er_ap_get_uuid(er, uuid); - if (ap == NULL) { - wpa_printf(MSG_DEBUG, "WPS ER: AP not found for learn " - "request"); - return -1; - } - if (ap->wps || ap->http) { - wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing " - "with the AP - cannot start learn"); + if (ap->http) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing " + "with the AP - cannot get device info"); return -1; } @@ -1635,8 +1693,100 @@ int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin, return -1; } - /* TODO: add PIN without SetSelectedRegistrar trigger to all APs */ - wps_registrar_add_pin(er->wps->registrar, uuid, pin, pin_len, 0); + ap->m1_handler = m1_handler; + + return 0; +} + + +int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin, + size_t pin_len) +{ + struct wps_er_ap *ap; + + if (er == NULL) + return -1; + + ap = wps_er_ap_get(er, NULL, uuid); + if (ap == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: AP not found for learn " + "request"); + return -1; + } + if (ap->wps) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing " + "with the AP - cannot start learn"); + return -1; + } + + if (wps_er_send_get_device_info(ap, wps_er_ap_learn_m1) < 0) + return -1; + + er->skip_set_sel_reg = 1; + wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0); + er->skip_set_sel_reg = 0; + + return 0; +} + + +static void wps_er_ap_config_m1(struct wps_er_ap *ap, struct wpabuf *m1) +{ + struct wps_config cfg; + + if (ap->wps) { + wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in " + "progress with this AP"); + return; + } + + os_memset(&cfg, 0, sizeof(cfg)); + cfg.wps = ap->er->wps; + cfg.registrar = 1; + cfg.new_ap_settings = ap->ap_settings; + ap->wps = wps_init(&cfg); + if (ap->wps == NULL) + return; + ap->wps->ap_settings_cb = NULL; + ap->wps->ap_settings_cb_ctx = NULL; + + wps_er_ap_process(ap, m1); +} + + +int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin, + size_t pin_len, const struct wps_credential *cred) +{ + struct wps_er_ap *ap; + + if (er == NULL) + return -1; + + ap = wps_er_ap_get(er, NULL, uuid); + if (ap == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: AP not found for config " + "request"); + return -1; + } + if (ap->wps) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing " + "with the AP - cannot start config"); + return -1; + } + + os_free(ap->ap_settings); + ap->ap_settings = os_malloc(sizeof(*cred)); + if (ap->ap_settings == NULL) + return -1; + os_memcpy(ap->ap_settings, cred, sizeof(*cred)); + ap->ap_settings->cred_attr = NULL; + + if (wps_er_send_get_device_info(ap, wps_er_ap_config_m1) < 0) + return -1; + + er->skip_set_sel_reg = 1; + wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0); + er->skip_set_sel_reg = 0; return 0; }