Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / src / wps / wps_upnp_web.c
index 250ad8e..d5b0b5b 100644 (file)
@@ -179,7 +179,8 @@ static const char *wps_device_xml_postfix =
 /* format_wps_device_xml -- produce content of "file" wps_device.xml
  * (UPNP_WPS_DEVICE_XML_FILE)
  */
-static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
+static void format_wps_device_xml(struct upnp_wps_device_interface *iface,
+                                 struct upnp_wps_device_sm *sm,
                                  struct wpabuf *buf)
 {
        const char *s;
@@ -191,38 +192,38 @@ static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
         * Add required fields with default values if not configured. Add
         * optional and recommended fields only if configured.
         */
-       s = sm->wps->friendly_name;
+       s = iface->wps->friendly_name;
        s = ((s && *s) ? s : "WPS Access Point");
        xml_add_tagged_data(buf, "friendlyName", s);
 
-       s = sm->wps->dev.manufacturer;
+       s = iface->wps->dev.manufacturer;
        s = ((s && *s) ? s : "");
        xml_add_tagged_data(buf, "manufacturer", s);
 
-       if (sm->wps->manufacturer_url)
+       if (iface->wps->manufacturer_url)
                xml_add_tagged_data(buf, "manufacturerURL",
-                                   sm->wps->manufacturer_url);
+                                   iface->wps->manufacturer_url);
 
-       if (sm->wps->model_description)
+       if (iface->wps->model_description)
                xml_add_tagged_data(buf, "modelDescription",
-                                   sm->wps->model_description);
+                                   iface->wps->model_description);
 
-       s = sm->wps->dev.model_name;
+       s = iface->wps->dev.model_name;
        s = ((s && *s) ? s : "");
        xml_add_tagged_data(buf, "modelName", s);
 
-       if (sm->wps->dev.model_number)
+       if (iface->wps->dev.model_number)
                xml_add_tagged_data(buf, "modelNumber",
-                                   sm->wps->dev.model_number);
+                                   iface->wps->dev.model_number);
 
-       if (sm->wps->model_url)
-               xml_add_tagged_data(buf, "modelURL", sm->wps->model_url);
+       if (iface->wps->model_url)
+               xml_add_tagged_data(buf, "modelURL", iface->wps->model_url);
 
-       if (sm->wps->dev.serial_number)
+       if (iface->wps->dev.serial_number)
                xml_add_tagged_data(buf, "serialNumber",
-                                   sm->wps->dev.serial_number);
+                                   iface->wps->dev.serial_number);
 
-       uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string));
+       uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
        s = uuid_string;
        /* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
         * easily...
@@ -231,8 +232,8 @@ static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
        xml_data_encode(buf, s, os_strlen(s));
        wpabuf_put_str(buf, "</UDN>\n");
 
-       if (sm->wps->upc)
-               xml_add_tagged_data(buf, "UPC", sm->wps->upc);
+       if (iface->wps->upc)
+               xml_add_tagged_data(buf, "UPC", iface->wps->upc);
 
        wpabuf_put_str(buf, wps_device_xml_postfix);
 }
@@ -311,27 +312,33 @@ static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
        size_t extra_len = 0;
        int body_length;
        char len_buf[10];
+       struct upnp_wps_device_interface *iface;
+
+       iface = dl_list_first(&sm->interfaces,
+                             struct upnp_wps_device_interface, list);
+       if (iface == NULL) {
+               http_request_deinit(hreq);
+               return;
+       }
 
        /*
         * It is not required that filenames be case insensitive but it is
         * allowed and cannot hurt here.
         */
-       if (filename == NULL)
-               filename = "(null)"; /* just in case */
        if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
                wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
                req = GET_DEVICE_XML_FILE;
                extra_len = 3000;
-               if (sm->wps->friendly_name)
-                       extra_len += os_strlen(sm->wps->friendly_name);
-               if (sm->wps->manufacturer_url)
-                       extra_len += os_strlen(sm->wps->manufacturer_url);
-               if (sm->wps->model_description)
-                       extra_len += os_strlen(sm->wps->model_description);
-               if (sm->wps->model_url)
-                       extra_len += os_strlen(sm->wps->model_url);
-               if (sm->wps->upc)
-                       extra_len += os_strlen(sm->wps->upc);
+               if (iface->wps->friendly_name)
+                       extra_len += os_strlen(iface->wps->friendly_name);
+               if (iface->wps->manufacturer_url)
+                       extra_len += os_strlen(iface->wps->manufacturer_url);
+               if (iface->wps->model_description)
+                       extra_len += os_strlen(iface->wps->model_description);
+               if (iface->wps->model_url)
+                       extra_len += os_strlen(iface->wps->model_url);
+               if (iface->wps->upc)
+                       extra_len += os_strlen(iface->wps->upc);
        } else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
                wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
                req = GET_SCPD_XML_FILE;
@@ -385,7 +392,7 @@ static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
 
        switch (req) {
        case GET_DEVICE_XML_FILE:
-               format_wps_device_xml(sm, buf);
+               format_wps_device_xml(iface, sm, buf);
                break;
        case GET_SCPD_XML_FILE:
                wpabuf_put_str(buf, wps_scpd_xml);
@@ -408,13 +415,19 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm,
 {
        static const char *name = "NewDeviceInfo";
        struct wps_config cfg;
-       struct upnp_wps_peer *peer = &sm->peer;
+       struct upnp_wps_device_interface *iface;
+       struct upnp_wps_peer *peer;
+
+       iface = dl_list_first(&sm->interfaces,
+                             struct upnp_wps_device_interface, list);
 
        wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
 
-       if (sm->ctx->ap_pin == NULL)
+       if (!iface || iface->ctx->ap_pin == NULL)
                return HTTP_INTERNAL_SERVER_ERROR;
 
+       peer = &iface->peer;
+
        /*
         * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS
         * registration over UPnP with the AP acting as an Enrollee. It should
@@ -427,9 +440,9 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm,
                wps_deinit(peer->wps);
 
        os_memset(&cfg, 0, sizeof(cfg));
-       cfg.wps = sm->wps;
-       cfg.pin = (u8 *) sm->ctx->ap_pin;
-       cfg.pin_len = os_strlen(sm->ctx->ap_pin);
+       cfg.wps = iface->wps;
+       cfg.pin = (u8 *) iface->ctx->ap_pin;
+       cfg.pin_len = os_strlen(iface->ctx->ap_pin);
        peer->wps = wps_init(&cfg);
        if (peer->wps) {
                enum wsc_op_code op_code;
@@ -458,6 +471,12 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
        enum http_reply_code ret;
        enum wps_process_res res;
        enum wsc_op_code op_code;
+       struct upnp_wps_device_interface *iface;
+
+       iface = dl_list_first(&sm->interfaces,
+                             struct upnp_wps_device_interface, list);
+       if (!iface)
+               return HTTP_INTERNAL_SERVER_ERROR;
 
        /*
         * PutMessage is used by external UPnP-based Registrar to perform WPS
@@ -468,11 +487,11 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
        msg = xml_get_base64_item(data, "NewInMessage", &ret);
        if (msg == NULL)
                return ret;
-       res = wps_process_msg(sm->peer.wps, WSC_UPnP, msg);
+       res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg);
        if (res == WPS_FAILURE)
                *reply = NULL;
        else
-               *reply = wps_get_msg(sm->peer.wps, &op_code);
+               *reply = wps_get_msg(iface->peer.wps, &op_code);
        wpabuf_free(msg);
        if (*reply == NULL)
                return HTTP_INTERNAL_SERVER_ERROR;
@@ -491,6 +510,8 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
        int ev_type;
        int type;
        char *val;
+       struct upnp_wps_device_interface *iface;
+       int ok = 0;
 
        /*
         * External UPnP-based Registrar is passing us a message to be proxied
@@ -559,9 +580,16 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
                wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
        } else
                type = -1;
-       if (!sm->ctx->rx_req_put_wlan_response ||
-           sm->ctx->rx_req_put_wlan_response(sm->priv, ev_type, macaddr, msg,
-                                             type)) {
+       dl_list_for_each(iface, &sm->interfaces,
+                        struct upnp_wps_device_interface, list) {
+               if (iface->ctx->rx_req_put_wlan_response &&
+                   iface->ctx->rx_req_put_wlan_response(iface->priv, ev_type,
+                                                        macaddr, msg, type)
+                   == 0)
+                       ok = 1;
+       }
+
+       if (!ok) {
                wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
                           "rx_req_put_wlan_response");
                wpabuf_free(msg);
@@ -606,6 +634,8 @@ web_process_set_selected_registrar(struct upnp_wps_device_sm *sm,
        struct wpabuf *msg;
        enum http_reply_code ret;
        struct subscription *s;
+       struct upnp_wps_device_interface *iface;
+       int err = 0;
 
        wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
        s = find_er(sm, cli);
@@ -617,11 +647,15 @@ web_process_set_selected_registrar(struct upnp_wps_device_sm *sm,
        msg = xml_get_base64_item(data, "NewMessage", &ret);
        if (msg == NULL)
                return ret;
-       if (upnp_er_set_selected_registrar(sm->wps->registrar, s, msg)) {
-               wpabuf_free(msg);
-               return HTTP_INTERNAL_SERVER_ERROR;
+       dl_list_for_each(iface, &sm->interfaces,
+                        struct upnp_wps_device_interface, list) {
+               if (upnp_er_set_selected_registrar(iface->wps->registrar, s,
+                                                  msg))
+                       err = 1;
        }
        wpabuf_free(msg);
+       if (err)
+               return HTTP_INTERNAL_SERVER_ERROR;
        *replyname = NULL;
        *reply = NULL;
        return HTTP_OK;
@@ -901,6 +935,9 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
                return;
        }
 
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE",
+                         (u8 *) hdr, os_strlen(hdr));
+
        /* Parse/validate headers */
        h = hdr;
        /* First line: SUBSCRIBE /wps_event HTTP/1.1
@@ -913,7 +950,7 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
        wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
        end = os_strchr(h, '\n');
 
-       for (; end != NULL; h = end + 1) {
+       while (end) {
                /* Option line by option line */
                h = end + 1;
                end = os_strchr(h, '\n');
@@ -921,7 +958,7 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
                        break; /* no unterminated lines allowed */
 
                /* NT assures that it is our type of subscription;
-                * not used for a renewl.
+                * not used for a renewal.
                 **/
                match = "NT:";
                match_len = os_strlen(match);
@@ -961,13 +998,13 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
                                h++;
                        len = end - h;
                        os_free(callback_urls);
-                       callback_urls = os_malloc(len + 1);
+                       callback_urls = dup_binstr(h, len);
                        if (callback_urls == NULL) {
                                ret = HTTP_INTERNAL_SERVER_ERROR;
                                goto error;
                        }
-                       os_memcpy(callback_urls, h, len);
-                       callback_urls[len] = 0;
+                       if (len > 0 && callback_urls[len - 1] == '\r')
+                               callback_urls[len - 1] = '\0';
                        continue;
                }
                /* SID is only for renewal */
@@ -1000,16 +1037,22 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
 
        if (got_uuid) {
                /* renewal */
+               wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewal");
                if (callback_urls) {
                        ret = HTTP_BAD_REQUEST;
                        goto error;
                }
                s = subscription_renew(sm, uuid);
                if (s == NULL) {
+                       char str[80];
+                       uuid_bin2str(uuid, str, sizeof(str));
+                       wpa_printf(MSG_DEBUG, "WPS UPnP: Could not find "
+                                  "SID %s", str);
                        ret = HTTP_PRECONDITION_FAILED;
                        goto error;
                }
        } else if (callback_urls) {
+               wpa_printf(MSG_DEBUG, "WPS UPnP: New subscription");
                if (!got_nt) {
                        ret = HTTP_PRECONDITION_FAILED;
                        goto error;
@@ -1033,6 +1076,7 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
        /* subscription id */
        b = wpabuf_put(buf, 0);
        uuid_bin2str(s->uuid, b, 80);
+       wpa_printf(MSG_DEBUG, "WPS UPnP: Assigned SID %s", b);
        wpabuf_put(buf, os_strlen(b));
        wpabuf_put_str(buf, "\r\n");
        wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
@@ -1066,6 +1110,7 @@ error:
        *     HTTP 500-series error code.
        *   599 Too many subscriptions (not a standard HTTP error)
        */
+       wpa_printf(MSG_DEBUG, "WPS UPnP: SUBSCRIBE failed - return %d", ret);
        http_put_empty(buf, ret);
        http_request_send_and_deinit(req, buf);
        os_free(callback_urls);
@@ -1114,7 +1159,7 @@ static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
        wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
        end = os_strchr(h, '\n');
 
-       for (; end != NULL; h = end + 1) {
+       while (end) {
                /* Option line by option line */
                h = end + 1;
                end = os_strchr(h, '\n');
@@ -1132,7 +1177,6 @@ static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
                        .....
                }
 #endif
-               /* SID is only for renewal */
                match = "SID:";
                match_len = os_strlen(match);
                if (os_strncasecmp(h, match, match_len) == 0) {
@@ -1155,19 +1199,44 @@ static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
                        got_uuid = 1;
                        continue;
                }
+
+               match = "NT:";
+               match_len = os_strlen(match);
+               if (os_strncasecmp(h, match, match_len) == 0) {
+                       ret = HTTP_BAD_REQUEST;
+                       goto send_msg;
+               }
+
+               match = "CALLBACK:";
+               match_len = os_strlen(match);
+               if (os_strncasecmp(h, match, match_len) == 0) {
+                       ret = HTTP_BAD_REQUEST;
+                       goto send_msg;
+               }
        }
 
        if (got_uuid) {
+               char str[80];
+
+               uuid_bin2str(uuid, str, sizeof(str));
+
                s = subscription_find(sm, uuid);
                if (s) {
                        struct subscr_addr *sa;
                        sa = dl_list_first(&s->addr_list, struct subscr_addr,
                                           list);
-                       wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s",
-                                  s, (sa && sa->domain_and_port) ?
+                       wpa_printf(MSG_DEBUG,
+                                  "WPS UPnP: Unsubscribing %p (SID %s) %s",
+                                  s, str, (sa && sa->domain_and_port) ?
                                   sa->domain_and_port : "-null-");
                        dl_list_del(&s->list);
                        subscription_destroy(s);
+               } else {
+                       wpa_printf(MSG_INFO,
+                                  "WPS UPnP: Could not find matching subscription to unsubscribe (SID %s)",
+                                  str);
+                       ret = HTTP_PRECONDITION_FAILED;
+                       goto send_msg;
                }
        } else {
                wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "