X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fwps%2Fwps_upnp.c;h=29806ec2012270c5b90870023455ca7008baf65a;hb=d0ebf3285f01640231aac233a09222ec19e69382;hp=00e2bc3c0ea66f99a4396e29299d35777a55b7d2;hpb=fda90ab4b73b19d4638e8b7cd4c90458e51f9e3e;p=libeap.git diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c index 00e2bc3..29806ec 100644 --- a/src/wps/wps_upnp.c +++ b/src/wps/wps_upnp.c @@ -47,9 +47,6 @@ * -- Needs renaming with module prefix to avoid polluting the debugger * namespace and causing possible collisions with other static fncs * and structure declarations when using the debugger. - * -- Just what should be in the first event message sent after subscription - * for the WLANEvent field? If i pass it empty, Vista replies with OK - * but apparently barfs on the message. * -- The http error code generation is pretty bogus, hopefully noone cares. * * Author: Ted Merrill, Atheros Communications, based upon earlier work @@ -283,53 +280,18 @@ static void subscr_addr_delete(struct subscr_addr *a) } -/* subscr_addr_unlink -- unlink subscriber address from linked list */ -static void subscr_addr_unlink(struct subscription *s, struct subscr_addr *a) -{ - struct subscr_addr **listp = &s->addr_list; - s->n_addr--; - a->next->prev = a->prev; - a->prev->next = a->next; - if (*listp == a) { - if (a == a->next) { - /* last in queue */ - *listp = NULL; - assert(s->n_addr == 0); - } else { - *listp = a->next; - } - } -} - - /* subscr_addr_free_all -- unlink and delete list of subscriber addresses. */ static void subscr_addr_free_all(struct subscription *s) { - struct subscr_addr **listp = &s->addr_list; - struct subscr_addr *a; - while ((a = *listp) != NULL) { - subscr_addr_unlink(s, a); + struct subscr_addr *a, *tmp; + dl_list_for_each_safe(a, tmp, &s->addr_list, struct subscr_addr, list) + { + dl_list_del(&a->list); subscr_addr_delete(a); } } -/* subscr_addr_link -- add subscriber address to list of addresses */ -static void subscr_addr_link(struct subscription *s, struct subscr_addr *a) -{ - struct subscr_addr **listp = &s->addr_list; - s->n_addr++; - if (*listp == NULL) { - *listp = a->next = a->prev = a; - } else { - a->next = *listp; - a->prev = (*listp)->prev; - a->prev->next = a; - a->next->prev = a; - } -} - - /* subscr_addr_add_url -- add address(es) for one url to subscription */ static void subscr_addr_add_url(struct subscription *s, const char *url) { @@ -345,7 +307,6 @@ 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)) @@ -405,8 +366,10 @@ 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 (s->n_addr >= MAX_ADDR_PER_SUBSCRIPTION) { + if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) { wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: " "Ignoring excessive addresses"); break; @@ -415,7 +378,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); @@ -428,15 +390,13 @@ static void subscr_addr_add_url(struct subscription *s, const char *url) os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr)); a->saddr.sin_port = htons(port); - subscr_addr_link(s, a); - a = NULL; /* don't free it below */ + dl_list_add(&s->addr_list, &a->list); } fail: if (result) freeaddrinfo(result); os_free(scratch_mem); - os_free(a); } @@ -465,7 +425,7 @@ static void subscr_addr_list_create(struct subscription *s, int send_wpabuf(int fd, struct wpabuf *buf) { - wpa_printf(MSG_DEBUG, "WPS UPnP: %lu byte response", + wpa_printf(MSG_DEBUG, "WPS UPnP: Send %lu byte message", (unsigned long) wpabuf_len(buf)); errno = 0; if (write(fd, wpabuf_head(buf), wpabuf_len(buf)) != @@ -505,14 +465,14 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm) /* Enqueue event message for all subscribers */ struct wpabuf *buf; /* holds event message */ int buf_size = 0; - struct subscription *s; + struct subscription *s, *tmp; /* Actually, utf-8 is the default, but it doesn't hurt to specify it */ const char *format_head = "\n" "\n"; const char *format_tail = "\n"; - if (sm->subscriptions == NULL) { + if (dl_list_empty(&sm->subscriptions)) { /* optimize */ return; } @@ -534,19 +494,15 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm) wpa_printf(MSG_MSGDUMP, "WPS UPnP: WLANEvent message:\n%s", (char *) wpabuf_head(buf)); - s = sm->subscriptions; - do { + dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription, + list) { if (event_add(s, buf)) { - struct subscription *s_old = s; wpa_printf(MSG_INFO, "WPS UPnP: Dropping " "subscriber due to event backlog"); - s = s_old->next; - subscription_unlink(s_old); - subscription_destroy(s_old); - } else { - s = s->next; + dl_list_del(&s->list); + subscription_destroy(s); } - } while (s != sm->subscriptions); + } wpabuf_free(buf); } @@ -558,52 +514,15 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm) * This is the result of an incoming HTTP over TCP SUBSCRIBE request. */ -/* subscription_unlink -- remove from the active list */ -void subscription_unlink(struct subscription *s) -{ - struct upnp_wps_device_sm *sm = s->sm; - - if (s->next == s) { - /* only one? */ - sm->subscriptions = NULL; - } else { - if (sm->subscriptions == s) - sm->subscriptions = s->next; - s->next->prev = s->prev; - s->prev->next = s->next; - } - sm->n_subscriptions--; -} - - -/* subscription_link_to_end -- link to end of active list - * (should have high expiry time!) - */ -static void subscription_link_to_end(struct subscription *s) -{ - struct upnp_wps_device_sm *sm = s->sm; - - if (sm->subscriptions) { - s->next = sm->subscriptions; - s->prev = s->next->prev; - s->prev->next = s; - s->next->prev = s; - } else { - sm->subscriptions = s->next = s->prev = s; - } - sm->n_subscriptions++; -} - - /* subscription_destroy -- destroy an unlinked subscription * Be sure to unlink first if necessary. */ void subscription_destroy(struct subscription *s) { wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s); - if (s->addr_list) - subscr_addr_free_all(s); + subscr_addr_free_all(s); event_delete_all(s); + upnp_er_remove_notification(s); os_free(s); } @@ -611,10 +530,13 @@ void subscription_destroy(struct subscription *s) /* subscription_list_age -- remove expired subscriptions */ static void subscription_list_age(struct upnp_wps_device_sm *sm, time_t now) { - struct subscription *s; - while ((s = sm->subscriptions) != NULL && s->timeout_time < now) { + struct subscription *s, *tmp; + dl_list_for_each_safe(s, tmp, &sm->subscriptions, + struct subscription, list) { + if (s->timeout_time > now) + break; wpa_printf(MSG_DEBUG, "WPS UPnP: Removing aged subscription"); - subscription_unlink(s); + dl_list_del(&s->list); subscription_destroy(s); } } @@ -626,21 +548,40 @@ static void subscription_list_age(struct upnp_wps_device_sm *sm, time_t now) struct subscription * subscription_find(struct upnp_wps_device_sm *sm, const u8 uuid[UUID_LEN]) { - struct subscription *s0 = sm->subscriptions; - struct subscription *s = s0; - - if (s0 == NULL) - return NULL; - do { + struct subscription *s; + dl_list_for_each(s, &sm->subscriptions, struct subscription, list) { if (os_memcmp(s->uuid, uuid, UUID_LEN) == 0) return s; /* Found match */ - s = s->next; - } while (s != s0); - + } return NULL; } +static struct wpabuf * build_fake_wsc_ack(void) +{ + struct wpabuf *msg = wpabuf_alloc(100); + if (msg == NULL) + return NULL; + wpabuf_put_u8(msg, UPNP_WPS_WLANEVENT_TYPE_EAP); + wpabuf_put_str(msg, "00:00:00:00:00:00"); + 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); + wpabuf_put(msg, WPS_NONCE_LEN); + /* Registrar Nonce */ + 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; +} + + /* subscription_first_event -- send format/queue event that is automatically * sent on a new subscription. */ @@ -665,6 +606,28 @@ static int subscription_first_event(struct subscription *s) const char *tail = "\n"; char txt[10]; + if (s->sm->wlanevent == NULL) { + /* + * There has been no events before the subscription. However, + * UPnP device architecture specification requires all the + * evented variables to be included, so generate a dummy event + * for this particular case using a WSC_ACK and all-zeros + * nonces. The ER (UPnP control point) will ignore this, but at + * least it will learn that WLANEvent variable will be used in + * event notifications in the future. + */ + struct wpabuf *msg; + wpa_printf(MSG_DEBUG, "WPS UPnP: Use a fake WSC_ACK as the " + "initial WLANEvent"); + msg = build_fake_wsc_ack(); + if (msg) { + s->sm->wlanevent = (char *) + base64_encode(wpabuf_head(msg), + wpabuf_len(msg), NULL); + wpabuf_free(msg); + } + } + wlan_event = s->sm->wlanevent; if (wlan_event == NULL || *wlan_event == '\0') { wpa_printf(MSG_DEBUG, "WPS UPnP: WLANEvent not known for " @@ -694,13 +657,13 @@ static int subscription_first_event(struct subscription *s) /** - * subscription_start - Rremember a UPnP control point to send events to. + * subscription_start - Remember a UPnP control point to send events to. * @sm: WPS UPnP state machine from upnp_wps_device_init() - * @callback_urls: malloc' mem given to the subscription + * @callback_urls: Callback URLs * Returns: %NULL on error, or pointer to new subscription structure. */ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, - char *callback_urls) + const char *callback_urls) { struct subscription *s; time_t now = time(NULL); @@ -710,37 +673,39 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, subscription_list_age(sm, now); /* If too many subscriptions, remove oldest */ - if (sm->n_subscriptions >= MAX_SUBSCRIPTIONS) { - s = sm->subscriptions; + if (dl_list_len(&sm->subscriptions) >= MAX_SUBSCRIPTIONS) { + s = dl_list_first(&sm->subscriptions, struct subscription, + list); wpa_printf(MSG_INFO, "WPS UPnP: Too many subscriptions, " "trashing oldest"); - subscription_unlink(s); + dl_list_del(&s->list); subscription_destroy(s); } s = os_zalloc(sizeof(*s)); if (s == NULL) return NULL; + dl_list_init(&s->addr_list); + dl_list_init(&s->event_queue); s->sm = sm; s->timeout_time = expire; uuid_make(s->uuid); subscr_addr_list_create(s, callback_urls); /* Add to end of list, since it has the highest expiration time */ - subscription_link_to_end(s); + dl_list_add_tail(&sm->subscriptions, &s->list); /* Queue up immediate event message (our last event) * as required by UPnP spec. */ if (subscription_first_event(s)) { wpa_printf(MSG_INFO, "WPS UPnP: Dropping subscriber due to " "event backlog"); - subscription_unlink(s); + dl_list_del(&s->list); subscription_destroy(s); return NULL; } wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s", s, callback_urls); - os_free(callback_urls); /* Schedule sending this */ event_send_all_later(sm); return s; @@ -757,10 +722,10 @@ struct subscription * subscription_renew(struct upnp_wps_device_sm *sm, if (s == NULL) return NULL; wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewed"); - subscription_unlink(s); + dl_list_del(&s->list); s->timeout_time = expire; /* add back to end of list, since it now has highest expiry */ - subscription_link_to_end(s); + dl_list_add_tail(&sm->subscriptions, &s->list); return s; } @@ -832,18 +797,60 @@ fail: } +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include +#include +#include + +static int eth_get(const char *device, u8 ea[ETH_ALEN]) +{ + struct if_msghdr *ifm; + struct sockaddr_dl *sdl; + u_char *p, *buf; + size_t len; + int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 }; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) + return -1; + if ((buf = os_malloc(len)) == NULL) + return -1; + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { + os_free(buf); + return -1; + } + for (p = buf; p < buf + len; p += ifm->ifm_msglen) { + ifm = (struct if_msghdr *)p; + sdl = (struct sockaddr_dl *)(ifm + 1); + if (ifm->ifm_type != RTM_IFINFO || + (ifm->ifm_addrs & RTA_IFP) == 0) + continue; + if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 || + os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0) + continue; + os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen); + break; + } + os_free(buf); + + if (p >= buf + len) { + errno = ESRCH; + return -1; + } + return 0; +} +#endif /* __FreeBSD__ */ + + /** * get_netif_info - Get hw and IP addresses for network device * @net_if: Selected network interface name * @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 */ -static int get_netif_info(const char *net_if, unsigned *ip_addr, - char **ip_addr_text, u8 mac[ETH_ALEN], - char **mac_addr_text) +int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text, + u8 mac[ETH_ALEN]) { struct ifreq req; int sock = -1; @@ -851,15 +858,14 @@ static int get_netif_info(const char *net_if, unsigned *ip_addr, 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); if (sock < 0) goto fail; - os_strncpy(req.ifr_name, net_if, sizeof(req.ifr_name)); + os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name)); if (ioctl(sock, SIOCGIFADDR, &req) < 0) { wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFADDR failed: %d (%s)", errno, strerror(errno)); @@ -870,14 +876,22 @@ static int get_netif_info(const char *net_if, unsigned *ip_addr, in_addr.s_addr = *ip_addr; os_snprintf(*ip_addr_text, 16, "%s", inet_ntoa(in_addr)); - os_strncpy(req.ifr_name, net_if, sizeof(req.ifr_name)); +#ifdef __linux__ + os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name)); if (ioctl(sock, SIOCGIFHWADDR, &req) < 0) { wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFHWADDR failed: " "%d (%s)", errno, strerror(errno)); goto fail; } os_memcpy(mac, req.ifr_addr.sa_data, 6); - os_snprintf(*mac_addr_text, 18, MACSTR, MAC2STR(req.ifr_addr.sa_data)); +#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; + } +#else +#error MAC address fetch not implemented +#endif close(sock); return 0; @@ -887,12 +901,29 @@ 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; + dl_list_for_each_safe(s, tmp, head, struct subscription, list) { + dl_list_del(&s->list); + subscription_destroy(s); + } +} + + /** * upnp_wps_device_stop - Stop WPS UPnP operations on an interface * @sm: WPS UPnP state machine from upnp_wps_device_init() @@ -904,26 +935,14 @@ 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->web_connections) - web_connection_stop(sm->web_connections); - while (sm->msearch_replies) - msearchreply_state_machine_stop(sm->msearch_replies); - while (sm->subscriptions) { - struct subscription *s = sm->subscriptions; - subscription_unlink(s); - subscription_destroy(s); - } + upnp_wps_free_msearchreply(&sm->msearch_replies); + upnp_wps_free_subscriptions(&sm->subscriptions); - advertisement_state_machine_stop(sm); - /* TODO: send byebye notifications */ + advertisement_state_machine_stop(sm, 1); 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) @@ -949,7 +968,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; @@ -960,9 +978,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; @@ -1013,6 +1030,7 @@ void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm) wps_deinit(sm->peer.wps); os_free(sm->root_dir); os_free(sm->desc_url); + os_free(sm->ctx->ap_pin); os_free(sm->ctx); os_free(sm); } @@ -1040,6 +1058,8 @@ 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; } @@ -1052,5 +1072,22 @@ upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps, */ int upnp_wps_subscribers(struct upnp_wps_device_sm *sm) { - return sm->subscriptions != NULL; + 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; }