WPS: Limit Probe Request event queuing if subscriber may have left
[libeap.git] / src / wps / wps_upnp.c
index 62c24c7..410df46 100644 (file)
@@ -3,7 +3,7 @@
  * Copyright (c) 2000-2003 Intel Corporation
  * Copyright (c) 2006-2007 Sony Corporation
  * Copyright (c) 2008-2009 Atheros Communications
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
  *
  * See below for more details on licensing and code history.
  */
@@ -270,7 +270,7 @@ static void uuid_make(u8 uuid[UUID_LEN])
 /* subscr_addr_delete -- delete single unlinked subscriber address
  * (be sure to unlink first if need be)
  */
-static void subscr_addr_delete(struct subscr_addr *a)
+void subscr_addr_delete(struct subscr_addr *a)
 {
        /*
         * Note: do NOT free domain_and_port or path because they point to
@@ -293,7 +293,8 @@ static void subscr_addr_free_all(struct subscription *s)
 
 
 /* subscr_addr_add_url -- add address(es) for one url to subscription */
-static void subscr_addr_add_url(struct subscription *s, const char *url)
+static void subscr_addr_add_url(struct subscription *s, const char *url,
+                               size_t url_len)
 {
        int alloc_len;
        char *scratch_mem = NULL;
@@ -307,20 +308,21 @@ static void subscr_addr_add_url(struct subscription *s, const char *url)
        struct addrinfo *result = NULL;
        struct addrinfo *rp;
        int rerr;
-       struct subscr_addr *a = NULL;
 
        /* url MUST begin with http: */
-       if (os_strncasecmp(url, "http://", 7))
+       if (url_len < 7 || os_strncasecmp(url, "http://", 7))
                goto fail;
        url += 7;
+       url_len -= 7;
 
        /* allocate memory for the extra stuff we need */
-       alloc_len = (2 * (os_strlen(url) + 1));
+       alloc_len = 2 * (url_len + 1);
        scratch_mem = os_zalloc(alloc_len);
        if (scratch_mem == NULL)
                goto fail;
        mem = scratch_mem;
-       strcpy(mem, url);
+       os_strncpy(mem, url, url_len);
+       wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", mem);
        domain_and_port = mem;
        mem += 1 + os_strlen(mem);
        delim = os_strchr(domain_and_port, '/');
@@ -332,7 +334,7 @@ static void subscr_addr_add_url(struct subscription *s, const char *url)
        }
        domain = mem;
        strcpy(domain, domain_and_port);
-       delim = strchr(domain, ':');
+       delim = os_strchr(domain, ':');
        if (delim) {
                *delim++ = 0;   /* null terminate domain */
                if (isdigit(*delim))
@@ -367,6 +369,8 @@ static void subscr_addr_add_url(struct subscription *s, const char *url)
                goto fail;
        }
        for (rp = result; rp; rp = rp->ai_next) {
+               struct subscr_addr *a;
+
                /* Limit no. of address to avoid denial of service attack */
                if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) {
                        wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: "
@@ -377,7 +381,6 @@ static void subscr_addr_add_url(struct subscription *s, const char *url)
                a = os_zalloc(sizeof(*a) + alloc_len);
                if (a == NULL)
                        continue;
-               a->s = s;
                mem = (void *) (a + 1);
                a->domain_and_port = mem;
                strcpy(mem, domain_and_port);
@@ -386,19 +389,17 @@ static void subscr_addr_add_url(struct subscription *s, const char *url)
                if (path[0] != '/')
                        *mem++ = '/';
                strcpy(mem, path);
-               mem += 1 + strlen(mem);
+               mem += 1 + os_strlen(mem);
                os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr));
                a->saddr.sin_port = htons(port);
 
                dl_list_add(&s->addr_list, &a->list);
-               a = NULL;       /* don't free it below */
        }
 
 fail:
        if (result)
                freeaddrinfo(result);
        os_free(scratch_mem);
-       os_free(a);
 }
 
 
@@ -408,7 +409,8 @@ fail:
 static void subscr_addr_list_create(struct subscription *s,
                                    const char *url_list)
 {
-       char *end;
+       const char *end;
+       wpa_printf(MSG_DEBUG, "WPS UPnP: Parsing URL list '%s'", url_list);
        for (;;) {
                while (*url_list == ' ' || *url_list == '\t')
                        url_list++;
@@ -418,9 +420,8 @@ static void subscr_addr_list_create(struct subscription *s,
                end = os_strchr(url_list, '>');
                if (end == NULL)
                        break;
-               *end++ = 0;
-               subscr_addr_add_url(s, url_list);
-               url_list = end;
+               subscr_addr_add_url(s, url_list, end - url_list);
+               url_list = end + 1;
        }
 }
 
@@ -498,9 +499,10 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm)
 
        dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
                              list) {
-               if (event_add(s, buf)) {
+               if (event_add(s, buf, sm->wlanevent_type ==
+                             UPNP_WPS_WLANEVENT_TYPE_PROBE) == 1) {
                        wpa_printf(MSG_INFO, "WPS UPnP: Dropping "
-                                  "subscriber due to event backlog");
+                                  "subscriber %p due to event backlog", s);
                        dl_list_del(&s->list);
                        subscription_destroy(s);
                }
@@ -566,8 +568,11 @@ static struct wpabuf * build_fake_wsc_ack(void)
                return NULL;
        wpabuf_put_u8(msg, UPNP_WPS_WLANEVENT_TYPE_EAP);
        wpabuf_put_str(msg, "00:00:00:00:00:00");
-       wps_build_version(msg);
-       wps_build_msg_type(msg, WPS_WSC_ACK);
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_WSC_ACK)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
        /* Enrollee Nonce */
        wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
        wpabuf_put_be16(msg, WPS_NONCE_LEN);
@@ -576,6 +581,7 @@ static struct wpabuf * build_fake_wsc_ack(void)
        wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
        wpabuf_put_be16(msg, WPS_NONCE_LEN);
        wpabuf_put(msg, WPS_NONCE_LEN);
+       wps_build_wfa_ext(msg, 0, NULL, 0);
        return msg;
 }
 
@@ -603,6 +609,7 @@ static int subscription_first_event(struct subscription *s)
                "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
        const char *tail = "</e:propertyset>\n";
        char txt[10];
+       int ret;
 
        if (s->sm->wlanevent == NULL) {
                /*
@@ -634,7 +641,7 @@ static int subscription_first_event(struct subscription *s)
        }
        buf = wpabuf_alloc(500 + os_strlen(wlan_event));
        if (buf == NULL)
-               return 1;
+               return -1;
 
        wpabuf_put_str(buf, head);
        wpabuf_put_property(buf, "STAStatus", "1");
@@ -644,9 +651,10 @@ static int subscription_first_event(struct subscription *s)
                wpabuf_put_property(buf, "WLANEvent", wlan_event);
        wpabuf_put_str(buf, tail);
 
-       if (event_add(s, buf)) {
+       ret = event_add(s, buf, 0);
+       if (ret) {
                wpabuf_free(buf);
-               return 1;
+               return ret;
        }
        wpabuf_free(buf);
 
@@ -690,6 +698,13 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
        s->timeout_time = expire;
        uuid_make(s->uuid);
        subscr_addr_list_create(s, callback_urls);
+       if (dl_list_empty(&s->addr_list)) {
+               wpa_printf(MSG_DEBUG, "WPS UPnP: No valid callback URLs in "
+                          "'%s' - drop subscription", callback_urls);
+               subscription_destroy(s);
+               return NULL;
+       }
+
        /* Add to end of list, since it has the highest expiration time */
        dl_list_add_tail(&sm->subscriptions, &s->list);
        /* Queue up immediate event message (our last event)
@@ -784,6 +799,7 @@ int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
 
        os_free(sm->wlanevent);
        sm->wlanevent = val;
+       sm->wlanevent_type = ev_type;
        upnp_wps_device_send_event(sm);
 
        ret = 0;
@@ -795,7 +811,7 @@ fail:
 }
 
 
-#ifdef __FreeBSD__
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 #include <sys/sysctl.h>
 #include <net/route.h>
 #include <net/if_dl.h>
@@ -845,11 +861,10 @@ static int eth_get(const char *device, u8 ea[ETH_ALEN])
  * @ip_addr: Buffer for returning IP address in network byte order
  * @ip_addr_text: Buffer for returning a pointer to allocated IP address text
  * @mac: Buffer for returning MAC address
- * @mac_addr_text: Buffer for returning allocated MAC address text
  * Returns: 0 on success, -1 on failure
  */
 int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
-                  u8 mac[ETH_ALEN], char **mac_addr_text)
+                  u8 mac[ETH_ALEN])
 {
        struct ifreq req;
        int sock = -1;
@@ -857,8 +872,7 @@ int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
        struct in_addr in_addr;
 
        *ip_addr_text = os_zalloc(16);
-       *mac_addr_text = os_zalloc(18);
-       if (*ip_addr_text == NULL || *mac_addr_text == NULL)
+       if (*ip_addr_text == NULL)
                goto fail;
 
        sock = socket(AF_INET, SOCK_DGRAM, 0);
@@ -884,7 +898,7 @@ int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
                goto fail;
        }
        os_memcpy(mac, req.ifr_addr.sa_data, 6);
-#elif defined(__FreeBSD__)
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
        if (eth_get(net_if, mac) < 0) {
                wpa_printf(MSG_ERROR, "WPS UPnP: Failed to get MAC address");
                goto fail;
@@ -892,7 +906,6 @@ int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
 #else
 #error MAC address fetch not implemented
 #endif
-       os_snprintf(*mac_addr_text, 18, MACSTR, MAC2STR(mac));
 
        close(sock);
        return 0;
@@ -902,12 +915,19 @@ fail:
                close(sock);
        os_free(*ip_addr_text);
        *ip_addr_text = NULL;
-       os_free(*mac_addr_text);
-       *mac_addr_text = NULL;
        return -1;
 }
 
 
+static void upnp_wps_free_msearchreply(struct dl_list *head)
+{
+       struct advertisement_state_machine *a, *tmp;
+       dl_list_for_each_safe(a, tmp, head, struct advertisement_state_machine,
+                             list)
+               msearchreply_state_machine_stop(a);
+}
+
+
 static void upnp_wps_free_subscriptions(struct dl_list *head)
 {
        struct subscription *s, *tmp;
@@ -929,8 +949,7 @@ void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
 
        wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device");
        web_listener_stop(sm);
-       while (sm->msearch_replies)
-               msearchreply_state_machine_stop(sm->msearch_replies);
+       upnp_wps_free_msearchreply(&sm->msearch_replies);
        upnp_wps_free_subscriptions(&sm->subscriptions);
 
        advertisement_state_machine_stop(sm, 1);
@@ -938,10 +957,6 @@ void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
        event_send_stop_all(sm);
        os_free(sm->wlanevent);
        sm->wlanevent = NULL;
-       os_free(sm->net_if);
-       sm->net_if = NULL;
-       os_free(sm->mac_addr_text);
-       sm->mac_addr_text = NULL;
        os_free(sm->ip_addr_text);
        sm->ip_addr_text = NULL;
        if (sm->multicast_sd >= 0)
@@ -967,7 +982,6 @@ int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
        if (sm->started)
                upnp_wps_device_stop(sm);
 
-       sm->net_if = strdup(net_if);
        sm->multicast_sd = -1;
        sm->ssdp_sd = -1;
        sm->started = 1;
@@ -978,9 +992,8 @@ int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
                goto fail;
 
        /* Determine which IP and mac address we're using */
-       if (get_netif_info(net_if,
-                          &sm->ip_addr, &sm->ip_addr_text,
-                          sm->mac_addr, &sm->mac_addr_text)) {
+       if (get_netif_info(net_if, &sm->ip_addr, &sm->ip_addr_text,
+                          sm->mac_addr)) {
                wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
                           "for %s. Does it have IP address?", net_if);
                goto fail;
@@ -1059,6 +1072,7 @@ upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
        sm->ctx = ctx;
        sm->wps = wps;
        sm->priv = priv;
+       dl_list_init(&sm->msearch_replies);
        dl_list_init(&sm->subscriptions);
 
        return sm;
@@ -1074,3 +1088,20 @@ int upnp_wps_subscribers(struct upnp_wps_device_sm *sm)
 {
        return !dl_list_empty(&sm->subscriptions);
 }
+
+
+int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin)
+{
+       if (sm == NULL)
+               return 0;
+
+       os_free(sm->ctx->ap_pin);
+       if (ap_pin) {
+               sm->ctx->ap_pin = os_strdup(ap_pin);
+               if (sm->ctx->ap_pin == NULL)
+                       return -1;
+       } else
+               sm->ctx->ap_pin = NULL;
+
+       return 0;
+}