WPS ER: Add STA/Enrollee entries and start processing EAP messages
[libeap.git] / src / wps / wps_er.c
index bfa27e2..05c33da 100644 (file)
  */
 
 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;
@@ -63,7 +83,7 @@ struct wps_er_ap {
 };
 
 struct wps_er {
-       struct wps_registrar *reg;
+       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 */
@@ -78,6 +98,48 @@ struct wps_er {
 };
 
 
+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) {
+               if (os_memcmp(sta->addr, addr, ETH_ALEN) == 0)
+                       return sta;
+               sta = sta->next;
+       }
+       return NULL;
+}
+
+
+static void wps_er_sta_free(struct wps_er_sta *sta)
+{
+       if (sta->wps)
+               wps_deinit(sta->wps);
+       os_free(sta->manufacturer);
+       os_free(sta->model_name);
+       os_free(sta->model_number);
+       os_free(sta->serial_number);
+       os_free(sta->dev_name);
+       http_client_free(sta->http);
+       eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL);
+       os_free(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);
+       }
+}
+
+
 static void wps_er_pin_needed_cb(void *ctx, const u8 *uuid_e,
                                 const struct wps_device_data *dev)
 {
@@ -133,6 +195,8 @@ 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);
 }
 
@@ -489,19 +553,312 @@ static void wps_er_send_ssdp_msearch(struct wps_er *er)
 }
 
 
+static void http_put_date(struct wpabuf *buf)
+{
+       wpabuf_put_str(buf, "Date: ");
+       format_date(buf);
+       wpabuf_put_str(buf, "\r\n");
+}
+
+
+static void wps_er_http_resp_not_found(struct http_request *req)
+{
+       struct wpabuf *buf;
+       buf = wpabuf_alloc(200);
+       if (buf == NULL) {
+               http_request_deinit(req);
+               return;
+       }
+
+       wpabuf_put_str(buf,
+                      "HTTP/1.1 404 Not Found\r\n"
+                      "Server: unspecified, UPnP/1.0, unspecified\r\n"
+                      "Connection: close\r\n");
+       http_put_date(buf);
+       wpabuf_put_str(buf, "\r\n");
+       http_request_send_and_deinit(req, buf);
+}
+
+
+static void wps_er_http_resp_ok(struct http_request *req)
+{
+       struct wpabuf *buf;
+       buf = wpabuf_alloc(200);
+       if (buf == NULL) {
+               http_request_deinit(req);
+               return;
+       }
+
+       wpabuf_put_str(buf,
+                      "HTTP/1.1 200 OK\r\n"
+                      "Server: unspecified, UPnP/1.0, unspecified\r\n"
+                      "Connection: close\r\n"
+                      "Content-Length: 0\r\n");
+       http_put_date(buf);
+       wpabuf_put_str(buf, "\r\n");
+       http_request_send_and_deinit(req, buf);
+}
+
+
+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");
+       wps_er_sta_free(sta);
+}
+
+
+static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap,
+                                              const u8 *addr,
+                                              struct wps_parse_attr *attr,
+                                              int probe_req)
+{
+       struct wps_er_sta *sta = wps_er_sta_get(ap, addr);
+
+       if (sta == 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;
+       }
+
+       if (!probe_req)
+               sta->m1_received = 1;
+
+       if (attr->config_methods && (!probe_req || !sta->m1_received))
+               sta->config_methods = WPA_GET_BE16(attr->config_methods);
+       if (attr->uuid_e && (!probe_req || !sta->m1_received))
+               os_memcpy(sta->uuid, attr->uuid_e, WPS_UUID_LEN);
+       if (attr->primary_dev_type && (!probe_req || !sta->m1_received))
+               os_memcpy(sta->pri_dev_type, attr->primary_dev_type, 8);
+       if (attr->dev_password_id && (!probe_req || !sta->m1_received))
+               sta->dev_passwd_id = WPA_GET_BE16(attr->dev_password_id);
+
+       if (attr->manufacturer) {
+               os_free(sta->manufacturer);
+               sta->manufacturer = os_malloc(attr->manufacturer_len + 1);
+               if (sta->manufacturer) {
+                       os_memcpy(sta->manufacturer, attr->manufacturer,
+                                 attr->manufacturer_len);
+                       sta->manufacturer[attr->manufacturer_len] = '\0';
+               }
+       }
+
+       if (attr->model_name) {
+               os_free(sta->model_name);
+               sta->model_name = os_malloc(attr->model_name_len + 1);
+               if (sta->model_name) {
+                       os_memcpy(sta->model_name, attr->model_name,
+                                 attr->model_name_len);
+                       sta->model_name[attr->model_name_len] = '\0';
+               }
+       }
+
+       if (attr->model_number) {
+               os_free(sta->model_number);
+               sta->model_number = os_malloc(attr->model_number_len + 1);
+               if (sta->model_number) {
+                       os_memcpy(sta->model_number, attr->model_number,
+                                 attr->model_number_len);
+                       sta->model_number[attr->model_number_len] = '\0';
+               }
+       }
+
+       if (attr->serial_number) {
+               os_free(sta->serial_number);
+               sta->serial_number = os_malloc(attr->serial_number_len + 1);
+               if (sta->serial_number) {
+                       os_memcpy(sta->serial_number, attr->serial_number,
+                                 attr->serial_number_len);
+                       sta->serial_number[attr->serial_number_len] = '\0';
+               }
+       }
+
+       if (attr->dev_name) {
+               os_free(sta->dev_name);
+               sta->dev_name = os_malloc(attr->dev_name_len + 1);
+               if (sta->dev_name) {
+                       os_memcpy(sta->dev_name, attr->dev_name,
+                                 attr->dev_name_len);
+                       sta->dev_name[attr->dev_name_len] = '\0';
+               }
+       }
+
+       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 */
+
+       return sta;
+}
+
+
+static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap,
+                                              const u8 *addr,
+                                              struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+
+       wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - Probe Request - from "
+                  MACSTR, MAC2STR(addr));
+       wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message "
+                       "(TLVs from Probe Request)", msg);
+
+       if (wps_parse_msg(msg, &attr) < 0) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in "
+                          "WLANEvent message");
+               return;
+       }
+
+       wps_er_add_sta_data(ap, addr, &attr, 1);
+}
+
+
+static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg)
+{
+       /* TODO: send msg as UPnP POST: PutWLANResponse(NewMessage,
+        * NewWLANEventType, NewWLANEventMAC) */
+
+       wpabuf_free(msg);
+}
+
+
+static void wps_er_sta_process(struct wps_er_sta *sta, struct wpabuf *msg)
+{
+       enum wps_process_res res;
+
+       res = wps_process_msg(sta->wps, WSC_MSG, 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);
+       }
+}
+
+
+static void wps_er_sta_start(struct wps_er_sta *sta, struct wpabuf *msg)
+{
+       struct wps_config cfg;
+
+       if (sta->wps)
+               wps_deinit(sta->wps);
+
+       os_memset(&cfg, 0, sizeof(cfg));
+       cfg.wps = sta->ap->er->wps;
+       cfg.registrar = 1;
+       cfg.peer_addr = sta->addr;
+
+       sta->wps = wps_init(&cfg);
+       if (sta->wps == NULL)
+               return;
+
+       wps_er_sta_process(sta, msg);
+}
+
+
+static void wps_er_process_wlanevent_eap(struct wps_er_ap *ap, const u8 *addr,
+                                        struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+       struct wps_er_sta *sta;
+
+       wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - EAP - from " MACSTR,
+                  MAC2STR(addr));
+       wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message "
+                       "(TLVs from EAP-WSC)", msg);
+
+       if (wps_parse_msg(msg, &attr) < 0) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in "
+                          "WLANEvent message");
+               return;
+       }
+
+       sta = wps_er_add_sta_data(ap, addr, &attr, 0);
+
+       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);
+}
+
+
+static void wps_er_process_wlanevent(struct wps_er_ap *ap,
+                                    struct wpabuf *event)
+{
+       u8 *data;
+       u8 wlan_event_type;
+       u8 wlan_event_mac[ETH_ALEN];
+       struct wpabuf msg;
+
+       wpa_hexdump(MSG_MSGDUMP, "WPS ER: Received WLANEvent",
+                   wpabuf_head(event), wpabuf_len(event));
+       if (wpabuf_len(event) < 1 + 17) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Too short WLANEvent");
+               return;
+       }
+
+       data = wpabuf_mhead(event);
+       wlan_event_type = data[0];
+       if (hwaddr_aton((char *) data + 1, wlan_event_mac) < 0) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Invalid WLANEventMAC in "
+                          "WLANEvent");
+               return;
+       }
+
+       wpabuf_set(&msg, data + 1 + 17, wpabuf_len(event) - (1 + 17));
+
+       switch (wlan_event_type) {
+       case 1:
+               wps_er_process_wlanevent_probe_req(ap, wlan_event_mac, &msg);
+               break;
+       case 2:
+               wps_er_process_wlanevent_eap(ap, wlan_event_mac, &msg);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "WPS ER: Unknown WLANEventType %d",
+                          wlan_event_type);
+               break;
+       }
+}
+
+
 static void wps_er_http_event(struct wps_er *er, struct http_request *req,
                              unsigned int ap_id)
 {
        struct wps_er_ap *ap = wps_er_ap_get_id(er, ap_id);
+       struct wpabuf *event;
+       enum http_reply_code ret;
+
        if (ap == NULL) {
                wpa_printf(MSG_DEBUG, "WPS ER: HTTP event from unknown AP id "
                           "%u", ap_id);
+               wps_er_http_resp_not_found(req);
                return;
        }
        wpa_printf(MSG_MSGDUMP, "WPS ER: HTTP event from AP id %u: %s",
                   ap_id, http_request_get_data(req));
-       /* TODO */
-       http_request_deinit(req);
+
+       event = xml_get_base64_item(http_request_get_data(req), "WLANEvent",
+                                   &ret);
+       if (event == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Could not extract WLANEvent "
+                          "from the event notification");
+               /*
+                * Reply with OK anyway to avoid getting unregistered from
+                * events.
+                */
+               wps_er_http_resp_ok(req);
+               return;
+       }
+
+       wps_er_process_wlanevent(ap, event);
+
+       wpabuf_free(event);
+       wps_er_http_resp_ok(req);
 }
 
 
@@ -514,7 +871,7 @@ static void wps_er_http_notify(struct wps_er *er, struct http_request *req)
        } else {
                wpa_printf(MSG_DEBUG, "WPS ER: Unknown HTTP NOTIFY for '%s'",
                           uri);
-               http_request_deinit(req);
+               wps_er_http_resp_not_found(req);
        }
 }
 
@@ -524,6 +881,8 @@ static void wps_er_http_req(void *ctx, struct http_request *req)
        struct wps_er *er = ctx;
        struct sockaddr_in *cli = http_request_get_cli_addr(req);
        enum httpread_hdr_type type = http_request_get_type(req);
+       struct wpabuf *buf;
+
        wpa_printf(MSG_DEBUG, "WPS ER: HTTP request: '%s' (type %d) from "
                   "%s:%d",
                   http_request_get_uri(req), type,
@@ -536,7 +895,17 @@ static void wps_er_http_req(void *ctx, struct http_request *req)
        default:
                wpa_printf(MSG_DEBUG, "WPS ER: Unsupported HTTP request type "
                           "%d", type);
-               http_request_deinit(req);
+               buf = wpabuf_alloc(200);
+               if (buf == NULL) {
+                       http_request_deinit(req);
+                       return;
+               }
+               wpabuf_put_str(buf,
+                              "HTTP/1.1 501 Unimplemented\r\n"
+                              "Connection: close\r\n");
+               http_put_date(buf);
+               wpabuf_put_str(buf, "\r\n");
+               http_request_send_and_deinit(req, buf);
                break;
        }
 }
@@ -557,16 +926,11 @@ wps_er_init(struct wps_context *wps, const char *ifname)
        er->ssdp_sd = -1;
 
        os_strlcpy(er->ifname, ifname, sizeof(er->ifname));
+       er->wps = wps;
        os_memset(&rcfg, 0, sizeof(rcfg));
        rcfg.pin_needed_cb = wps_er_pin_needed_cb;
        rcfg.cb_ctx = er;
 
-       er->reg = wps_registrar_init(wps, &rcfg);
-       if (er->reg == NULL) {
-               wps_er_deinit(er);
-               return NULL;
-       }
-
        if (get_netif_info(ifname,
                           &er->ip_addr, &er->ip_addr_text,
                           er->mac_addr, &er->mac_addr_text)) {
@@ -632,7 +996,6 @@ void wps_er_deinit(struct wps_er *er)
                eloop_unregister_sock(er->ssdp_sd, EVENT_TYPE_READ);
                close(er->ssdp_sd);
        }
-       wps_registrar_deinit(er->reg);
        os_free(er->ip_addr_text);
        os_free(er->mac_addr_text);
        os_free(er);