LCOV - code coverage report
Current view: top level - src/wps - wps_upnp_web.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1475438200 Lines: 575 595 96.6 %
Date: 2016-10-02 Functions: 21 21 100.0 %

          Line data    Source code
       1             : /*
       2             :  * UPnP WPS Device - Web connections
       3             :  * Copyright (c) 2000-2003 Intel Corporation
       4             :  * Copyright (c) 2006-2007 Sony Corporation
       5             :  * Copyright (c) 2008-2009 Atheros Communications
       6             :  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
       7             :  *
       8             :  * See wps_upnp.c for more details on licensing and code history.
       9             :  */
      10             : 
      11             : #include "includes.h"
      12             : 
      13             : #include "common.h"
      14             : #include "base64.h"
      15             : #include "uuid.h"
      16             : #include "httpread.h"
      17             : #include "http_server.h"
      18             : #include "wps_i.h"
      19             : #include "wps_upnp.h"
      20             : #include "wps_upnp_i.h"
      21             : #include "upnp_xml.h"
      22             : 
      23             : /***************************************************************************
      24             :  * Web connections (we serve pages of info about ourselves, handle
      25             :  * requests, etc. etc.).
      26             :  **************************************************************************/
      27             : 
      28             : #define WEB_CONNECTION_TIMEOUT_SEC 30   /* Drop web connection after t.o. */
      29             : #define WEB_CONNECTION_MAX_READ 8000    /* Max we'll read for TCP request */
      30             : #define MAX_WEB_CONNECTIONS 10          /* max simultaneous web connects */
      31             : 
      32             : 
      33             : static const char *urn_wfawlanconfig =
      34             :         "urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
      35             : static const char *http_server_hdr =
      36             :         "Server: unspecified, UPnP/1.0, unspecified\r\n";
      37             : static const char *http_connection_close =
      38             :         "Connection: close\r\n";
      39             : 
      40             : /*
      41             :  * "Files" that we serve via HTTP. The format of these files is given by
      42             :  * WFA WPS specifications. Extra white space has been removed to save space.
      43             :  */
      44             : 
      45             : static const char wps_scpd_xml[] =
      46             : "<?xml version=\"1.0\"?>\n"
      47             : "<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">\n"
      48             : "<specVersion><major>1</major><minor>0</minor></specVersion>\n"
      49             : "<actionList>\n"
      50             : "<action>\n"
      51             : "<name>GetDeviceInfo</name>\n"
      52             : "<argumentList>\n"
      53             : "<argument>\n"
      54             : "<name>NewDeviceInfo</name>\n"
      55             : "<direction>out</direction>\n"
      56             : "<relatedStateVariable>DeviceInfo</relatedStateVariable>\n"
      57             : "</argument>\n"
      58             : "</argumentList>\n"
      59             : "</action>\n"
      60             : "<action>\n"
      61             : "<name>PutMessage</name>\n"
      62             : "<argumentList>\n"
      63             : "<argument>\n"
      64             : "<name>NewInMessage</name>\n"
      65             : "<direction>in</direction>\n"
      66             : "<relatedStateVariable>InMessage</relatedStateVariable>\n"
      67             : "</argument>\n"
      68             : "<argument>\n"
      69             : "<name>NewOutMessage</name>\n"
      70             : "<direction>out</direction>\n"
      71             : "<relatedStateVariable>OutMessage</relatedStateVariable>\n"
      72             : "</argument>\n"
      73             : "</argumentList>\n"
      74             : "</action>\n"
      75             : "<action>\n"
      76             : "<name>PutWLANResponse</name>\n"
      77             : "<argumentList>\n"
      78             : "<argument>\n"
      79             : "<name>NewMessage</name>\n"
      80             : "<direction>in</direction>\n"
      81             : "<relatedStateVariable>Message</relatedStateVariable>\n"
      82             : "</argument>\n"
      83             : "<argument>\n"
      84             : "<name>NewWLANEventType</name>\n"
      85             : "<direction>in</direction>\n"
      86             : "<relatedStateVariable>WLANEventType</relatedStateVariable>\n"
      87             : "</argument>\n"
      88             : "<argument>\n"
      89             : "<name>NewWLANEventMAC</name>\n"
      90             : "<direction>in</direction>\n"
      91             : "<relatedStateVariable>WLANEventMAC</relatedStateVariable>\n"
      92             : "</argument>\n"
      93             : "</argumentList>\n"
      94             : "</action>\n"
      95             : "<action>\n"
      96             : "<name>SetSelectedRegistrar</name>\n"
      97             : "<argumentList>\n"
      98             : "<argument>\n"
      99             : "<name>NewMessage</name>\n"
     100             : "<direction>in</direction>\n"
     101             : "<relatedStateVariable>Message</relatedStateVariable>\n"
     102             : "</argument>\n"
     103             : "</argumentList>\n"
     104             : "</action>\n"
     105             : "</actionList>\n"
     106             : "<serviceStateTable>\n"
     107             : "<stateVariable sendEvents=\"no\">\n"
     108             : "<name>Message</name>\n"
     109             : "<dataType>bin.base64</dataType>\n"
     110             : "</stateVariable>\n"
     111             : "<stateVariable sendEvents=\"no\">\n"
     112             : "<name>InMessage</name>\n"
     113             : "<dataType>bin.base64</dataType>\n"
     114             : "</stateVariable>\n"
     115             : "<stateVariable sendEvents=\"no\">\n"
     116             : "<name>OutMessage</name>\n"
     117             : "<dataType>bin.base64</dataType>\n"
     118             : "</stateVariable>\n"
     119             : "<stateVariable sendEvents=\"no\">\n"
     120             : "<name>DeviceInfo</name>\n"
     121             : "<dataType>bin.base64</dataType>\n"
     122             : "</stateVariable>\n"
     123             : "<stateVariable sendEvents=\"yes\">\n"
     124             : "<name>APStatus</name>\n"
     125             : "<dataType>ui1</dataType>\n"
     126             : "</stateVariable>\n"
     127             : "<stateVariable sendEvents=\"yes\">\n"
     128             : "<name>STAStatus</name>\n"
     129             : "<dataType>ui1</dataType>\n"
     130             : "</stateVariable>\n"
     131             : "<stateVariable sendEvents=\"yes\">\n"
     132             : "<name>WLANEvent</name>\n"
     133             : "<dataType>bin.base64</dataType>\n"
     134             : "</stateVariable>\n"
     135             : "<stateVariable sendEvents=\"no\">\n"
     136             : "<name>WLANEventType</name>\n"
     137             : "<dataType>ui1</dataType>\n"
     138             : "</stateVariable>\n"
     139             : "<stateVariable sendEvents=\"no\">\n"
     140             : "<name>WLANEventMAC</name>\n"
     141             : "<dataType>string</dataType>\n"
     142             : "</stateVariable>\n"
     143             : "<stateVariable sendEvents=\"no\">\n"
     144             : "<name>WLANResponse</name>\n"
     145             : "<dataType>bin.base64</dataType>\n"
     146             : "</stateVariable>\n"
     147             : "</serviceStateTable>\n"
     148             : "</scpd>\n"
     149             : ;
     150             : 
     151             : 
     152             : static const char *wps_device_xml_prefix =
     153             :         "<?xml version=\"1.0\"?>\n"
     154             :         "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
     155             :         "<specVersion>\n"
     156             :         "<major>1</major>\n"
     157             :         "<minor>0</minor>\n"
     158             :         "</specVersion>\n"
     159             :         "<device>\n"
     160             :         "<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1"
     161             :         "</deviceType>\n";
     162             : 
     163             : static const char *wps_device_xml_postfix =
     164             :         "<serviceList>\n"
     165             :         "<service>\n"
     166             :         "<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1"
     167             :         "</serviceType>\n"
     168             :         "<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>"
     169             :         "\n"
     170             :         "<SCPDURL>" UPNP_WPS_SCPD_XML_FILE "</SCPDURL>\n"
     171             :         "<controlURL>" UPNP_WPS_DEVICE_CONTROL_FILE "</controlURL>\n"
     172             :         "<eventSubURL>" UPNP_WPS_DEVICE_EVENT_FILE "</eventSubURL>\n"
     173             :         "</service>\n"
     174             :         "</serviceList>\n"
     175             :         "</device>\n"
     176             :         "</root>\n";
     177             : 
     178             : 
     179             : /* format_wps_device_xml -- produce content of "file" wps_device.xml
     180             :  * (UPNP_WPS_DEVICE_XML_FILE)
     181             :  */
     182          32 : static void format_wps_device_xml(struct upnp_wps_device_interface *iface,
     183             :                                   struct upnp_wps_device_sm *sm,
     184             :                                   struct wpabuf *buf)
     185             : {
     186             :         const char *s;
     187             :         char uuid_string[80];
     188             : 
     189          32 :         wpabuf_put_str(buf, wps_device_xml_prefix);
     190             : 
     191             :         /*
     192             :          * Add required fields with default values if not configured. Add
     193             :          * optional and recommended fields only if configured.
     194             :          */
     195          32 :         s = iface->wps->friendly_name;
     196          32 :         s = ((s && *s) ? s : "WPS Access Point");
     197          32 :         xml_add_tagged_data(buf, "friendlyName", s);
     198             : 
     199          32 :         s = iface->wps->dev.manufacturer;
     200          32 :         s = ((s && *s) ? s : "");
     201          32 :         xml_add_tagged_data(buf, "manufacturer", s);
     202             : 
     203          32 :         if (iface->wps->manufacturer_url)
     204           8 :                 xml_add_tagged_data(buf, "manufacturerURL",
     205           8 :                                     iface->wps->manufacturer_url);
     206             : 
     207          32 :         if (iface->wps->model_description)
     208           8 :                 xml_add_tagged_data(buf, "modelDescription",
     209           8 :                                     iface->wps->model_description);
     210             : 
     211          32 :         s = iface->wps->dev.model_name;
     212          32 :         s = ((s && *s) ? s : "");
     213          32 :         xml_add_tagged_data(buf, "modelName", s);
     214             : 
     215          32 :         if (iface->wps->dev.model_number)
     216          32 :                 xml_add_tagged_data(buf, "modelNumber",
     217          32 :                                     iface->wps->dev.model_number);
     218             : 
     219          32 :         if (iface->wps->model_url)
     220           8 :                 xml_add_tagged_data(buf, "modelURL", iface->wps->model_url);
     221             : 
     222          32 :         if (iface->wps->dev.serial_number)
     223          32 :                 xml_add_tagged_data(buf, "serialNumber",
     224          32 :                                     iface->wps->dev.serial_number);
     225             : 
     226          32 :         uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
     227          32 :         s = uuid_string;
     228             :         /* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
     229             :          * easily...
     230             :          */
     231          32 :         wpabuf_put_str(buf, "<UDN>uuid:");
     232          32 :         xml_data_encode(buf, s, os_strlen(s));
     233          32 :         wpabuf_put_str(buf, "</UDN>\n");
     234             : 
     235          32 :         if (iface->wps->upc)
     236           8 :                 xml_add_tagged_data(buf, "UPC", iface->wps->upc);
     237             : 
     238          32 :         wpabuf_put_str(buf, wps_device_xml_postfix);
     239          32 : }
     240             : 
     241             : 
     242          97 : static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
     243             : {
     244          97 :         wpabuf_put_str(buf, "HTTP/1.1 ");
     245          97 :         switch (code) {
     246             :         case HTTP_OK:
     247          61 :                 wpabuf_put_str(buf, "200 OK\r\n");
     248          61 :                 break;
     249             :         case HTTP_BAD_REQUEST:
     250           8 :                 wpabuf_put_str(buf, "400 Bad request\r\n");
     251           8 :                 break;
     252             :         case HTTP_PRECONDITION_FAILED:
     253           8 :                 wpabuf_put_str(buf, "412 Precondition failed\r\n");
     254           8 :                 break;
     255             :         case HTTP_UNIMPLEMENTED:
     256          11 :                 wpabuf_put_str(buf, "501 Unimplemented\r\n");
     257          11 :                 break;
     258             :         case HTTP_INTERNAL_SERVER_ERROR:
     259             :         default:
     260           9 :                 wpabuf_put_str(buf, "500 Internal server error\r\n");
     261           9 :                 break;
     262             :         }
     263          97 : }
     264             : 
     265             : 
     266         256 : static void http_put_date(struct wpabuf *buf)
     267             : {
     268         256 :         wpabuf_put_str(buf, "Date: ");
     269         256 :         format_date(buf);
     270         256 :         wpabuf_put_str(buf, "\r\n");
     271         256 : }
     272             : 
     273             : 
     274          58 : static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
     275             : {
     276          58 :         http_put_reply_code(buf, code);
     277          58 :         wpabuf_put_str(buf, http_server_hdr);
     278          58 :         wpabuf_put_str(buf, http_connection_close);
     279          58 :         wpabuf_put_str(buf, "Content-Length: 0\r\n"
     280             :                        "\r\n");
     281          58 : }
     282             : 
     283             : 
     284             : /* Given that we have received a header w/ GET, act upon it
     285             :  *
     286             :  * Format of GET (case-insensitive):
     287             :  *
     288             :  * First line must be:
     289             :  *      GET /<file> HTTP/1.1
     290             :  * Since we don't do anything fancy we just ignore other lines.
     291             :  *
     292             :  * Our response (if no error) which includes only required lines is:
     293             :  * HTTP/1.1 200 OK
     294             :  * Connection: close
     295             :  * Content-Type: text/xml
     296             :  * Date: <rfc1123-date>
     297             :  *
     298             :  * Header lines must end with \r\n
     299             :  * Per RFC 2616, content-length: is not required but connection:close
     300             :  * would appear to be required (given that we will be closing it!).
     301             :  */
     302          37 : static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
     303             :                                      struct http_request *hreq,
     304             :                                      const char *filename)
     305             : {
     306             :         struct wpabuf *buf; /* output buffer, allocated */
     307             :         char *put_length_here;
     308             :         char *body_start;
     309             :         enum {
     310             :                 GET_DEVICE_XML_FILE,
     311             :                 GET_SCPD_XML_FILE
     312             :         } req;
     313          37 :         size_t extra_len = 0;
     314             :         int body_length;
     315             :         char len_buf[10];
     316             :         struct upnp_wps_device_interface *iface;
     317             : 
     318          37 :         iface = dl_list_first(&sm->interfaces,
     319             :                               struct upnp_wps_device_interface, list);
     320          37 :         if (iface == NULL) {
     321           0 :                 http_request_deinit(hreq);
     322           0 :                 return;
     323             :         }
     324             : 
     325             :         /*
     326             :          * It is not required that filenames be case insensitive but it is
     327             :          * allowed and cannot hurt here.
     328             :          */
     329          37 :         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
     330          33 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
     331          33 :                 req = GET_DEVICE_XML_FILE;
     332          33 :                 extra_len = 3000;
     333          33 :                 if (iface->wps->friendly_name)
     334          12 :                         extra_len += os_strlen(iface->wps->friendly_name);
     335          33 :                 if (iface->wps->manufacturer_url)
     336           9 :                         extra_len += os_strlen(iface->wps->manufacturer_url);
     337          33 :                 if (iface->wps->model_description)
     338           9 :                         extra_len += os_strlen(iface->wps->model_description);
     339          33 :                 if (iface->wps->model_url)
     340           9 :                         extra_len += os_strlen(iface->wps->model_url);
     341          33 :                 if (iface->wps->upc)
     342           9 :                         extra_len += os_strlen(iface->wps->upc);
     343           4 :         } else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
     344           1 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
     345           1 :                 req = GET_SCPD_XML_FILE;
     346           1 :                 extra_len = os_strlen(wps_scpd_xml);
     347             :         } else {
     348             :                 /* File not found */
     349           3 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s",
     350             :                            filename);
     351           3 :                 buf = wpabuf_alloc(200);
     352           3 :                 if (buf == NULL) {
     353           1 :                         http_request_deinit(hreq);
     354           1 :                         return;
     355             :                 }
     356           2 :                 wpabuf_put_str(buf,
     357             :                                "HTTP/1.1 404 Not Found\r\n"
     358             :                                "Connection: close\r\n");
     359             : 
     360           2 :                 http_put_date(buf);
     361             : 
     362             :                 /* terminating empty line */
     363           2 :                 wpabuf_put_str(buf, "\r\n");
     364             : 
     365           2 :                 goto send_buf;
     366             :         }
     367             : 
     368          34 :         buf = wpabuf_alloc(1000 + extra_len);
     369          34 :         if (buf == NULL) {
     370           1 :                 http_request_deinit(hreq);
     371           1 :                 return;
     372             :         }
     373             : 
     374          33 :         wpabuf_put_str(buf,
     375             :                        "HTTP/1.1 200 OK\r\n"
     376             :                        "Content-Type: text/xml; charset=\"utf-8\"\r\n");
     377          33 :         wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n");
     378          33 :         wpabuf_put_str(buf, "Connection: close\r\n");
     379          33 :         wpabuf_put_str(buf, "Content-Length: ");
     380             :         /*
     381             :          * We will paste the length in later, leaving some extra whitespace.
     382             :          * HTTP code is supposed to be tolerant of extra whitespace.
     383             :          */
     384          33 :         put_length_here = wpabuf_put(buf, 0);
     385          33 :         wpabuf_put_str(buf, "        \r\n");
     386             : 
     387          33 :         http_put_date(buf);
     388             : 
     389             :         /* terminating empty line */
     390          33 :         wpabuf_put_str(buf, "\r\n");
     391             : 
     392          33 :         body_start = wpabuf_put(buf, 0);
     393             : 
     394          33 :         switch (req) {
     395             :         case GET_DEVICE_XML_FILE:
     396          32 :                 format_wps_device_xml(iface, sm, buf);
     397          32 :                 break;
     398             :         case GET_SCPD_XML_FILE:
     399           1 :                 wpabuf_put_str(buf, wps_scpd_xml);
     400           1 :                 break;
     401             :         }
     402             : 
     403             :         /* Now patch in the content length at the end */
     404          33 :         body_length = (char *) wpabuf_put(buf, 0) - body_start;
     405          33 :         os_snprintf(len_buf, 10, "%d", body_length);
     406          33 :         os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
     407             : 
     408             : send_buf:
     409          35 :         http_request_send_and_deinit(hreq, buf);
     410             : }
     411             : 
     412             : 
     413          12 : static void wps_upnp_peer_del(struct upnp_wps_peer *peer)
     414             : {
     415          12 :         dl_list_del(&peer->list);
     416          12 :         if (peer->wps)
     417          12 :                 wps_deinit(peer->wps);
     418          12 :         os_free(peer);
     419          12 : }
     420             : 
     421             : 
     422             : static enum http_reply_code
     423          46 : web_process_get_device_info(struct upnp_wps_device_sm *sm,
     424             :                             struct wpabuf **reply, const char **replyname)
     425             : {
     426             :         static const char *name = "NewDeviceInfo";
     427             :         struct wps_config cfg;
     428             :         struct upnp_wps_device_interface *iface;
     429             :         struct upnp_wps_peer *peer;
     430             : 
     431          46 :         iface = dl_list_first(&sm->interfaces,
     432             :                               struct upnp_wps_device_interface, list);
     433             : 
     434          46 :         wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
     435             : 
     436          46 :         if (!iface || iface->ctx->ap_pin == NULL)
     437           0 :                 return HTTP_INTERNAL_SERVER_ERROR;
     438             : 
     439          46 :         peer = os_zalloc(sizeof(*peer));
     440          46 :         if (!peer)
     441           1 :                 return HTTP_INTERNAL_SERVER_ERROR;
     442             : 
     443             :         /*
     444             :          * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS
     445             :          * registration over UPnP with the AP acting as an Enrollee. It should
     446             :          * be noted that this is frequently used just to get the device data,
     447             :          * i.e., there may not be any intent to actually complete the
     448             :          * registration.
     449             :          */
     450             : 
     451          45 :         os_memset(&cfg, 0, sizeof(cfg));
     452          45 :         cfg.wps = iface->wps;
     453          45 :         cfg.pin = (u8 *) iface->ctx->ap_pin;
     454          45 :         cfg.pin_len = os_strlen(iface->ctx->ap_pin);
     455          45 :         peer->wps = wps_init(&cfg);
     456          45 :         if (peer->wps) {
     457             :                 enum wsc_op_code op_code;
     458          45 :                 *reply = wps_get_msg(peer->wps, &op_code);
     459          45 :                 if (*reply == NULL) {
     460           1 :                         wps_deinit(peer->wps);
     461           1 :                         peer->wps = NULL;
     462             :                 }
     463             :         } else
     464           0 :                 *reply = NULL;
     465          45 :         if (*reply == NULL) {
     466           1 :                 wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
     467           1 :                 os_free(peer);
     468           1 :                 return HTTP_INTERNAL_SERVER_ERROR;
     469             :         }
     470             : 
     471          44 :         if (dl_list_len(&iface->peers) > 3) {
     472             :                 struct upnp_wps_peer *old;
     473             : 
     474           3 :                 old = dl_list_first(&iface->peers, struct upnp_wps_peer, list);
     475           3 :                 if (old) {
     476           3 :                         wpa_printf(MSG_DEBUG, "WPS UPnP: Drop oldest active session");
     477           3 :                         wps_upnp_peer_del(old);
     478             :                 }
     479             :         }
     480          44 :         dl_list_add_tail(&iface->peers, &peer->list);
     481             :         /* TODO: Could schedule a timeout to free the entry */
     482             : 
     483          44 :         *replyname = name;
     484          44 :         return HTTP_OK;
     485             : }
     486             : 
     487             : 
     488             : static enum http_reply_code
     489          43 : web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
     490             :                         struct wpabuf **reply, const char **replyname)
     491             : {
     492             :         struct wpabuf *msg;
     493             :         static const char *name = "NewOutMessage";
     494             :         enum http_reply_code ret;
     495             :         enum wps_process_res res;
     496             :         enum wsc_op_code op_code;
     497             :         struct upnp_wps_device_interface *iface;
     498             :         struct wps_parse_attr attr;
     499             :         struct upnp_wps_peer *tmp, *peer;
     500             : 
     501          43 :         iface = dl_list_first(&sm->interfaces,
     502             :                               struct upnp_wps_device_interface, list);
     503          43 :         if (!iface)
     504           0 :                 return HTTP_INTERNAL_SERVER_ERROR;
     505             : 
     506             :         /*
     507             :          * PutMessage is used by external UPnP-based Registrar to perform WPS
     508             :          * operation with the access point itself; as compared with
     509             :          * PutWLANResponse which is for proxying.
     510             :          */
     511          43 :         wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
     512          43 :         msg = xml_get_base64_item(data, "NewInMessage", &ret);
     513          43 :         if (msg == NULL)
     514           1 :                 return ret;
     515             : 
     516          42 :         if (wps_parse_msg(msg, &attr)) {
     517           0 :                 wpa_printf(MSG_DEBUG,
     518             :                            "WPS UPnP: Could not parse PutMessage - NewInMessage");
     519           0 :                 wpabuf_free(msg);
     520           0 :                 return HTTP_BAD_REQUEST;
     521             :         }
     522             : 
     523             :         /* Find a matching active peer session */
     524          42 :         peer = NULL;
     525         104 :         dl_list_for_each(tmp, &iface->peers, struct upnp_wps_peer, list) {
     526         104 :                 if (!tmp->wps)
     527           0 :                         continue;
     528         208 :                 if (attr.enrollee_nonce &&
     529         104 :                     os_memcmp(tmp->wps->nonce_e, attr.enrollee_nonce,
     530             :                               WPS_NONCE_LEN) != 0)
     531          62 :                         continue; /* Enrollee nonce mismatch */
     532          84 :                 if (attr.msg_type &&
     533          72 :                     *attr.msg_type != WPS_M2 &&
     534          60 :                     *attr.msg_type != WPS_M2D &&
     535          39 :                     attr.registrar_nonce &&
     536           9 :                     os_memcmp(tmp->wps->nonce_r, attr.registrar_nonce,
     537             :                               WPS_NONCE_LEN) != 0)
     538           0 :                         continue; /* Registrar nonce mismatch */
     539          42 :                 peer = tmp;
     540          42 :                 break;
     541             :         }
     542          42 :         if (!peer) {
     543             :                 /*
     544             :                   Try to use the first entry in case message could work with
     545             :                  * it. The actual handler function will reject this, if needed.
     546             :                  * This maintains older behavior where only a single peer entry
     547             :                  * was supported.
     548             :                  */
     549           0 :                 peer = dl_list_first(&iface->peers, struct upnp_wps_peer, list);
     550             :         }
     551          42 :         if (!peer || !peer->wps) {
     552           0 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: No active peer entry found");
     553           0 :                 wpabuf_free(msg);
     554           0 :                 return HTTP_BAD_REQUEST;
     555             :         }
     556             : 
     557          42 :         res = wps_process_msg(peer->wps, WSC_UPnP, msg);
     558          42 :         if (res == WPS_FAILURE) {
     559           9 :                 *reply = NULL;
     560           9 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Drop active peer session");
     561           9 :                 wps_upnp_peer_del(peer);
     562             :         } else {
     563          33 :                 *reply = wps_get_msg(peer->wps, &op_code);
     564             :         }
     565          42 :         wpabuf_free(msg);
     566          42 :         if (*reply == NULL)
     567           9 :                 return HTTP_INTERNAL_SERVER_ERROR;
     568          33 :         *replyname = name;
     569          33 :         return HTTP_OK;
     570             : }
     571             : 
     572             : 
     573             : static enum http_reply_code
     574          46 : web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
     575             :                               struct wpabuf **reply, const char **replyname)
     576             : {
     577             :         struct wpabuf *msg;
     578             :         enum http_reply_code ret;
     579             :         u8 macaddr[ETH_ALEN];
     580             :         int ev_type;
     581             :         int type;
     582             :         char *val;
     583             :         struct upnp_wps_device_interface *iface;
     584          46 :         int ok = 0;
     585             : 
     586             :         /*
     587             :          * External UPnP-based Registrar is passing us a message to be proxied
     588             :          * over to a Wi-Fi -based client of ours.
     589             :          */
     590             : 
     591          46 :         wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
     592          46 :         msg = xml_get_base64_item(data, "NewMessage", &ret);
     593          46 :         if (msg == NULL) {
     594           1 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Could not extract NewMessage "
     595             :                            "from PutWLANResponse");
     596           1 :                 return ret;
     597             :         }
     598          45 :         val = xml_get_first_item(data, "NewWLANEventType");
     599          45 :         if (val == NULL) {
     600           1 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventType in "
     601             :                            "PutWLANResponse");
     602           1 :                 wpabuf_free(msg);
     603           1 :                 return UPNP_ARG_VALUE_INVALID;
     604             :         }
     605          44 :         ev_type = atol(val);
     606          44 :         os_free(val);
     607          44 :         val = xml_get_first_item(data, "NewWLANEventMAC");
     608          44 :         if (val == NULL) {
     609           1 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventMAC in "
     610             :                            "PutWLANResponse");
     611           1 :                 wpabuf_free(msg);
     612           1 :                 return UPNP_ARG_VALUE_INVALID;
     613             :         }
     614          43 :         if (hwaddr_aton(val, macaddr)) {
     615           2 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid NewWLANEventMAC in "
     616             :                            "PutWLANResponse: '%s'", val);
     617             : #ifdef CONFIG_WPS_STRICT
     618             :                 {
     619             :                         struct wps_parse_attr attr;
     620             :                         if (wps_parse_msg(msg, &attr) < 0 || attr.version2) {
     621             :                                 wpabuf_free(msg);
     622             :                                 os_free(val);
     623             :                                 return UPNP_ARG_VALUE_INVALID;
     624             :                         }
     625             :                 }
     626             : #endif /* CONFIG_WPS_STRICT */
     627           2 :                 if (hwaddr_aton2(val, macaddr) > 0) {
     628             :                         /*
     629             :                          * At least some versions of Intel PROset seem to be
     630             :                          * using dot-deliminated MAC address format here.
     631             :                          */
     632           6 :                         wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow "
     633             :                                    "incorrect MAC address format in "
     634             :                                    "NewWLANEventMAC: %s -> " MACSTR,
     635           6 :                                    val, MAC2STR(macaddr));
     636             :                 } else {
     637           1 :                         wpabuf_free(msg);
     638           1 :                         os_free(val);
     639           1 :                         return UPNP_ARG_VALUE_INVALID;
     640             :                 }
     641             :         }
     642          42 :         os_free(val);
     643          42 :         if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
     644             :                 struct wps_parse_attr attr;
     645          81 :                 if (wps_parse_msg(msg, &attr) < 0 ||
     646          40 :                     attr.msg_type == NULL)
     647           1 :                         type = -1;
     648             :                 else
     649          40 :                         type = *attr.msg_type;
     650          41 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
     651             :         } else
     652           1 :                 type = -1;
     653          84 :         dl_list_for_each(iface, &sm->interfaces,
     654             :                          struct upnp_wps_device_interface, list) {
     655          84 :                 if (iface->ctx->rx_req_put_wlan_response &&
     656          42 :                     iface->ctx->rx_req_put_wlan_response(iface->priv, ev_type,
     657             :                                                          macaddr, msg, type)
     658             :                     == 0)
     659          41 :                         ok = 1;
     660             :         }
     661             : 
     662          42 :         if (!ok) {
     663           1 :                 wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
     664             :                            "rx_req_put_wlan_response");
     665           1 :                 wpabuf_free(msg);
     666           1 :                 return HTTP_INTERNAL_SERVER_ERROR;
     667             :         }
     668          41 :         wpabuf_free(msg);
     669          41 :         *replyname = NULL;
     670          41 :         *reply = NULL;
     671          41 :         return HTTP_OK;
     672             : }
     673             : 
     674             : 
     675          36 : static int find_er_addr(struct subscription *s, struct sockaddr_in *cli)
     676             : {
     677             :         struct subscr_addr *a;
     678             : 
     679          36 :         dl_list_for_each(a, &s->addr_list, struct subscr_addr, list) {
     680          36 :                 if (cli->sin_addr.s_addr == a->saddr.sin_addr.s_addr)
     681          36 :                         return 1;
     682             :         }
     683           0 :         return 0;
     684             : }
     685             : 
     686             : 
     687          37 : static struct subscription * find_er(struct upnp_wps_device_sm *sm,
     688             :                                      struct sockaddr_in *cli)
     689             : {
     690             :         struct subscription *s;
     691          37 :         dl_list_for_each(s, &sm->subscriptions, struct subscription, list)
     692          36 :                 if (find_er_addr(s, cli))
     693          36 :                         return s;
     694           1 :         return NULL;
     695             : }
     696             : 
     697             : 
     698             : static enum http_reply_code
     699          37 : web_process_set_selected_registrar(struct upnp_wps_device_sm *sm,
     700             :                                    struct sockaddr_in *cli, char *data,
     701             :                                    struct wpabuf **reply,
     702             :                                    const char **replyname)
     703             : {
     704             :         struct wpabuf *msg;
     705             :         enum http_reply_code ret;
     706             :         struct subscription *s;
     707             :         struct upnp_wps_device_interface *iface;
     708          37 :         int err = 0;
     709             : 
     710          37 :         wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
     711          37 :         s = find_er(sm, cli);
     712          37 :         if (s == NULL) {
     713           1 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Ignore SetSelectedRegistrar "
     714             :                            "from unknown ER");
     715           1 :                 return UPNP_ACTION_FAILED;
     716             :         }
     717          36 :         msg = xml_get_base64_item(data, "NewMessage", &ret);
     718          36 :         if (msg == NULL)
     719           0 :                 return ret;
     720          72 :         dl_list_for_each(iface, &sm->interfaces,
     721             :                          struct upnp_wps_device_interface, list) {
     722          36 :                 if (upnp_er_set_selected_registrar(iface->wps->registrar, s,
     723             :                                                    msg))
     724           1 :                         err = 1;
     725             :         }
     726          36 :         wpabuf_free(msg);
     727          36 :         if (err)
     728           1 :                 return HTTP_INTERNAL_SERVER_ERROR;
     729          35 :         *replyname = NULL;
     730          35 :         *reply = NULL;
     731          35 :         return HTTP_OK;
     732             : }
     733             : 
     734             : 
     735             : static const char *soap_prefix =
     736             :         "<?xml version=\"1.0\"?>\n"
     737             :         "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
     738             :         "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
     739             :         "<s:Body>\n";
     740             : static const char *soap_postfix =
     741             :         "</s:Body>\n</s:Envelope>\n";
     742             : 
     743             : static const char *soap_error_prefix =
     744             :         "<s:Fault>\n"
     745             :         "<faultcode>s:Client</faultcode>\n"
     746             :         "<faultstring>UPnPError</faultstring>\n"
     747             :         "<detail>\n"
     748             :         "<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n";
     749             : static const char *soap_error_postfix =
     750             :         "<errorDescription>Error</errorDescription>\n"
     751             :         "</UPnPError>\n"
     752             :         "</detail>\n"
     753             :         "</s:Fault>\n";
     754             : 
     755         183 : static void web_connection_send_reply(struct http_request *req,
     756             :                                       enum http_reply_code ret,
     757             :                                       const char *action, int action_len,
     758             :                                       const struct wpabuf *reply,
     759             :                                       const char *replyname)
     760             : {
     761             :         struct wpabuf *buf;
     762             :         char *replydata;
     763         183 :         char *put_length_here = NULL;
     764         183 :         char *body_start = NULL;
     765             : 
     766         183 :         if (reply) {
     767             :                 size_t len;
     768          77 :                 replydata = (char *) base64_encode(wpabuf_head(reply),
     769             :                                                    wpabuf_len(reply), &len);
     770             :         } else
     771         106 :                 replydata = NULL;
     772             : 
     773             :         /* Parameters of the response:
     774             :          *      action(action_len) -- action we are responding to
     775             :          *      replyname -- a name we need for the reply
     776             :          *      replydata -- NULL or null-terminated string
     777             :          */
     778         356 :         buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
     779         173 :                            (action_len > 0 ? action_len * 2 : 0));
     780         183 :         if (buf == NULL) {
     781           1 :                 wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to "
     782             :                            "POST");
     783           1 :                 os_free(replydata);
     784           1 :                 http_request_deinit(req);
     785         184 :                 return;
     786             :         }
     787             : 
     788             :         /*
     789             :          * Assuming we will be successful, put in the output header first.
     790             :          * Note: we do not keep connections alive (and httpread does
     791             :          * not support it)... therefore we must have Connection: close.
     792             :          */
     793         182 :         if (ret == HTTP_OK) {
     794         152 :                 wpabuf_put_str(buf,
     795             :                                "HTTP/1.1 200 OK\r\n"
     796             :                                "Content-Type: text/xml; "
     797             :                                "charset=\"utf-8\"\r\n");
     798             :         } else {
     799          30 :                 wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
     800             :         }
     801         182 :         wpabuf_put_str(buf, http_connection_close);
     802             : 
     803         182 :         wpabuf_put_str(buf, "Content-Length: ");
     804             :         /*
     805             :          * We will paste the length in later, leaving some extra whitespace.
     806             :          * HTTP code is supposed to be tolerant of extra whitespace.
     807             :          */
     808         182 :         put_length_here = wpabuf_put(buf, 0);
     809         182 :         wpabuf_put_str(buf, "        \r\n");
     810             : 
     811         182 :         http_put_date(buf);
     812             : 
     813             :         /* terminating empty line */
     814         182 :         wpabuf_put_str(buf, "\r\n");
     815             : 
     816         182 :         body_start = wpabuf_put(buf, 0);
     817             : 
     818         182 :         if (ret == HTTP_OK) {
     819         152 :                 wpabuf_put_str(buf, soap_prefix);
     820         152 :                 wpabuf_put_str(buf, "<u:");
     821         152 :                 wpabuf_put_data(buf, action, action_len);
     822         152 :                 wpabuf_put_str(buf, "Response xmlns:u=\"");
     823         152 :                 wpabuf_put_str(buf, urn_wfawlanconfig);
     824         152 :                 wpabuf_put_str(buf, "\">\n");
     825         152 :                 if (replydata && replyname) {
     826             :                         /* TODO: might possibly need to escape part of reply
     827             :                          * data? ...
     828             :                          * probably not, unlikely to have ampersand(&) or left
     829             :                          * angle bracket (<) in it...
     830             :                          */
     831          76 :                         wpabuf_printf(buf, "<%s>", replyname);
     832          76 :                         wpabuf_put_str(buf, replydata);
     833          76 :                         wpabuf_printf(buf, "</%s>\n", replyname);
     834             :                 }
     835         152 :                 wpabuf_put_str(buf, "</u:");
     836         152 :                 wpabuf_put_data(buf, action, action_len);
     837         152 :                 wpabuf_put_str(buf, "Response>\n");
     838         152 :                 wpabuf_put_str(buf, soap_postfix);
     839             :         } else {
     840             :                 /* Error case */
     841          30 :                 wpabuf_put_str(buf, soap_prefix);
     842          30 :                 wpabuf_put_str(buf, soap_error_prefix);
     843          30 :                 wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
     844          30 :                 wpabuf_put_str(buf, soap_error_postfix);
     845          30 :                 wpabuf_put_str(buf, soap_postfix);
     846             :         }
     847         182 :         os_free(replydata);
     848             : 
     849             :         /* Now patch in the content length at the end */
     850         182 :         if (body_start && put_length_here) {
     851         182 :                 int body_length = (char *) wpabuf_put(buf, 0) - body_start;
     852             :                 char len_buf[10];
     853         182 :                 os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
     854         182 :                 os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
     855             :         }
     856             : 
     857         182 :         http_request_send_and_deinit(req, buf);
     858             : }
     859             : 
     860             : 
     861         179 : static const char * web_get_action(struct http_request *req,
     862             :                                    size_t *action_len)
     863             : {
     864             :         const char *match;
     865             :         int match_len;
     866             :         char *b;
     867             :         char *action;
     868             : 
     869         179 :         *action_len = 0;
     870             :         /* The SOAPAction line of the header tells us what we want to do */
     871         179 :         b = http_request_get_hdr_line(req, "SOAPAction:");
     872         179 :         if (b == NULL)
     873           1 :                 return NULL;
     874         178 :         if (*b == '"')
     875         176 :                 b++;
     876             :         else
     877           2 :                 return NULL;
     878         176 :         match = urn_wfawlanconfig;
     879         176 :         match_len = os_strlen(urn_wfawlanconfig) - 1;
     880         176 :         if (os_strncasecmp(b, match, match_len))
     881           1 :                 return NULL;
     882         175 :         b += match_len;
     883             :         /* skip over version */
     884         528 :         while (isgraph(*b) && *b != '#')
     885         178 :                 b++;
     886         175 :         if (*b != '#')
     887           1 :                 return NULL;
     888         174 :         b++;
     889             :         /* Following the sharp(#) should be the action and a double quote */
     890         174 :         action = b;
     891        2822 :         while (isgraph(*b) && *b != '"')
     892        2474 :                 b++;
     893         174 :         if (*b != '"')
     894           1 :                 return NULL;
     895         173 :         *action_len = b - action;
     896         173 :         return action;
     897             : }
     898             : 
     899             : 
     900             : /* Given that we have received a header w/ POST, act upon it
     901             :  *
     902             :  * Format of POST (case-insensitive):
     903             :  *
     904             :  * First line must be:
     905             :  *      POST /<file> HTTP/1.1
     906             :  * Since we don't do anything fancy we just ignore other lines.
     907             :  *
     908             :  * Our response (if no error) which includes only required lines is:
     909             :  * HTTP/1.1 200 OK
     910             :  * Connection: close
     911             :  * Content-Type: text/xml
     912             :  * Date: <rfc1123-date>
     913             :  *
     914             :  * Header lines must end with \r\n
     915             :  * Per RFC 2616, content-length: is not required but connection:close
     916             :  * would appear to be required (given that we will be closing it!).
     917             :  */
     918         183 : static void web_connection_parse_post(struct upnp_wps_device_sm *sm,
     919             :                                       struct sockaddr_in *cli,
     920             :                                       struct http_request *req,
     921             :                                       const char *filename)
     922             : {
     923             :         enum http_reply_code ret;
     924         183 :         char *data = http_request_get_data(req); /* body of http msg */
     925         183 :         const char *action = NULL;
     926         183 :         size_t action_len = 0;
     927         183 :         const char *replyname = NULL; /* argument name for the reply */
     928         183 :         struct wpabuf *reply = NULL; /* data for the reply */
     929             : 
     930         183 :         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
     931           4 :                 wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
     932             :                            filename);
     933           4 :                 ret = HTTP_NOT_FOUND;
     934           4 :                 goto bad;
     935             :         }
     936             : 
     937         179 :         ret = UPNP_INVALID_ACTION;
     938         179 :         action = web_get_action(req, &action_len);
     939         179 :         if (action == NULL)
     940           6 :                 goto bad;
     941             : 
     942         173 :         if (!os_strncasecmp("GetDeviceInfo", action, action_len))
     943          46 :                 ret = web_process_get_device_info(sm, &reply, &replyname);
     944         127 :         else if (!os_strncasecmp("PutMessage", action, action_len))
     945          43 :                 ret = web_process_put_message(sm, data, &reply, &replyname);
     946          84 :         else if (!os_strncasecmp("PutWLANResponse", action, action_len))
     947          46 :                 ret = web_process_put_wlan_response(sm, data, &reply,
     948             :                                                     &replyname);
     949          38 :         else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
     950          37 :                 ret = web_process_set_selected_registrar(sm, cli, data, &reply,
     951             :                                                          &replyname);
     952             :         else
     953           1 :                 wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type");
     954             : 
     955             : bad:
     956         183 :         if (ret != HTTP_OK)
     957          30 :                 wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
     958         183 :         web_connection_send_reply(req, ret, action, action_len, reply,
     959             :                                   replyname);
     960         183 :         wpabuf_free(reply);
     961         183 : }
     962             : 
     963             : 
     964             : /* Given that we have received a header w/ SUBSCRIBE, act upon it
     965             :  *
     966             :  * Format of SUBSCRIBE (case-insensitive):
     967             :  *
     968             :  * First line must be:
     969             :  *      SUBSCRIBE /wps_event HTTP/1.1
     970             :  *
     971             :  * Our response (if no error) which includes only required lines is:
     972             :  * HTTP/1.1 200 OK
     973             :  * Server: xx, UPnP/1.0, xx
     974             :  * SID: uuid:xxxxxxxxx
     975             :  * Timeout: Second-<n>
     976             :  * Content-Length: 0
     977             :  * Date: xxxx
     978             :  *
     979             :  * Header lines must end with \r\n
     980             :  * Per RFC 2616, content-length: is not required but connection:close
     981             :  * would appear to be required (given that we will be closing it!).
     982             :  */
     983          57 : static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
     984             :                                            struct http_request *req,
     985             :                                            const char *filename)
     986             : {
     987             :         struct wpabuf *buf;
     988             :         char *b;
     989          57 :         char *hdr = http_request_get_hdr(req);
     990             :         char *h;
     991             :         char *match;
     992             :         int match_len;
     993             :         char *end;
     994             :         int len;
     995          57 :         int got_nt = 0;
     996             :         u8 uuid[UUID_LEN];
     997          57 :         int got_uuid = 0;
     998          57 :         char *callback_urls = NULL;
     999          57 :         struct subscription *s = NULL;
    1000          57 :         enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
    1001             : 
    1002          57 :         buf = wpabuf_alloc(1000);
    1003          57 :         if (buf == NULL) {
    1004           1 :                 http_request_deinit(req);
    1005           1 :                 return;
    1006             :         }
    1007             : 
    1008          56 :         wpa_hexdump_ascii(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE",
    1009             :                           (u8 *) hdr, os_strlen(hdr));
    1010             : 
    1011             :         /* Parse/validate headers */
    1012          56 :         h = hdr;
    1013             :         /* First line: SUBSCRIBE /wps_event HTTP/1.1
    1014             :          * has already been parsed.
    1015             :          */
    1016          56 :         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
    1017           1 :                 ret = HTTP_PRECONDITION_FAILED;
    1018           1 :                 goto error;
    1019             :         }
    1020          55 :         wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
    1021          55 :         end = os_strchr(h, '\n');
    1022             : 
    1023         437 :         while (end) {
    1024             :                 /* Option line by option line */
    1025         382 :                 h = end + 1;
    1026         382 :                 end = os_strchr(h, '\n');
    1027         382 :                 if (end == NULL)
    1028          51 :                         break; /* no unterminated lines allowed */
    1029             : 
    1030             :                 /* NT assures that it is our type of subscription;
    1031             :                  * not used for a renewal.
    1032             :                  **/
    1033         331 :                 match = "NT:";
    1034         331 :                 match_len = os_strlen(match);
    1035         331 :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1036          53 :                         h += match_len;
    1037         159 :                         while (*h == ' ' || *h == '\t')
    1038          53 :                                 h++;
    1039          53 :                         match = "upnp:event";
    1040          53 :                         match_len = os_strlen(match);
    1041          53 :                         if (os_strncasecmp(h, match, match_len) != 0) {
    1042           1 :                                 ret = HTTP_BAD_REQUEST;
    1043           1 :                                 goto error;
    1044             :                         }
    1045          52 :                         got_nt = 1;
    1046          52 :                         continue;
    1047             :                 }
    1048             :                 /* HOST should refer to us */
    1049             : #if 0
    1050             :                 match = "HOST:";
    1051             :                 match_len = os_strlen(match);
    1052             :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1053             :                         h += match_len;
    1054             :                         while (*h == ' ' || *h == '\t')
    1055             :                                 h++;
    1056             :                         .....
    1057             :                 }
    1058             : #endif
    1059             :                 /* CALLBACK gives one or more URLs for NOTIFYs
    1060             :                  * to be sent as a result of the subscription.
    1061             :                  * Each URL is enclosed in angle brackets.
    1062             :                  */
    1063         278 :                 match = "CALLBACK:";
    1064         278 :                 match_len = os_strlen(match);
    1065         278 :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1066          49 :                         h += match_len;
    1067         148 :                         while (*h == ' ' || *h == '\t')
    1068          50 :                                 h++;
    1069          49 :                         len = end - h;
    1070          49 :                         os_free(callback_urls);
    1071          49 :                         callback_urls = dup_binstr(h, len);
    1072          49 :                         if (callback_urls == NULL) {
    1073           1 :                                 ret = HTTP_INTERNAL_SERVER_ERROR;
    1074           1 :                                 goto error;
    1075             :                         }
    1076          48 :                         if (len > 0 && callback_urls[len - 1] == '\r')
    1077          48 :                                 callback_urls[len - 1] = '\0';
    1078          48 :                         continue;
    1079             :                 }
    1080             :                 /* SID is only for renewal */
    1081         229 :                 match = "SID:";
    1082         229 :                 match_len = os_strlen(match);
    1083         229 :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1084           6 :                         h += match_len;
    1085          18 :                         while (*h == ' ' || *h == '\t')
    1086           6 :                                 h++;
    1087           6 :                         match = "uuid:";
    1088           6 :                         match_len = os_strlen(match);
    1089           6 :                         if (os_strncasecmp(h, match, match_len) != 0) {
    1090           1 :                                 ret = HTTP_BAD_REQUEST;
    1091           1 :                                 goto error;
    1092             :                         }
    1093           5 :                         h += match_len;
    1094          14 :                         while (*h == ' ' || *h == '\t')
    1095           4 :                                 h++;
    1096           5 :                         if (uuid_str2bin(h, uuid)) {
    1097           1 :                                 ret = HTTP_BAD_REQUEST;
    1098           1 :                                 goto error;
    1099             :                         }
    1100           4 :                         got_uuid = 1;
    1101           4 :                         continue;
    1102             :                 }
    1103             :                 /* TIMEOUT is requested timeout, but apparently we can
    1104             :                  * just ignore this.
    1105             :                  */
    1106             :         }
    1107             : 
    1108          51 :         if (got_uuid) {
    1109             :                 /* renewal */
    1110           4 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewal");
    1111           4 :                 if (callback_urls) {
    1112           1 :                         ret = HTTP_BAD_REQUEST;
    1113           1 :                         goto error;
    1114             :                 }
    1115           3 :                 s = subscription_renew(sm, uuid);
    1116           3 :                 if (s == NULL) {
    1117             :                         char str[80];
    1118           1 :                         uuid_bin2str(uuid, str, sizeof(str));
    1119           1 :                         wpa_printf(MSG_DEBUG, "WPS UPnP: Could not find "
    1120             :                                    "SID %s", str);
    1121           1 :                         ret = HTTP_PRECONDITION_FAILED;
    1122           1 :                         goto error;
    1123             :                 }
    1124          47 :         } else if (callback_urls) {
    1125          46 :                 wpa_printf(MSG_DEBUG, "WPS UPnP: New subscription");
    1126          46 :                 if (!got_nt) {
    1127           1 :                         ret = HTTP_PRECONDITION_FAILED;
    1128           1 :                         goto error;
    1129             :                 }
    1130          45 :                 s = subscription_start(sm, callback_urls);
    1131          45 :                 if (s == NULL) {
    1132           8 :                         ret = HTTP_INTERNAL_SERVER_ERROR;
    1133           8 :                         goto error;
    1134             :                 }
    1135             :         } else {
    1136           1 :                 ret = HTTP_PRECONDITION_FAILED;
    1137           1 :                 goto error;
    1138             :         }
    1139             : 
    1140             :         /* success */
    1141          39 :         http_put_reply_code(buf, HTTP_OK);
    1142          39 :         wpabuf_put_str(buf, http_server_hdr);
    1143          39 :         wpabuf_put_str(buf, http_connection_close);
    1144          39 :         wpabuf_put_str(buf, "Content-Length: 0\r\n");
    1145          39 :         wpabuf_put_str(buf, "SID: uuid:");
    1146             :         /* subscription id */
    1147          39 :         b = wpabuf_put(buf, 0);
    1148          39 :         uuid_bin2str(s->uuid, b, 80);
    1149          39 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Assigned SID %s", b);
    1150          39 :         wpabuf_put(buf, os_strlen(b));
    1151          39 :         wpabuf_put_str(buf, "\r\n");
    1152          39 :         wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
    1153          39 :         http_put_date(buf);
    1154             :         /* And empty line to terminate header: */
    1155          39 :         wpabuf_put_str(buf, "\r\n");
    1156             : 
    1157          39 :         os_free(callback_urls);
    1158          39 :         http_request_send_and_deinit(req, buf);
    1159          39 :         return;
    1160             : 
    1161             : error:
    1162             :         /* Per UPnP spec:
    1163             :         * Errors
    1164             :         * Incompatible headers
    1165             :         *   400 Bad Request. If SID header and one of NT or CALLBACK headers
    1166             :         *     are present, the publisher must respond with HTTP error
    1167             :         *     400 Bad Request.
    1168             :         * Missing or invalid CALLBACK
    1169             :         *   412 Precondition Failed. If CALLBACK header is missing or does not
    1170             :         *     contain a valid HTTP URL, the publisher must respond with HTTP
    1171             :         *     error 412 Precondition Failed.
    1172             :         * Invalid NT
    1173             :         *   412 Precondition Failed. If NT header does not equal upnp:event,
    1174             :         *     the publisher must respond with HTTP error 412 Precondition
    1175             :         *     Failed.
    1176             :         * [For resubscription, use 412 if unknown uuid].
    1177             :         * Unable to accept subscription
    1178             :         *   5xx. If a publisher is not able to accept a subscription (such as
    1179             :         *     due to insufficient resources), it must respond with a
    1180             :         *     HTTP 500-series error code.
    1181             :         *   599 Too many subscriptions (not a standard HTTP error)
    1182             :         */
    1183          17 :         wpa_printf(MSG_DEBUG, "WPS UPnP: SUBSCRIBE failed - return %d", ret);
    1184          17 :         http_put_empty(buf, ret);
    1185          17 :         http_request_send_and_deinit(req, buf);
    1186          17 :         os_free(callback_urls);
    1187             : }
    1188             : 
    1189             : 
    1190             : /* Given that we have received a header w/ UNSUBSCRIBE, act upon it
    1191             :  *
    1192             :  * Format of UNSUBSCRIBE (case-insensitive):
    1193             :  *
    1194             :  * First line must be:
    1195             :  *      UNSUBSCRIBE /wps_event HTTP/1.1
    1196             :  *
    1197             :  * Our response (if no error) which includes only required lines is:
    1198             :  * HTTP/1.1 200 OK
    1199             :  * Content-Length: 0
    1200             :  *
    1201             :  * Header lines must end with \r\n
    1202             :  * Per RFC 2616, content-length: is not required but connection:close
    1203             :  * would appear to be required (given that we will be closing it!).
    1204             :  */
    1205          31 : static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
    1206             :                                              struct http_request *req,
    1207             :                                              const char *filename)
    1208             : {
    1209             :         struct wpabuf *buf;
    1210          31 :         char *hdr = http_request_get_hdr(req);
    1211             :         char *h;
    1212             :         char *match;
    1213             :         int match_len;
    1214             :         char *end;
    1215             :         u8 uuid[UUID_LEN];
    1216          31 :         int got_uuid = 0;
    1217          31 :         struct subscription *s = NULL;
    1218          31 :         enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
    1219             : 
    1220             :         /* Parse/validate headers */
    1221          31 :         h = hdr;
    1222             :         /* First line: UNSUBSCRIBE /wps_event HTTP/1.1
    1223             :          * has already been parsed.
    1224             :          */
    1225          31 :         if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
    1226           1 :                 ret = HTTP_PRECONDITION_FAILED;
    1227           1 :                 goto send_msg;
    1228             :         }
    1229          30 :         wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
    1230          30 :         end = os_strchr(h, '\n');
    1231             : 
    1232         160 :         while (end) {
    1233             :                 /* Option line by option line */
    1234         130 :                 h = end + 1;
    1235         130 :                 end = os_strchr(h, '\n');
    1236         130 :                 if (end == NULL)
    1237          25 :                         break; /* no unterminated lines allowed */
    1238             : 
    1239             :                 /* HOST should refer to us */
    1240             : #if 0
    1241             :                 match = "HOST:";
    1242             :                 match_len = os_strlen(match);
    1243             :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1244             :                         h += match_len;
    1245             :                         while (*h == ' ' || *h == '\t')
    1246             :                                 h++;
    1247             :                         .....
    1248             :                 }
    1249             : #endif
    1250         105 :                 match = "SID:";
    1251         105 :                 match_len = os_strlen(match);
    1252         105 :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1253          26 :                         h += match_len;
    1254          82 :                         while (*h == ' ' || *h == '\t')
    1255          30 :                                 h++;
    1256          26 :                         match = "uuid:";
    1257          26 :                         match_len = os_strlen(match);
    1258          26 :                         if (os_strncasecmp(h, match, match_len) != 0) {
    1259           1 :                                 ret = HTTP_BAD_REQUEST;
    1260           1 :                                 goto send_msg;
    1261             :                         }
    1262          25 :                         h += match_len;
    1263          53 :                         while (*h == ' ' || *h == '\t')
    1264           3 :                                 h++;
    1265          25 :                         if (uuid_str2bin(h, uuid)) {
    1266           1 :                                 ret = HTTP_BAD_REQUEST;
    1267           1 :                                 goto send_msg;
    1268             :                         }
    1269          24 :                         got_uuid = 1;
    1270          24 :                         continue;
    1271             :                 }
    1272             : 
    1273          79 :                 match = "NT:";
    1274          79 :                 match_len = os_strlen(match);
    1275          79 :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1276           1 :                         ret = HTTP_BAD_REQUEST;
    1277           1 :                         goto send_msg;
    1278             :                 }
    1279             : 
    1280          78 :                 match = "CALLBACK:";
    1281          78 :                 match_len = os_strlen(match);
    1282          78 :                 if (os_strncasecmp(h, match, match_len) == 0) {
    1283           2 :                         ret = HTTP_BAD_REQUEST;
    1284           2 :                         goto send_msg;
    1285             :                 }
    1286             :         }
    1287             : 
    1288          25 :         if (got_uuid) {
    1289             :                 char str[80];
    1290             : 
    1291          24 :                 uuid_bin2str(uuid, str, sizeof(str));
    1292             : 
    1293          24 :                 s = subscription_find(sm, uuid);
    1294          24 :                 if (s) {
    1295             :                         struct subscr_addr *sa;
    1296          22 :                         sa = dl_list_first(&s->addr_list, struct subscr_addr,
    1297             :                                            list);
    1298          44 :                         wpa_printf(MSG_DEBUG,
    1299             :                                    "WPS UPnP: Unsubscribing %p (SID %s) %s",
    1300          22 :                                    s, str, (sa && sa->domain_and_port) ?
    1301             :                                    sa->domain_and_port : "-null-");
    1302          22 :                         dl_list_del(&s->list);
    1303          22 :                         subscription_destroy(s);
    1304             :                 } else {
    1305           2 :                         wpa_printf(MSG_INFO,
    1306             :                                    "WPS UPnP: Could not find matching subscription to unsubscribe (SID %s)",
    1307             :                                    str);
    1308           2 :                         ret = HTTP_PRECONDITION_FAILED;
    1309           2 :                         goto send_msg;
    1310             :                 }
    1311             :         } else {
    1312           1 :                 wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
    1313             :                            "found)");
    1314           1 :                 ret = HTTP_PRECONDITION_FAILED;
    1315           1 :                 goto send_msg;
    1316             :         }
    1317             : 
    1318          22 :         ret = HTTP_OK;
    1319             : 
    1320             : send_msg:
    1321          31 :         buf = wpabuf_alloc(200);
    1322          31 :         if (buf == NULL) {
    1323           1 :                 http_request_deinit(req);
    1324          32 :                 return;
    1325             :         }
    1326          30 :         http_put_empty(buf, ret);
    1327          30 :         http_request_send_and_deinit(req, buf);
    1328             : }
    1329             : 
    1330             : 
    1331             : /* Send error in response to unknown requests */
    1332          12 : static void web_connection_unimplemented(struct http_request *req)
    1333             : {
    1334             :         struct wpabuf *buf;
    1335          12 :         buf = wpabuf_alloc(200);
    1336          12 :         if (buf == NULL) {
    1337           1 :                 http_request_deinit(req);
    1338          13 :                 return;
    1339             :         }
    1340          11 :         http_put_empty(buf, HTTP_UNIMPLEMENTED);
    1341          11 :         http_request_send_and_deinit(req, buf);
    1342             : }
    1343             : 
    1344             : 
    1345             : 
    1346             : /* Called when we have gotten an apparently valid http request.
    1347             :  */
    1348         320 : static void web_connection_check_data(void *ctx, struct http_request *req)
    1349             : {
    1350         320 :         struct upnp_wps_device_sm *sm = ctx;
    1351         320 :         enum httpread_hdr_type htype = http_request_get_type(req);
    1352         320 :         char *filename = http_request_get_uri(req);
    1353         320 :         struct sockaddr_in *cli = http_request_get_cli_addr(req);
    1354             : 
    1355         320 :         if (!filename) {
    1356           0 :                 wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
    1357           0 :                 http_request_deinit(req);
    1358         320 :                 return;
    1359             :         }
    1360             :         /* Trim leading slashes from filename */
    1361         944 :         while (*filename == '/')
    1362         304 :                 filename++;
    1363             : 
    1364         320 :         wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
    1365         320 :                    htype, inet_ntoa(cli->sin_addr), htons(cli->sin_port));
    1366             : 
    1367         320 :         switch (htype) {
    1368             :         case HTTPREAD_HDR_TYPE_GET:
    1369          37 :                 web_connection_parse_get(sm, req, filename);
    1370          37 :                 break;
    1371             :         case HTTPREAD_HDR_TYPE_POST:
    1372         183 :                 web_connection_parse_post(sm, cli, req, filename);
    1373         183 :                 break;
    1374             :         case HTTPREAD_HDR_TYPE_SUBSCRIBE:
    1375          57 :                 web_connection_parse_subscribe(sm, req, filename);
    1376          57 :                 break;
    1377             :         case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
    1378          31 :                 web_connection_parse_unsubscribe(sm, req, filename);
    1379          31 :                 break;
    1380             : 
    1381             :                 /* We are not required to support M-POST; just plain
    1382             :                  * POST is supposed to work, so we only support that.
    1383             :                  * If for some reason we need to support M-POST, it is
    1384             :                  * mostly the same as POST, with small differences.
    1385             :                  */
    1386             :         default:
    1387             :                 /* Send 501 for anything else */
    1388          12 :                 web_connection_unimplemented(req);
    1389          12 :                 break;
    1390             :         }
    1391             : }
    1392             : 
    1393             : 
    1394             : /*
    1395             :  * Listening for web connections
    1396             :  * We have a single TCP listening port, and hand off connections as we get
    1397             :  * them.
    1398             :  */
    1399             : 
    1400          38 : void web_listener_stop(struct upnp_wps_device_sm *sm)
    1401             : {
    1402          38 :         http_server_deinit(sm->web_srv);
    1403          38 :         sm->web_srv = NULL;
    1404          38 : }
    1405             : 
    1406             : 
    1407          37 : int web_listener_start(struct upnp_wps_device_sm *sm)
    1408             : {
    1409             :         struct in_addr addr;
    1410          37 :         addr.s_addr = sm->ip_addr;
    1411          37 :         sm->web_srv = http_server_init(&addr, -1, web_connection_check_data,
    1412             :                                        sm);
    1413          37 :         if (sm->web_srv == NULL) {
    1414           0 :                 web_listener_stop(sm);
    1415           0 :                 return -1;
    1416             :         }
    1417          37 :         sm->web_port = http_server_get_port(sm->web_srv);
    1418             : 
    1419          37 :         return 0;
    1420             : }

Generated by: LCOV version 1.10