X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fwps%2Fwps_er.c;h=4726e52612ca0302ebd5524a37dcba213c38a511;hb=2d5e0d78e98a8209a53ee8f5539fef751fc23adc;hp=c86ea2665a4a082085800fa7716d252a25f9c535;hpb=72df2f5fc6ed9e31ede05fe21b0ff8c4ecbbb6b2;p=libeap.git diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c index c86ea26..4726e52 100644 --- a/src/wps/wps_er.c +++ b/src/wps/wps_er.c @@ -25,87 +25,49 @@ #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); +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)); -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; - - 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_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_sta_event(struct wps_context *wps, struct wps_er_sta *sta, + enum wps_event event) +{ + union wps_event_data data; + struct wps_event_er_enrollee *ev = &data.enrollee; + + if (wps->event_cb == NULL) + return; + + os_memset(&data, 0, sizeof(data)); + ev->uuid = sta->uuid; + ev->mac_addr = sta->addr; + ev->m1_received = sta->m1_received; + ev->config_methods = sta->config_methods; + ev->dev_passwd_id = sta->dev_passwd_id; + ev->pri_dev_type = sta->pri_dev_type; + ev->dev_name = sta->dev_name; + ev->manufacturer = sta->manufacturer; + ev->model_name = sta->model_name; + ev->model_number = sta->model_number; + ev->serial_number = sta->serial_number; + wps->event_cb(wps->cb_ctx, event, &data); +} 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; } @@ -113,6 +75,7 @@ static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr) static void wps_er_sta_free(struct wps_er_sta *sta) { + wps_er_sta_event(sta->ap->er->wps, sta, WPS_EV_ER_ENROLLEE_REMOVE); if (sta->wps) wps_deinit(sta->wps); os_free(sta->manufacturer); @@ -122,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); } @@ -129,51 +93,69 @@ 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; + 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; } -static void wps_er_ap_free(struct wps_er *er, struct wps_er_ap *ap) +static void wps_er_ap_event(struct wps_context *wps, struct wps_er_ap *ap, + enum wps_event event) +{ + union wps_event_data data; + struct wps_event_er_ap *evap = &data.ap; + + if (wps->event_cb == NULL) + return; + + os_memset(&data, 0, sizeof(data)); + evap->uuid = ap->uuid; + evap->friendly_name = ap->friendly_name; + evap->manufacturer = ap->manufacturer; + evap->manufacturer_url = ap->manufacturer_url; + evap->model_description = ap->model_description; + evap->model_name = ap->model_name; + evap->model_number = ap->model_number; + 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_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); - os_free(ap->location); http_client_free(ap->http); + ap->http = NULL; + os_free(ap->location); os_free(ap->friendly_name); os_free(ap->manufacturer); os_free(ap->manufacturer_url); @@ -189,18 +171,174 @@ static void wps_er_ap_free(struct wps_er *er, struct wps_er_ap *ap) os_free(ap->control_url); os_free(ap->event_sub_url); - wps_er_sta_remove_all(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; + } + + 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); +} + + 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; } @@ -212,6 +350,9 @@ 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: case HTTP_CLIENT_INVALID_REPLY: @@ -255,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)); @@ -272,11 +414,37 @@ 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) { /* Note: reply includes null termination after the buffer data */ const char *data = wpabuf_head(reply); + char *pos; wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Device info", wpabuf_head(reply), wpabuf_len(reply)); @@ -309,6 +477,12 @@ static void wps_er_parse_device_description(struct wps_er_ap *ap, ap->udn = xml_get_first_item(data, "UDN"); wpa_printf(MSG_DEBUG, "WPS ER: UDN='%s'", ap->udn); + pos = os_strstr(ap->udn, "uuid:"); + if (pos) { + pos += 5; + 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"); wpa_printf(MSG_DEBUG, "WPS ER: UPC='%s'", ap->upc); @@ -332,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: @@ -340,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: @@ -350,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); @@ -371,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); @@ -378,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)", @@ -393,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; } } @@ -415,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); } @@ -598,6 +639,7 @@ static void wps_er_sta_timeout(void *eloop_data, void *user_ctx) { struct wps_er_sta *sta = eloop_data; wpa_printf(MSG_DEBUG, "WPS ER: STA entry timed out"); + dl_list_del(&sta->list); wps_er_sta_free(sta); } @@ -608,18 +650,30 @@ static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap, int probe_req) { struct wps_er_sta *sta = wps_er_sta_get(ap, addr); + int new_sta = 0; + int m1; + + m1 = !probe_req && attr->msg_type && *attr->msg_type == WPS_M1; if (sta == NULL) { + /* + * Only allow new STA entry to be added based on Probe Request + * or M1. This will filter out bogus events and anything that + * may have been ongoing at the time ER subscribed for events. + */ + if (!probe_req && !m1) + return NULL; + sta = os_zalloc(sizeof(*sta)); if (sta == NULL) 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; } - if (!probe_req) + if (m1) sta->m1_received = 1; if (attr->config_methods && (!probe_req || !sta->m1_received)) @@ -684,7 +738,8 @@ static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap, eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL); eloop_register_timeout(300, 0, wps_er_sta_timeout, sta, NULL); - /* TODO: wpa_msg indication if new STA */ + if (m1 || new_sta) + wps_er_sta_event(ap->er->wps, sta, WPS_EV_ER_ENROLLEE_ADD); return sta; } @@ -708,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); } @@ -742,7 +798,8 @@ static const char *urn_wfawlanconfig = "urn:schemas-wifialliance-org:service:WFAWLANConfig:1"; static struct wpabuf * wps_er_soap_hdr(const struct wpabuf *msg, - const char *name, const char *path, + const char *name, const char *arg_name, + const char *path, const struct sockaddr_in *dst, char **len_ptr, char **body_ptr) { @@ -750,10 +807,15 @@ static struct wpabuf * wps_er_soap_hdr(const struct wpabuf *msg, size_t encoded_len; struct wpabuf *buf; - encoded = base64_encode(wpabuf_head(msg), wpabuf_len(msg), - &encoded_len); - if (encoded == NULL) - return NULL; + if (msg) { + encoded = base64_encode(wpabuf_head(msg), wpabuf_len(msg), + &encoded_len); + if (encoded == NULL) + return NULL; + } else { + encoded = NULL; + encoded_len = 0; + } buf = wpabuf_alloc(1000 + encoded_len); if (buf == NULL) { @@ -781,8 +843,11 @@ static struct wpabuf * wps_er_soap_hdr(const struct wpabuf *msg, wpabuf_printf(buf, "\n"); - wpabuf_printf(buf, "%s\n", (char *) encoded); - os_free(encoded); + if (encoded) { + wpabuf_printf(buf, "<%s>%s\n", + arg_name, (char *) encoded, arg_name); + os_free(encoded); + } return buf; } @@ -827,8 +892,8 @@ static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg) return; } - buf = wps_er_soap_hdr(msg, "PutWLANResponse", path, &dst, &len_ptr, - &body_ptr); + buf = wps_er_soap_hdr(msg, "PutWLANResponse", "NewMessage", path, &dst, + &len_ptr, &body_ptr); wpabuf_free(msg); os_free(url); if (buf == NULL) @@ -847,16 +912,28 @@ static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg) } -static void wps_er_sta_process(struct wps_er_sta *sta, struct wpabuf *msg) +static void wps_er_sta_process(struct wps_er_sta *sta, struct wpabuf *msg, + enum wsc_op_code op_code) { enum wps_process_res res; - res = wps_process_msg(sta->wps, WSC_MSG, msg); + res = wps_process_msg(sta->wps, op_code, msg); if (res == WPS_CONTINUE) { - enum wsc_op_code op_code; 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); + } } } @@ -876,8 +953,21 @@ static void wps_er_sta_start(struct wps_er_sta *sta, struct wpabuf *msg) sta->wps = wps_init(&cfg); if (sta->wps == NULL) 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); + wps_er_sta_process(sta, msg, WSC_MSG); } @@ -899,11 +989,28 @@ static void wps_er_process_wlanevent_eap(struct wps_er_ap *ap, const u8 *addr, } sta = wps_er_add_sta_data(ap, addr, &attr, 0); + if (sta == NULL) + return; if (attr.msg_type && *attr.msg_type == WPS_M1) wps_er_sta_start(sta, msg); - else if (sta->wps) - wps_er_sta_process(sta, msg); + else if (sta->wps) { + enum wsc_op_code op_code = WSC_MSG; + if (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; + } + } + wps_er_sta_process(sta, msg, op_code); + } } @@ -988,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); @@ -1033,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; @@ -1041,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; } - - if (add_ssdp_network(ifname)) { - wps_er_deinit(er); - return NULL; + /* Limit event_id to < 32 bits to avoid issues with atoi() */ + er->event_id &= 0x0fffffff; + + 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; } @@ -1163,8 +1307,8 @@ static void wps_er_send_set_sel_reg(struct wps_er_ap *ap, struct wpabuf *msg) return; } - buf = wps_er_soap_hdr(msg, "SetSelectedRegistrar", path, &dst, - &len_ptr, &body_ptr); + buf = wps_er_soap_hdr(msg, "SetSelectedRegistrar", "NewMessage", path, + &dst, &len_ptr, &body_ptr); os_free(url); if (buf == NULL) return; @@ -1206,26 +1350,443 @@ 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); } + + +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 + * APs in PBC mode which could result in session overlap at the + * Enrollee. + */ + if (wps_registrar_button_pushed(er->wps->registrar)) + return -1; + + return 0; +} + + +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)); + if (ap->ap_settings) { + os_memcpy(ap->ap_settings, cred, sizeof(*cred)); + ap->ap_settings->cred_attr = NULL; + } + + 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); +} + + +static void wps_er_http_put_message_cb(void *ctx, struct http_client *c, + enum http_client_event event) +{ + struct wps_er_ap *ap = ctx; + struct wpabuf *reply; + char *msg = NULL; + + switch (event) { + case HTTP_CLIENT_OK: + wpa_printf(MSG_DEBUG, "WPS ER: PutMessage OK"); + reply = http_client_get_body(c); + if (reply == NULL) + break; + msg = os_zalloc(wpabuf_len(reply) + 1); + if (msg == NULL) + break; + os_memcpy(msg, wpabuf_head(reply), wpabuf_len(reply)); + break; + case HTTP_CLIENT_FAILED: + case HTTP_CLIENT_INVALID_REPLY: + case HTTP_CLIENT_TIMEOUT: + wpa_printf(MSG_DEBUG, "WPS ER: PutMessage failed"); + if (ap->wps) { + wps_deinit(ap->wps); + ap->wps = NULL; + } + break; + } + http_client_free(ap->http); + ap->http = NULL; + + if (msg) { + struct wpabuf *buf; + enum http_reply_code ret; + buf = xml_get_base64_item(msg, "NewOutMessage", &ret); + os_free(msg); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: Could not extract " + "NewOutMessage from PutMessage response"); + return; + } + wps_er_ap_process(ap, buf); + wpabuf_free(buf); + } +} + + +static void wps_er_ap_put_message(struct wps_er_ap *ap, + const struct wpabuf *msg) +{ + struct wpabuf *buf; + char *len_ptr, *body_ptr; + struct sockaddr_in dst; + char *url, *path; + + if (ap->http) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing " + "with the AP - cannot continue learn"); + return; + } + + if (ap->control_url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP"); + return; + } + + url = http_client_url_parse(ap->control_url, &dst, &path); + if (url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); + return; + } + + buf = wps_er_soap_hdr(msg, "PutMessage", "NewInMessage", path, &dst, + &len_ptr, &body_ptr); + os_free(url); + if (buf == NULL) + return; + + wps_er_soap_end(buf, "PutMessage", len_ptr, body_ptr); + + ap->http = http_client_addr(&dst, buf, 10000, + wps_er_http_put_message_cb, ap); + if (ap->http == NULL) + wpabuf_free(buf); +} + + +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; + + 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) { + struct wpabuf *next = wps_get_msg(ap->wps, &op_code); + if (next) { + wps_er_ap_put_message(ap, next); + wpabuf_free(next); + } else { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to build " + "message"); + 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); + wps_deinit(ap->wps); + ap->wps = NULL; + } +} + + +static void wps_er_ap_learn_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; + ap->wps = wps_init(&cfg); + 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, 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); +} + + +static void wps_er_http_get_dev_info_cb(void *ctx, struct http_client *c, + enum http_client_event event) +{ + struct wps_er_ap *ap = ctx; + struct wpabuf *reply; + char *dev_info = NULL; + + switch (event) { + case HTTP_CLIENT_OK: + wpa_printf(MSG_DEBUG, "WPS ER: GetDeviceInfo OK"); + reply = http_client_get_body(c); + if (reply == NULL) + break; + dev_info = os_zalloc(wpabuf_len(reply) + 1); + if (dev_info == NULL) + break; + os_memcpy(dev_info, wpabuf_head(reply), wpabuf_len(reply)); + break; + case HTTP_CLIENT_FAILED: + case HTTP_CLIENT_INVALID_REPLY: + case HTTP_CLIENT_TIMEOUT: + wpa_printf(MSG_DEBUG, "WPS ER: GetDeviceInfo failed"); + break; + } + http_client_free(ap->http); + ap->http = NULL; + + if (dev_info) { + wps_er_ap_learn(ap, dev_info); + os_free(dev_info); + } +} + + +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 wpabuf *buf; + char *len_ptr, *body_ptr; + struct sockaddr_in dst; + char *url, *path; + + if (ap->http) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing " + "with the AP - cannot get device info"); + return -1; + } + + if (ap->control_url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP"); + return -1; + } + + url = http_client_url_parse(ap->control_url, &dst, &path); + if (url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); + return -1; + } + + buf = wps_er_soap_hdr(NULL, "GetDeviceInfo", NULL, path, &dst, + &len_ptr, &body_ptr); + os_free(url); + if (buf == NULL) + return -1; + + wps_er_soap_end(buf, "GetDeviceInfo", len_ptr, body_ptr); + + ap->http = http_client_addr(&dst, buf, 10000, + wps_er_http_get_dev_info_cb, ap); + if (ap->http == NULL) { + wpabuf_free(buf); + return -1; + } + + 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; +}