1189251Ssam/* 2189251Ssam * UPnP WPS Device 3189251Ssam * Copyright (c) 2000-2003 Intel Corporation 4189251Ssam * Copyright (c) 2006-2007 Sony Corporation 5189251Ssam * Copyright (c) 2008-2009 Atheros Communications 6252726Srpaulo * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi> 7189251Ssam * 8189251Ssam * See below for more details on licensing and code history. 9189251Ssam */ 10189251Ssam 11189251Ssam/* 12189251Ssam * This has been greatly stripped down from the original file 13189251Ssam * (upnp_wps_device.c) by Ted Merrill, Atheros Communications 14189251Ssam * in order to eliminate use of the bulky libupnp library etc. 15189251Ssam * 16189251Ssam * History: 17189251Ssam * upnp_wps_device.c is/was a shim layer between wps_opt_upnp.c and 18189251Ssam * the libupnp library. 19189251Ssam * The layering (by Sony) was well done; only a very minor modification 20189251Ssam * to API of upnp_wps_device.c was required. 21189251Ssam * libupnp was found to be undesirable because: 22189251Ssam * -- It consumed too much code and data space 23189251Ssam * -- It uses multiple threads, making debugging more difficult 24189251Ssam * and possibly reducing reliability. 25189251Ssam * -- It uses static variables and only supports one instance. 26189251Ssam * The shim and libupnp are here replaced by special code written 27189251Ssam * specifically for the needs of hostapd. 28189251Ssam * Various shortcuts can and are taken to keep the code size small. 29189251Ssam * Generally, execution time is not as crucial. 30189251Ssam * 31189251Ssam * BUGS: 32189251Ssam * -- UPnP requires that we be able to resolve domain names. 33189251Ssam * While uncommon, if we have to do it then it will stall the entire 34189251Ssam * hostapd program, which is bad. 35189251Ssam * This is because we use the standard linux getaddrinfo() function 36189251Ssam * which is syncronous. 37189251Ssam * An asyncronous solution would be to use the free "ares" library. 38189251Ssam * -- Does not have a robust output buffering scheme. Uses a single 39189251Ssam * fixed size output buffer per TCP/HTTP connection, with possible (although 40189251Ssam * unlikely) possibility of overflow and likely excessive use of RAM. 41189251Ssam * A better solution would be to write the HTTP output as a buffered stream, 42189251Ssam * using chunking: (handle header specially, then) generate data with 43189251Ssam * a printf-like function into a buffer, catching buffer full condition, 44189251Ssam * then send it out surrounded by http chunking. 45189251Ssam * -- There is some code that could be separated out into the common 46189251Ssam * library to be shared with wpa_supplicant. 47189251Ssam * -- Needs renaming with module prefix to avoid polluting the debugger 48189251Ssam * namespace and causing possible collisions with other static fncs 49189251Ssam * and structure declarations when using the debugger. 50252726Srpaulo * -- The http error code generation is pretty bogus, hopefully no one cares. 51189251Ssam * 52189251Ssam * Author: Ted Merrill, Atheros Communications, based upon earlier work 53189251Ssam * as explained above and below. 54189251Ssam * 55189251Ssam * Copyright: 56189251Ssam * Copyright 2008 Atheros Communications. 57189251Ssam * 58189251Ssam * The original header (of upnp_wps_device.c) reads: 59189251Ssam * 60189251Ssam * Copyright (c) 2006-2007 Sony Corporation. All Rights Reserved. 61189251Ssam * 62189251Ssam * File Name: upnp_wps_device.c 63189251Ssam * Description: EAP-WPS UPnP device source 64189251Ssam * 65189251Ssam * Redistribution and use in source and binary forms, with or without 66189251Ssam * modification, are permitted provided that the following conditions 67189251Ssam * are met: 68189251Ssam * 69189251Ssam * * Redistributions of source code must retain the above copyright 70189251Ssam * notice, this list of conditions and the following disclaimer. 71189251Ssam * * Redistributions in binary form must reproduce the above copyright 72189251Ssam * notice, this list of conditions and the following disclaimer in 73189251Ssam * the documentation and/or other materials provided with the 74189251Ssam * distribution. 75189251Ssam * * Neither the name of Sony Corporation nor the names of its 76189251Ssam * contributors may be used to endorse or promote products derived 77189251Ssam * from this software without specific prior written permission. 78189251Ssam * 79189251Ssam * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 80189251Ssam * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 81189251Ssam * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 82189251Ssam * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 83189251Ssam * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 84189251Ssam * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 85189251Ssam * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 86189251Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 87189251Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 88189251Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 89189251Ssam * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 90189251Ssam * 91189251Ssam * Portions from Intel libupnp files, e.g. genlib/net/http/httpreadwrite.c 92189251Ssam * typical header: 93189251Ssam * 94189251Ssam * Copyright (c) 2000-2003 Intel Corporation 95189251Ssam * All rights reserved. 96189251Ssam * 97189251Ssam * Redistribution and use in source and binary forms, with or without 98189251Ssam * modification, are permitted provided that the following conditions are met: 99189251Ssam * 100189251Ssam * * Redistributions of source code must retain the above copyright notice, 101189251Ssam * this list of conditions and the following disclaimer. 102189251Ssam * * Redistributions in binary form must reproduce the above copyright notice, 103189251Ssam * this list of conditions and the following disclaimer in the documentation 104189251Ssam * and/or other materials provided with the distribution. 105189251Ssam * * Neither name of Intel Corporation nor the names of its contributors 106189251Ssam * may be used to endorse or promote products derived from this software 107189251Ssam * without specific prior written permission. 108189251Ssam * 109189251Ssam * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 110189251Ssam * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 111189251Ssam * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 112189251Ssam * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR 113189251Ssam * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 114189251Ssam * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 115189251Ssam * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 116189251Ssam * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 117189251Ssam * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 118189251Ssam * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 119189251Ssam * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 120189251Ssam*/ 121189251Ssam 122189251Ssam/* 123189251Ssam * Overview of WPS over UPnP: 124189251Ssam * 125189251Ssam * UPnP is a protocol that allows devices to discover each other and control 126189251Ssam * each other. In UPnP terminology, a device is either a "device" (a server 127189251Ssam * that provides information about itself and allows itself to be controlled) 128189251Ssam * or a "control point" (a client that controls "devices") or possibly both. 129189251Ssam * This file implements a UPnP "device". 130189251Ssam * 131189251Ssam * For us, we use mostly basic UPnP discovery, but the control part of interest 132189251Ssam * is WPS carried via UPnP messages. There is quite a bit of basic UPnP 133189251Ssam * discovery to do before we can get to WPS, however. 134189251Ssam * 135189251Ssam * UPnP discovery begins with "devices" send out multicast UDP packets to a 136189251Ssam * certain fixed multicast IP address and port, and "control points" sending 137189251Ssam * out other such UDP packets. 138189251Ssam * 139189251Ssam * The packets sent by devices are NOTIFY packets (not to be confused with TCP 140189251Ssam * NOTIFY packets that are used later) and those sent by control points are 141189251Ssam * M-SEARCH packets. These packets contain a simple HTTP style header. The 142189251Ssam * packets are sent redundantly to get around packet loss. Devices respond to 143189251Ssam * M-SEARCH packets with HTTP-like UDP packets containing HTTP/1.1 200 OK 144189251Ssam * messages, which give similar information as the UDP NOTIFY packets. 145189251Ssam * 146189251Ssam * The above UDP packets advertise the (arbitrary) TCP ports that the 147189251Ssam * respective parties will listen to. The control point can then do a HTTP 148189251Ssam * SUBSCRIBE (something like an HTTP PUT) after which the device can do a 149189251Ssam * separate HTTP NOTIFY (also like an HTTP PUT) to do event messaging. 150189251Ssam * 151189251Ssam * The control point will also do HTTP GET of the "device file" listed in the 152189251Ssam * original UDP information from the device (see UPNP_WPS_DEVICE_XML_FILE 153189251Ssam * data), and based on this will do additional GETs... HTTP POSTs are done to 154189251Ssam * cause an action. 155189251Ssam * 156189251Ssam * Beyond some basic information in HTTP headers, additional information is in 157189251Ssam * the HTTP bodies, in a format set by the SOAP and XML standards, a markup 158189251Ssam * language related to HTML used for web pages. This language is intended to 159189251Ssam * provide the ultimate in self-documentation by providing a universal 160189251Ssam * namespace based on pseudo-URLs called URIs. Note that although a URI looks 161189251Ssam * like a URL (a web address), they are never accessed as such but are used 162189251Ssam * only as identifiers. 163189251Ssam * 164189251Ssam * The POST of a GetDeviceInfo gets information similar to what might be 165189251Ssam * obtained from a probe request or response on Wi-Fi. WPS messages M1-M8 166189251Ssam * are passed via a POST of a PutMessage; the M1-M8 WPS messages are converted 167189251Ssam * to a bin64 ascii representation for encapsulation. When proxying messages, 168189251Ssam * WLANEvent and PutWLANResponse are used. 169189251Ssam * 170189251Ssam * This of course glosses over a lot of details. 171189251Ssam */ 172189251Ssam 173189251Ssam#include "includes.h" 174189251Ssam 175252726Srpaulo#include <time.h> 176189251Ssam#include <net/if.h> 177189251Ssam#include <netdb.h> 178189251Ssam#include <sys/ioctl.h> 179189251Ssam 180189251Ssam#include "common.h" 181189251Ssam#include "uuid.h" 182189251Ssam#include "base64.h" 183189251Ssam#include "wps.h" 184189251Ssam#include "wps_i.h" 185189251Ssam#include "wps_upnp.h" 186189251Ssam#include "wps_upnp_i.h" 187189251Ssam 188189251Ssam 189189251Ssam/* 190189251Ssam * UPnP allows a client ("control point") to send a server like us ("device") 191189251Ssam * a domain name for registration, and we are supposed to resolve it. This is 192189251Ssam * bad because, using the standard Linux library, we will stall the entire 193189251Ssam * hostapd waiting for resolution. 194189251Ssam * 195189251Ssam * The "correct" solution would be to use an event driven library for domain 196189251Ssam * name resolution such as "ares". However, this would increase code size 197189251Ssam * further. Since it is unlikely that we'll actually see such domain names, we 198189251Ssam * can just refuse to accept them. 199189251Ssam */ 200189251Ssam#define NO_DOMAIN_NAME_RESOLUTION 1 /* 1 to allow only dotted ip addresses */ 201189251Ssam 202189251Ssam 203189251Ssam/* 204189251Ssam * UPnP does not scale well. If we were in a room with thousands of control 205189251Ssam * points then potentially we could be expected to handle subscriptions for 206189251Ssam * each of them, which would exhaust our memory. So we must set a limit. In 207189251Ssam * practice we are unlikely to see more than one or two. 208189251Ssam */ 209189251Ssam#define MAX_SUBSCRIPTIONS 4 /* how many subscribing clients we handle */ 210189251Ssam#define MAX_ADDR_PER_SUBSCRIPTION 8 211189251Ssam 212252726Srpaulo/* Maximum number of Probe Request events per second */ 213252726Srpaulo#define MAX_EVENTS_PER_SEC 5 214189251Ssam 215252726Srpaulo 216252726Srpaulostatic struct upnp_wps_device_sm *shared_upnp_device = NULL; 217252726Srpaulo 218252726Srpaulo 219189251Ssam/* Write the current date/time per RFC */ 220189251Ssamvoid format_date(struct wpabuf *buf) 221189251Ssam{ 222189251Ssam const char *weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat"; 223189251Ssam const char *month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0" 224189251Ssam "Jul\0Aug\0Sep\0Oct\0Nov\0Dec"; 225189251Ssam struct tm *date; 226189251Ssam time_t t; 227189251Ssam 228189251Ssam t = time(NULL); 229189251Ssam date = gmtime(&t); 230189251Ssam wpabuf_printf(buf, "%s, %02d %s %d %02d:%02d:%02d GMT", 231189251Ssam &weekday_str[date->tm_wday * 4], date->tm_mday, 232189251Ssam &month_str[date->tm_mon * 4], date->tm_year + 1900, 233189251Ssam date->tm_hour, date->tm_min, date->tm_sec); 234189251Ssam} 235189251Ssam 236189251Ssam 237189251Ssam/*************************************************************************** 238189251Ssam * UUIDs (unique identifiers) 239189251Ssam * 240189251Ssam * These are supposed to be unique in all the world. 241189251Ssam * Sometimes permanent ones are used, sometimes temporary ones 242189251Ssam * based on random numbers... there are different rules for valid content 243189251Ssam * of different types. 244189251Ssam * Each uuid is 16 bytes long. 245189251Ssam **************************************************************************/ 246189251Ssam 247189251Ssam/* uuid_make -- construct a random UUID 248189251Ssam * The UPnP documents don't seem to offer any guidelines as to which method to 249189251Ssam * use for constructing UUIDs for subscriptions. Presumably any method from 250189251Ssam * rfc4122 is good enough; I've chosen random number method. 251189251Ssam */ 252189251Ssamstatic void uuid_make(u8 uuid[UUID_LEN]) 253189251Ssam{ 254189251Ssam os_get_random(uuid, UUID_LEN); 255189251Ssam 256189251Ssam /* Replace certain bits as specified in rfc4122 or X.667 */ 257189251Ssam uuid[6] &= 0x0f; uuid[6] |= (4 << 4); /* version 4 == random gen */ 258189251Ssam uuid[8] &= 0x3f; uuid[8] |= 0x80; 259189251Ssam} 260189251Ssam 261189251Ssam 262189251Ssam/* 263189251Ssam * Subscriber address handling. 264189251Ssam * Since a subscriber may have an arbitrary number of addresses, we have to 265189251Ssam * add a bunch of code to handle them. 266189251Ssam * 267189251Ssam * Addresses are passed in text, and MAY be domain names instead of the (usual 268189251Ssam * and expected) dotted IP addresses. Resolving domain names consumes a lot of 269189251Ssam * resources. Worse, we are currently using the standard Linux getaddrinfo() 270189251Ssam * which will block the entire program until complete or timeout! The proper 271189251Ssam * solution would be to use the "ares" library or similar with more state 272189251Ssam * machine steps etc. or just disable domain name resolution by setting 273189251Ssam * NO_DOMAIN_NAME_RESOLUTION to 1 at top of this file. 274189251Ssam */ 275189251Ssam 276189251Ssam/* subscr_addr_delete -- delete single unlinked subscriber address 277189251Ssam * (be sure to unlink first if need be) 278189251Ssam */ 279252726Srpaulovoid subscr_addr_delete(struct subscr_addr *a) 280189251Ssam{ 281189251Ssam /* 282189251Ssam * Note: do NOT free domain_and_port or path because they point to 283189251Ssam * memory within the allocation of "a". 284189251Ssam */ 285189251Ssam os_free(a); 286189251Ssam} 287189251Ssam 288189251Ssam 289189251Ssam/* subscr_addr_free_all -- unlink and delete list of subscriber addresses. */ 290189251Ssamstatic void subscr_addr_free_all(struct subscription *s) 291189251Ssam{ 292214734Srpaulo struct subscr_addr *a, *tmp; 293214734Srpaulo dl_list_for_each_safe(a, tmp, &s->addr_list, struct subscr_addr, list) 294214734Srpaulo { 295214734Srpaulo dl_list_del(&a->list); 296189251Ssam subscr_addr_delete(a); 297189251Ssam } 298189251Ssam} 299189251Ssam 300189251Ssam 301189251Ssam/* subscr_addr_add_url -- add address(es) for one url to subscription */ 302252726Srpaulostatic void subscr_addr_add_url(struct subscription *s, const char *url, 303252726Srpaulo size_t url_len) 304189251Ssam{ 305189251Ssam int alloc_len; 306189251Ssam char *scratch_mem = NULL; 307189251Ssam char *mem; 308252726Srpaulo char *host; 309189251Ssam char *delim; 310189251Ssam char *path; 311189251Ssam int port = 80; /* port to send to (default is port 80) */ 312189251Ssam struct addrinfo hints; 313189251Ssam struct addrinfo *result = NULL; 314189251Ssam struct addrinfo *rp; 315189251Ssam int rerr; 316252726Srpaulo size_t host_len, path_len; 317189251Ssam 318189251Ssam /* url MUST begin with http: */ 319252726Srpaulo if (url_len < 7 || os_strncasecmp(url, "http://", 7)) 320189251Ssam goto fail; 321189251Ssam url += 7; 322252726Srpaulo url_len -= 7; 323189251Ssam 324252726Srpaulo /* Make a copy of the string to allow modification during parsing */ 325252726Srpaulo scratch_mem = os_malloc(url_len + 1); 326189251Ssam if (scratch_mem == NULL) 327189251Ssam goto fail; 328252726Srpaulo os_memcpy(scratch_mem, url, url_len); 329252726Srpaulo scratch_mem[url_len] = '\0'; 330252726Srpaulo wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", scratch_mem); 331252726Srpaulo host = scratch_mem; 332252726Srpaulo path = os_strchr(host, '/'); 333252726Srpaulo if (path) 334252726Srpaulo *path++ = '\0'; /* null terminate host */ 335252726Srpaulo 336252726Srpaulo /* Process and remove optional port component */ 337252726Srpaulo delim = os_strchr(host, ':'); 338189251Ssam if (delim) { 339252726Srpaulo *delim = '\0'; /* null terminate host name for now */ 340252726Srpaulo if (isdigit(delim[1])) 341252726Srpaulo port = atol(delim + 1); 342189251Ssam } 343189251Ssam 344189251Ssam /* 345189251Ssam * getaddrinfo does the right thing with dotted decimal notations, or 346189251Ssam * will resolve domain names. Resolving domain names will unfortunately 347189251Ssam * hang the entire program until it is resolved or it times out 348189251Ssam * internal to getaddrinfo; fortunately we think that the use of actual 349189251Ssam * domain names (vs. dotted decimal notations) should be uncommon. 350189251Ssam */ 351189251Ssam os_memset(&hints, 0, sizeof(struct addrinfo)); 352189251Ssam hints.ai_family = AF_INET; /* IPv4 */ 353189251Ssam hints.ai_socktype = SOCK_STREAM; 354189251Ssam#if NO_DOMAIN_NAME_RESOLUTION 355189251Ssam /* Suppress domain name resolutions that would halt 356189251Ssam * the program for periods of time 357189251Ssam */ 358189251Ssam hints.ai_flags = AI_NUMERICHOST; 359189251Ssam#else 360189251Ssam /* Allow domain name resolution. */ 361189251Ssam hints.ai_flags = 0; 362189251Ssam#endif 363189251Ssam hints.ai_protocol = 0; /* Any protocol? */ 364252726Srpaulo rerr = getaddrinfo(host, NULL /* fill in port ourselves */, 365189251Ssam &hints, &result); 366189251Ssam if (rerr) { 367189251Ssam wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s", 368252726Srpaulo rerr, gai_strerror(rerr), host); 369189251Ssam goto fail; 370189251Ssam } 371252726Srpaulo 372252726Srpaulo if (delim) 373252726Srpaulo *delim = ':'; /* Restore port */ 374252726Srpaulo 375252726Srpaulo host_len = os_strlen(host); 376252726Srpaulo path_len = path ? os_strlen(path) : 0; 377252726Srpaulo alloc_len = host_len + 1 + 1 + path_len + 1; 378252726Srpaulo 379189251Ssam for (rp = result; rp; rp = rp->ai_next) { 380252726Srpaulo struct subscr_addr *a; 381252726Srpaulo 382189251Ssam /* Limit no. of address to avoid denial of service attack */ 383214734Srpaulo if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) { 384189251Ssam wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: " 385189251Ssam "Ignoring excessive addresses"); 386189251Ssam break; 387189251Ssam } 388189251Ssam 389189251Ssam a = os_zalloc(sizeof(*a) + alloc_len); 390189251Ssam if (a == NULL) 391252726Srpaulo break; 392252726Srpaulo mem = (char *) (a + 1); 393189251Ssam a->domain_and_port = mem; 394252726Srpaulo os_memcpy(mem, host, host_len); 395252726Srpaulo mem += host_len + 1; 396189251Ssam a->path = mem; 397252726Srpaulo if (path == NULL || path[0] != '/') 398189251Ssam *mem++ = '/'; 399252726Srpaulo if (path) 400252726Srpaulo os_memcpy(mem, path, path_len); 401189251Ssam os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr)); 402189251Ssam a->saddr.sin_port = htons(port); 403189251Ssam 404214734Srpaulo dl_list_add(&s->addr_list, &a->list); 405189251Ssam } 406189251Ssam 407189251Ssamfail: 408189251Ssam if (result) 409189251Ssam freeaddrinfo(result); 410189251Ssam os_free(scratch_mem); 411189251Ssam} 412189251Ssam 413189251Ssam 414189251Ssam/* subscr_addr_list_create -- create list from urls in string. 415189251Ssam * Each url is enclosed by angle brackets. 416189251Ssam */ 417189251Ssamstatic void subscr_addr_list_create(struct subscription *s, 418189251Ssam const char *url_list) 419189251Ssam{ 420252726Srpaulo const char *end; 421252726Srpaulo wpa_printf(MSG_DEBUG, "WPS UPnP: Parsing URL list '%s'", url_list); 422189251Ssam for (;;) { 423189251Ssam while (*url_list == ' ' || *url_list == '\t') 424189251Ssam url_list++; 425189251Ssam if (*url_list != '<') 426189251Ssam break; 427189251Ssam url_list++; 428189251Ssam end = os_strchr(url_list, '>'); 429189251Ssam if (end == NULL) 430189251Ssam break; 431252726Srpaulo subscr_addr_add_url(s, url_list, end - url_list); 432252726Srpaulo url_list = end + 1; 433189251Ssam } 434189251Ssam} 435189251Ssam 436189251Ssam 437189251Ssamint send_wpabuf(int fd, struct wpabuf *buf) 438189251Ssam{ 439189251Ssam wpa_printf(MSG_DEBUG, "WPS UPnP: Send %lu byte message", 440189251Ssam (unsigned long) wpabuf_len(buf)); 441189251Ssam errno = 0; 442189251Ssam if (write(fd, wpabuf_head(buf), wpabuf_len(buf)) != 443189251Ssam (int) wpabuf_len(buf)) { 444189251Ssam wpa_printf(MSG_ERROR, "WPS UPnP: Failed to send buffer: " 445189251Ssam "errno=%d (%s)", 446189251Ssam errno, strerror(errno)); 447189251Ssam return -1; 448189251Ssam } 449189251Ssam 450189251Ssam return 0; 451189251Ssam} 452189251Ssam 453189251Ssam 454189251Ssamstatic void wpabuf_put_property(struct wpabuf *buf, const char *name, 455189251Ssam const char *value) 456189251Ssam{ 457189251Ssam wpabuf_put_str(buf, "<e:property>"); 458189251Ssam wpabuf_printf(buf, "<%s>", name); 459189251Ssam if (value) 460189251Ssam wpabuf_put_str(buf, value); 461189251Ssam wpabuf_printf(buf, "</%s>", name); 462189251Ssam wpabuf_put_str(buf, "</e:property>\n"); 463189251Ssam} 464189251Ssam 465189251Ssam 466189251Ssam/** 467189251Ssam * upnp_wps_device_send_event - Queue event messages for subscribers 468189251Ssam * @sm: WPS UPnP state machine from upnp_wps_device_init() 469189251Ssam * 470189251Ssam * This function queues the last WLANEvent to be sent for all currently 471189251Ssam * subscribed UPnP control points. sm->wlanevent must have been set with the 472189251Ssam * encoded data before calling this function. 473189251Ssam */ 474189251Ssamstatic void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm) 475189251Ssam{ 476189251Ssam /* Enqueue event message for all subscribers */ 477189251Ssam struct wpabuf *buf; /* holds event message */ 478189251Ssam int buf_size = 0; 479214734Srpaulo struct subscription *s, *tmp; 480189251Ssam /* Actually, utf-8 is the default, but it doesn't hurt to specify it */ 481189251Ssam const char *format_head = 482189251Ssam "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" 483189251Ssam "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n"; 484189251Ssam const char *format_tail = "</e:propertyset>\n"; 485252726Srpaulo struct os_time now; 486189251Ssam 487214734Srpaulo if (dl_list_empty(&sm->subscriptions)) { 488189251Ssam /* optimize */ 489189251Ssam return; 490189251Ssam } 491189251Ssam 492252726Srpaulo if (os_get_time(&now) == 0) { 493252726Srpaulo if (now.sec != sm->last_event_sec) { 494252726Srpaulo sm->last_event_sec = now.sec; 495252726Srpaulo sm->num_events_in_sec = 1; 496252726Srpaulo } else { 497252726Srpaulo sm->num_events_in_sec++; 498252726Srpaulo /* 499252726Srpaulo * In theory, this should apply to all WLANEvent 500252726Srpaulo * notifications, but EAP messages are of much higher 501252726Srpaulo * priority and Probe Request notifications should not 502252726Srpaulo * be allowed to drop EAP messages, so only throttle 503252726Srpaulo * Probe Request notifications. 504252726Srpaulo */ 505252726Srpaulo if (sm->num_events_in_sec > MAX_EVENTS_PER_SEC && 506252726Srpaulo sm->wlanevent_type == 507252726Srpaulo UPNP_WPS_WLANEVENT_TYPE_PROBE) { 508252726Srpaulo wpa_printf(MSG_DEBUG, "WPS UPnP: Throttle " 509252726Srpaulo "event notifications (%u seen " 510252726Srpaulo "during one second)", 511252726Srpaulo sm->num_events_in_sec); 512252726Srpaulo return; 513252726Srpaulo } 514252726Srpaulo } 515252726Srpaulo } 516252726Srpaulo 517189251Ssam /* Determine buffer size needed first */ 518189251Ssam buf_size += os_strlen(format_head); 519189251Ssam buf_size += 50 + 2 * os_strlen("WLANEvent"); 520189251Ssam if (sm->wlanevent) 521189251Ssam buf_size += os_strlen(sm->wlanevent); 522189251Ssam buf_size += os_strlen(format_tail); 523189251Ssam 524189251Ssam buf = wpabuf_alloc(buf_size); 525189251Ssam if (buf == NULL) 526189251Ssam return; 527189251Ssam wpabuf_put_str(buf, format_head); 528189251Ssam wpabuf_put_property(buf, "WLANEvent", sm->wlanevent); 529189251Ssam wpabuf_put_str(buf, format_tail); 530189251Ssam 531189251Ssam wpa_printf(MSG_MSGDUMP, "WPS UPnP: WLANEvent message:\n%s", 532189251Ssam (char *) wpabuf_head(buf)); 533189251Ssam 534214734Srpaulo dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription, 535214734Srpaulo list) { 536252726Srpaulo event_add(s, buf, 537252726Srpaulo sm->wlanevent_type == UPNP_WPS_WLANEVENT_TYPE_PROBE); 538214734Srpaulo } 539189251Ssam 540189251Ssam wpabuf_free(buf); 541189251Ssam} 542189251Ssam 543189251Ssam 544189251Ssam/* 545189251Ssam * Event subscription (subscriber machines register with us to receive event 546189251Ssam * messages). 547189251Ssam * This is the result of an incoming HTTP over TCP SUBSCRIBE request. 548189251Ssam */ 549189251Ssam 550189251Ssam/* subscription_destroy -- destroy an unlinked subscription 551189251Ssam * Be sure to unlink first if necessary. 552189251Ssam */ 553189251Ssamvoid subscription_destroy(struct subscription *s) 554189251Ssam{ 555252726Srpaulo struct upnp_wps_device_interface *iface; 556189251Ssam wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s); 557214734Srpaulo subscr_addr_free_all(s); 558189251Ssam event_delete_all(s); 559252726Srpaulo dl_list_for_each(iface, &s->sm->interfaces, 560252726Srpaulo struct upnp_wps_device_interface, list) 561252726Srpaulo upnp_er_remove_notification(iface->wps->registrar, s); 562189251Ssam os_free(s); 563189251Ssam} 564189251Ssam 565189251Ssam 566189251Ssam/* subscription_list_age -- remove expired subscriptions */ 567189251Ssamstatic void subscription_list_age(struct upnp_wps_device_sm *sm, time_t now) 568189251Ssam{ 569214734Srpaulo struct subscription *s, *tmp; 570214734Srpaulo dl_list_for_each_safe(s, tmp, &sm->subscriptions, 571214734Srpaulo struct subscription, list) { 572214734Srpaulo if (s->timeout_time > now) 573214734Srpaulo break; 574189251Ssam wpa_printf(MSG_DEBUG, "WPS UPnP: Removing aged subscription"); 575214734Srpaulo dl_list_del(&s->list); 576189251Ssam subscription_destroy(s); 577189251Ssam } 578189251Ssam} 579189251Ssam 580189251Ssam 581189251Ssam/* subscription_find -- return existing subscription matching uuid, if any 582189251Ssam * returns NULL if not found 583189251Ssam */ 584189251Ssamstruct subscription * subscription_find(struct upnp_wps_device_sm *sm, 585189251Ssam const u8 uuid[UUID_LEN]) 586189251Ssam{ 587214734Srpaulo struct subscription *s; 588214734Srpaulo dl_list_for_each(s, &sm->subscriptions, struct subscription, list) { 589189251Ssam if (os_memcmp(s->uuid, uuid, UUID_LEN) == 0) 590189251Ssam return s; /* Found match */ 591214734Srpaulo } 592189251Ssam return NULL; 593189251Ssam} 594189251Ssam 595189251Ssam 596209158Srpaulostatic struct wpabuf * build_fake_wsc_ack(void) 597209158Srpaulo{ 598209158Srpaulo struct wpabuf *msg = wpabuf_alloc(100); 599209158Srpaulo if (msg == NULL) 600209158Srpaulo return NULL; 601209158Srpaulo wpabuf_put_u8(msg, UPNP_WPS_WLANEVENT_TYPE_EAP); 602209158Srpaulo wpabuf_put_str(msg, "00:00:00:00:00:00"); 603214734Srpaulo if (wps_build_version(msg) || 604214734Srpaulo wps_build_msg_type(msg, WPS_WSC_ACK)) { 605214734Srpaulo wpabuf_free(msg); 606214734Srpaulo return NULL; 607214734Srpaulo } 608209158Srpaulo /* Enrollee Nonce */ 609209158Srpaulo wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE); 610209158Srpaulo wpabuf_put_be16(msg, WPS_NONCE_LEN); 611209158Srpaulo wpabuf_put(msg, WPS_NONCE_LEN); 612209158Srpaulo /* Registrar Nonce */ 613209158Srpaulo wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE); 614209158Srpaulo wpabuf_put_be16(msg, WPS_NONCE_LEN); 615209158Srpaulo wpabuf_put(msg, WPS_NONCE_LEN); 616252726Srpaulo wps_build_wfa_ext(msg, 0, NULL, 0); 617209158Srpaulo return msg; 618209158Srpaulo} 619209158Srpaulo 620209158Srpaulo 621189251Ssam/* subscription_first_event -- send format/queue event that is automatically 622189251Ssam * sent on a new subscription. 623189251Ssam */ 624189251Ssamstatic int subscription_first_event(struct subscription *s) 625189251Ssam{ 626189251Ssam /* 627189251Ssam * Actually, utf-8 is the default, but it doesn't hurt to specify it. 628189251Ssam * 629189251Ssam * APStatus is apparently a bit set, 630189251Ssam * 0x1 = configuration change (but is always set?) 631189251Ssam * 0x10 = ap is locked 632189251Ssam * 633189251Ssam * Per UPnP spec, we send out the last value of each variable, even 634189251Ssam * for WLANEvent, whatever it was. 635189251Ssam */ 636189251Ssam char *wlan_event; 637189251Ssam struct wpabuf *buf; 638189251Ssam int ap_status = 1; /* TODO: add 0x10 if access point is locked */ 639189251Ssam const char *head = 640189251Ssam "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" 641189251Ssam "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n"; 642189251Ssam const char *tail = "</e:propertyset>\n"; 643189251Ssam char txt[10]; 644252726Srpaulo int ret; 645189251Ssam 646209158Srpaulo if (s->sm->wlanevent == NULL) { 647209158Srpaulo /* 648209158Srpaulo * There has been no events before the subscription. However, 649209158Srpaulo * UPnP device architecture specification requires all the 650209158Srpaulo * evented variables to be included, so generate a dummy event 651209158Srpaulo * for this particular case using a WSC_ACK and all-zeros 652209158Srpaulo * nonces. The ER (UPnP control point) will ignore this, but at 653209158Srpaulo * least it will learn that WLANEvent variable will be used in 654209158Srpaulo * event notifications in the future. 655209158Srpaulo */ 656209158Srpaulo struct wpabuf *msg; 657209158Srpaulo wpa_printf(MSG_DEBUG, "WPS UPnP: Use a fake WSC_ACK as the " 658209158Srpaulo "initial WLANEvent"); 659209158Srpaulo msg = build_fake_wsc_ack(); 660209158Srpaulo if (msg) { 661209158Srpaulo s->sm->wlanevent = (char *) 662209158Srpaulo base64_encode(wpabuf_head(msg), 663209158Srpaulo wpabuf_len(msg), NULL); 664209158Srpaulo wpabuf_free(msg); 665209158Srpaulo } 666209158Srpaulo } 667209158Srpaulo 668189251Ssam wlan_event = s->sm->wlanevent; 669189251Ssam if (wlan_event == NULL || *wlan_event == '\0') { 670189251Ssam wpa_printf(MSG_DEBUG, "WPS UPnP: WLANEvent not known for " 671189251Ssam "initial event message"); 672189251Ssam wlan_event = ""; 673189251Ssam } 674189251Ssam buf = wpabuf_alloc(500 + os_strlen(wlan_event)); 675189251Ssam if (buf == NULL) 676252726Srpaulo return -1; 677189251Ssam 678189251Ssam wpabuf_put_str(buf, head); 679189251Ssam wpabuf_put_property(buf, "STAStatus", "1"); 680189251Ssam os_snprintf(txt, sizeof(txt), "%d", ap_status); 681189251Ssam wpabuf_put_property(buf, "APStatus", txt); 682189251Ssam if (*wlan_event) 683189251Ssam wpabuf_put_property(buf, "WLANEvent", wlan_event); 684189251Ssam wpabuf_put_str(buf, tail); 685189251Ssam 686252726Srpaulo ret = event_add(s, buf, 0); 687252726Srpaulo if (ret) { 688189251Ssam wpabuf_free(buf); 689252726Srpaulo return ret; 690189251Ssam } 691189251Ssam wpabuf_free(buf); 692189251Ssam 693189251Ssam return 0; 694189251Ssam} 695189251Ssam 696189251Ssam 697189251Ssam/** 698209158Srpaulo * subscription_start - Remember a UPnP control point to send events to. 699189251Ssam * @sm: WPS UPnP state machine from upnp_wps_device_init() 700209158Srpaulo * @callback_urls: Callback URLs 701189251Ssam * Returns: %NULL on error, or pointer to new subscription structure. 702189251Ssam */ 703189251Ssamstruct subscription * subscription_start(struct upnp_wps_device_sm *sm, 704209158Srpaulo const char *callback_urls) 705189251Ssam{ 706189251Ssam struct subscription *s; 707189251Ssam time_t now = time(NULL); 708189251Ssam time_t expire = now + UPNP_SUBSCRIBE_SEC; 709189251Ssam 710189251Ssam /* Get rid of expired subscriptions so we have room */ 711189251Ssam subscription_list_age(sm, now); 712189251Ssam 713189251Ssam /* If too many subscriptions, remove oldest */ 714214734Srpaulo if (dl_list_len(&sm->subscriptions) >= MAX_SUBSCRIPTIONS) { 715214734Srpaulo s = dl_list_first(&sm->subscriptions, struct subscription, 716214734Srpaulo list); 717189251Ssam wpa_printf(MSG_INFO, "WPS UPnP: Too many subscriptions, " 718189251Ssam "trashing oldest"); 719214734Srpaulo dl_list_del(&s->list); 720189251Ssam subscription_destroy(s); 721189251Ssam } 722189251Ssam 723189251Ssam s = os_zalloc(sizeof(*s)); 724189251Ssam if (s == NULL) 725189251Ssam return NULL; 726214734Srpaulo dl_list_init(&s->addr_list); 727214734Srpaulo dl_list_init(&s->event_queue); 728189251Ssam 729189251Ssam s->sm = sm; 730189251Ssam s->timeout_time = expire; 731189251Ssam uuid_make(s->uuid); 732189251Ssam subscr_addr_list_create(s, callback_urls); 733252726Srpaulo if (dl_list_empty(&s->addr_list)) { 734252726Srpaulo wpa_printf(MSG_DEBUG, "WPS UPnP: No valid callback URLs in " 735252726Srpaulo "'%s' - drop subscription", callback_urls); 736252726Srpaulo subscription_destroy(s); 737252726Srpaulo return NULL; 738252726Srpaulo } 739252726Srpaulo 740189251Ssam /* Add to end of list, since it has the highest expiration time */ 741214734Srpaulo dl_list_add_tail(&sm->subscriptions, &s->list); 742189251Ssam /* Queue up immediate event message (our last event) 743189251Ssam * as required by UPnP spec. 744189251Ssam */ 745189251Ssam if (subscription_first_event(s)) { 746189251Ssam wpa_printf(MSG_INFO, "WPS UPnP: Dropping subscriber due to " 747189251Ssam "event backlog"); 748214734Srpaulo dl_list_del(&s->list); 749189251Ssam subscription_destroy(s); 750189251Ssam return NULL; 751189251Ssam } 752189251Ssam wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s", 753189251Ssam s, callback_urls); 754189251Ssam /* Schedule sending this */ 755189251Ssam event_send_all_later(sm); 756189251Ssam return s; 757189251Ssam} 758189251Ssam 759189251Ssam 760189251Ssam/* subscription_renew -- find subscription and reset timeout */ 761189251Ssamstruct subscription * subscription_renew(struct upnp_wps_device_sm *sm, 762189251Ssam const u8 uuid[UUID_LEN]) 763189251Ssam{ 764189251Ssam time_t now = time(NULL); 765189251Ssam time_t expire = now + UPNP_SUBSCRIBE_SEC; 766189251Ssam struct subscription *s = subscription_find(sm, uuid); 767189251Ssam if (s == NULL) 768189251Ssam return NULL; 769189251Ssam wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewed"); 770214734Srpaulo dl_list_del(&s->list); 771189251Ssam s->timeout_time = expire; 772189251Ssam /* add back to end of list, since it now has highest expiry */ 773214734Srpaulo dl_list_add_tail(&sm->subscriptions, &s->list); 774189251Ssam return s; 775189251Ssam} 776189251Ssam 777189251Ssam 778189251Ssam/** 779189251Ssam * upnp_wps_device_send_wlan_event - Event notification 780189251Ssam * @sm: WPS UPnP state machine from upnp_wps_device_init() 781189251Ssam * @from_mac_addr: Source (Enrollee) MAC address for the event 782189251Ssam * @ev_type: Event type 783189251Ssam * @msg: Event data 784189251Ssam * Returns: 0 on success, -1 on failure 785189251Ssam * 786189251Ssam * Tell external Registrars (UPnP control points) that something happened. In 787189251Ssam * particular, events include WPS messages from clients that are proxied to 788189251Ssam * external Registrars. 789189251Ssam */ 790189251Ssamint upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm, 791189251Ssam const u8 from_mac_addr[ETH_ALEN], 792189251Ssam enum upnp_wps_wlanevent_type ev_type, 793189251Ssam const struct wpabuf *msg) 794189251Ssam{ 795189251Ssam int ret = -1; 796189251Ssam char type[2]; 797189251Ssam const u8 *mac = from_mac_addr; 798189251Ssam char mac_text[18]; 799189251Ssam u8 *raw = NULL; 800189251Ssam size_t raw_len; 801189251Ssam char *val; 802189251Ssam size_t val_len; 803189251Ssam int pos = 0; 804189251Ssam 805189251Ssam if (!sm) 806189251Ssam goto fail; 807189251Ssam 808189251Ssam os_snprintf(type, sizeof(type), "%1u", ev_type); 809189251Ssam 810189251Ssam raw_len = 1 + 17 + (msg ? wpabuf_len(msg) : 0); 811189251Ssam raw = os_zalloc(raw_len); 812189251Ssam if (!raw) 813189251Ssam goto fail; 814189251Ssam 815189251Ssam *(raw + pos) = (u8) ev_type; 816189251Ssam pos += 1; 817189251Ssam os_snprintf(mac_text, sizeof(mac_text), MACSTR, MAC2STR(mac)); 818189251Ssam wpa_printf(MSG_DEBUG, "WPS UPnP: Proxying WLANEvent from %s", 819189251Ssam mac_text); 820189251Ssam os_memcpy(raw + pos, mac_text, 17); 821189251Ssam pos += 17; 822189251Ssam if (msg) { 823189251Ssam os_memcpy(raw + pos, wpabuf_head(msg), wpabuf_len(msg)); 824189251Ssam pos += wpabuf_len(msg); 825189251Ssam } 826189251Ssam raw_len = pos; 827189251Ssam 828189251Ssam val = (char *) base64_encode(raw, raw_len, &val_len); 829189251Ssam if (val == NULL) 830189251Ssam goto fail; 831189251Ssam 832189251Ssam os_free(sm->wlanevent); 833189251Ssam sm->wlanevent = val; 834252726Srpaulo sm->wlanevent_type = ev_type; 835189251Ssam upnp_wps_device_send_event(sm); 836189251Ssam 837189251Ssam ret = 0; 838189251Ssam 839189251Ssamfail: 840189251Ssam os_free(raw); 841189251Ssam 842189251Ssam return ret; 843189251Ssam} 844189251Ssam 845189251Ssam 846214734Srpaulo#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) 847209158Srpaulo#include <sys/sysctl.h> 848209158Srpaulo#include <net/route.h> 849209158Srpaulo#include <net/if_dl.h> 850209158Srpaulo 851209158Srpaulostatic int eth_get(const char *device, u8 ea[ETH_ALEN]) 852209158Srpaulo{ 853209158Srpaulo struct if_msghdr *ifm; 854209158Srpaulo struct sockaddr_dl *sdl; 855209158Srpaulo u_char *p, *buf; 856209158Srpaulo size_t len; 857209158Srpaulo int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 }; 858209158Srpaulo 859209158Srpaulo if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) 860209158Srpaulo return -1; 861209158Srpaulo if ((buf = os_malloc(len)) == NULL) 862209158Srpaulo return -1; 863209158Srpaulo if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { 864209158Srpaulo os_free(buf); 865209158Srpaulo return -1; 866209158Srpaulo } 867209158Srpaulo for (p = buf; p < buf + len; p += ifm->ifm_msglen) { 868209158Srpaulo ifm = (struct if_msghdr *)p; 869209158Srpaulo sdl = (struct sockaddr_dl *)(ifm + 1); 870209158Srpaulo if (ifm->ifm_type != RTM_IFINFO || 871209158Srpaulo (ifm->ifm_addrs & RTA_IFP) == 0) 872209158Srpaulo continue; 873209158Srpaulo if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 || 874209158Srpaulo os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0) 875209158Srpaulo continue; 876209158Srpaulo os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen); 877209158Srpaulo break; 878209158Srpaulo } 879209158Srpaulo os_free(buf); 880209158Srpaulo 881209158Srpaulo if (p >= buf + len) { 882209158Srpaulo errno = ESRCH; 883209158Srpaulo return -1; 884209158Srpaulo } 885209158Srpaulo return 0; 886209158Srpaulo} 887209158Srpaulo#endif /* __FreeBSD__ */ 888209158Srpaulo 889209158Srpaulo 890189251Ssam/** 891189251Ssam * get_netif_info - Get hw and IP addresses for network device 892189251Ssam * @net_if: Selected network interface name 893189251Ssam * @ip_addr: Buffer for returning IP address in network byte order 894189251Ssam * @ip_addr_text: Buffer for returning a pointer to allocated IP address text 895189251Ssam * @mac: Buffer for returning MAC address 896189251Ssam * Returns: 0 on success, -1 on failure 897189251Ssam */ 898214734Srpauloint get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text, 899214734Srpaulo u8 mac[ETH_ALEN]) 900189251Ssam{ 901189251Ssam struct ifreq req; 902189251Ssam int sock = -1; 903189251Ssam struct sockaddr_in *addr; 904189251Ssam struct in_addr in_addr; 905189251Ssam 906189251Ssam *ip_addr_text = os_zalloc(16); 907214734Srpaulo if (*ip_addr_text == NULL) 908189251Ssam goto fail; 909189251Ssam 910189251Ssam sock = socket(AF_INET, SOCK_DGRAM, 0); 911189251Ssam if (sock < 0) 912189251Ssam goto fail; 913189251Ssam 914189251Ssam os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name)); 915189251Ssam if (ioctl(sock, SIOCGIFADDR, &req) < 0) { 916189251Ssam wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFADDR failed: %d (%s)", 917189251Ssam errno, strerror(errno)); 918189251Ssam goto fail; 919189251Ssam } 920189251Ssam addr = (void *) &req.ifr_addr; 921189251Ssam *ip_addr = addr->sin_addr.s_addr; 922189251Ssam in_addr.s_addr = *ip_addr; 923189251Ssam os_snprintf(*ip_addr_text, 16, "%s", inet_ntoa(in_addr)); 924189251Ssam 925209158Srpaulo#ifdef __linux__ 926189251Ssam os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name)); 927189251Ssam if (ioctl(sock, SIOCGIFHWADDR, &req) < 0) { 928189251Ssam wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFHWADDR failed: " 929189251Ssam "%d (%s)", errno, strerror(errno)); 930189251Ssam goto fail; 931189251Ssam } 932189251Ssam os_memcpy(mac, req.ifr_addr.sa_data, 6); 933214734Srpaulo#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) 934209158Srpaulo if (eth_get(net_if, mac) < 0) { 935209158Srpaulo wpa_printf(MSG_ERROR, "WPS UPnP: Failed to get MAC address"); 936209158Srpaulo goto fail; 937209158Srpaulo } 938209158Srpaulo#else 939209158Srpaulo#error MAC address fetch not implemented 940209158Srpaulo#endif 941189251Ssam 942189251Ssam close(sock); 943189251Ssam return 0; 944189251Ssam 945189251Ssamfail: 946189251Ssam if (sock >= 0) 947189251Ssam close(sock); 948189251Ssam os_free(*ip_addr_text); 949189251Ssam *ip_addr_text = NULL; 950189251Ssam return -1; 951189251Ssam} 952189251Ssam 953189251Ssam 954214734Srpaulostatic void upnp_wps_free_msearchreply(struct dl_list *head) 955214734Srpaulo{ 956214734Srpaulo struct advertisement_state_machine *a, *tmp; 957214734Srpaulo dl_list_for_each_safe(a, tmp, head, struct advertisement_state_machine, 958214734Srpaulo list) 959214734Srpaulo msearchreply_state_machine_stop(a); 960214734Srpaulo} 961214734Srpaulo 962214734Srpaulo 963252726Srpaulostatic void upnp_wps_free_subscriptions(struct dl_list *head, 964252726Srpaulo struct wps_registrar *reg) 965214734Srpaulo{ 966214734Srpaulo struct subscription *s, *tmp; 967214734Srpaulo dl_list_for_each_safe(s, tmp, head, struct subscription, list) { 968252726Srpaulo if (reg && s->reg != reg) 969252726Srpaulo continue; 970214734Srpaulo dl_list_del(&s->list); 971214734Srpaulo subscription_destroy(s); 972214734Srpaulo } 973214734Srpaulo} 974214734Srpaulo 975214734Srpaulo 976189251Ssam/** 977189251Ssam * upnp_wps_device_stop - Stop WPS UPnP operations on an interface 978189251Ssam * @sm: WPS UPnP state machine from upnp_wps_device_init() 979189251Ssam */ 980252726Srpaulostatic void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) 981189251Ssam{ 982189251Ssam if (!sm || !sm->started) 983189251Ssam return; 984189251Ssam 985189251Ssam wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device"); 986189251Ssam web_listener_stop(sm); 987214734Srpaulo upnp_wps_free_msearchreply(&sm->msearch_replies); 988252726Srpaulo upnp_wps_free_subscriptions(&sm->subscriptions, NULL); 989189251Ssam 990209158Srpaulo advertisement_state_machine_stop(sm, 1); 991189251Ssam 992189251Ssam event_send_stop_all(sm); 993189251Ssam os_free(sm->wlanevent); 994189251Ssam sm->wlanevent = NULL; 995189251Ssam os_free(sm->ip_addr_text); 996189251Ssam sm->ip_addr_text = NULL; 997189251Ssam if (sm->multicast_sd >= 0) 998189251Ssam close(sm->multicast_sd); 999189251Ssam sm->multicast_sd = -1; 1000189251Ssam ssdp_listener_stop(sm); 1001189251Ssam 1002189251Ssam sm->started = 0; 1003189251Ssam} 1004189251Ssam 1005189251Ssam 1006189251Ssam/** 1007189251Ssam * upnp_wps_device_start - Start WPS UPnP operations on an interface 1008189251Ssam * @sm: WPS UPnP state machine from upnp_wps_device_init() 1009189251Ssam * @net_if: Selected network interface name 1010189251Ssam * Returns: 0 on success, -1 on failure 1011189251Ssam */ 1012252726Srpaulostatic int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if) 1013189251Ssam{ 1014189251Ssam if (!sm || !net_if) 1015189251Ssam return -1; 1016189251Ssam 1017189251Ssam if (sm->started) 1018189251Ssam upnp_wps_device_stop(sm); 1019189251Ssam 1020189251Ssam sm->multicast_sd = -1; 1021189251Ssam sm->ssdp_sd = -1; 1022189251Ssam sm->started = 1; 1023189251Ssam sm->advertise_count = 0; 1024189251Ssam 1025189251Ssam /* Fix up linux multicast handling */ 1026189251Ssam if (add_ssdp_network(net_if)) 1027189251Ssam goto fail; 1028189251Ssam 1029189251Ssam /* Determine which IP and mac address we're using */ 1030214734Srpaulo if (get_netif_info(net_if, &sm->ip_addr, &sm->ip_addr_text, 1031214734Srpaulo sm->mac_addr)) { 1032189251Ssam wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address " 1033189251Ssam "for %s. Does it have IP address?", net_if); 1034189251Ssam goto fail; 1035189251Ssam } 1036189251Ssam 1037189251Ssam /* Listen for incoming TCP connections so that others 1038189251Ssam * can fetch our "xml files" from us. 1039189251Ssam */ 1040189251Ssam if (web_listener_start(sm)) 1041189251Ssam goto fail; 1042189251Ssam 1043189251Ssam /* Set up for receiving discovery (UDP) packets */ 1044189251Ssam if (ssdp_listener_start(sm)) 1045189251Ssam goto fail; 1046189251Ssam 1047189251Ssam /* Set up for sending multicast */ 1048189251Ssam if (ssdp_open_multicast(sm) < 0) 1049189251Ssam goto fail; 1050189251Ssam 1051189251Ssam /* 1052189251Ssam * Broadcast NOTIFY messages to let the world know we exist. 1053189251Ssam * This is done via a state machine since the messages should not be 1054189251Ssam * all sent out at once. 1055189251Ssam */ 1056189251Ssam if (advertisement_state_machine_start(sm)) 1057189251Ssam goto fail; 1058189251Ssam 1059189251Ssam return 0; 1060189251Ssam 1061189251Ssamfail: 1062189251Ssam upnp_wps_device_stop(sm); 1063189251Ssam return -1; 1064189251Ssam} 1065189251Ssam 1066189251Ssam 1067252726Srpaulostatic struct upnp_wps_device_interface * 1068252726Srpauloupnp_wps_get_iface(struct upnp_wps_device_sm *sm, void *priv) 1069252726Srpaulo{ 1070252726Srpaulo struct upnp_wps_device_interface *iface; 1071252726Srpaulo dl_list_for_each(iface, &sm->interfaces, 1072252726Srpaulo struct upnp_wps_device_interface, list) { 1073252726Srpaulo if (iface->priv == priv) 1074252726Srpaulo return iface; 1075252726Srpaulo } 1076252726Srpaulo return NULL; 1077252726Srpaulo} 1078252726Srpaulo 1079252726Srpaulo 1080189251Ssam/** 1081189251Ssam * upnp_wps_device_deinit - Deinitialize WPS UPnP 1082189251Ssam * @sm: WPS UPnP state machine from upnp_wps_device_init() 1083252726Srpaulo * @priv: External context data that was used in upnp_wps_device_init() call 1084189251Ssam */ 1085252726Srpaulovoid upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv) 1086189251Ssam{ 1087252726Srpaulo struct upnp_wps_device_interface *iface; 1088252726Srpaulo 1089189251Ssam if (!sm) 1090189251Ssam return; 1091189251Ssam 1092252726Srpaulo iface = upnp_wps_get_iface(sm, priv); 1093252726Srpaulo if (iface == NULL) { 1094252726Srpaulo wpa_printf(MSG_ERROR, "WPS UPnP: Could not find the interface " 1095252726Srpaulo "instance to deinit"); 1096252726Srpaulo return; 1097252726Srpaulo } 1098252726Srpaulo wpa_printf(MSG_DEBUG, "WPS UPnP: Deinit interface instance %p", iface); 1099252726Srpaulo if (dl_list_len(&sm->interfaces) == 1) { 1100252726Srpaulo wpa_printf(MSG_DEBUG, "WPS UPnP: Deinitializing last instance " 1101252726Srpaulo "- free global device instance"); 1102252726Srpaulo upnp_wps_device_stop(sm); 1103252726Srpaulo } else 1104252726Srpaulo upnp_wps_free_subscriptions(&sm->subscriptions, 1105252726Srpaulo iface->wps->registrar); 1106252726Srpaulo dl_list_del(&iface->list); 1107189251Ssam 1108252726Srpaulo if (iface->peer.wps) 1109252726Srpaulo wps_deinit(iface->peer.wps); 1110252726Srpaulo os_free(iface->ctx->ap_pin); 1111252726Srpaulo os_free(iface->ctx); 1112252726Srpaulo os_free(iface); 1113252726Srpaulo 1114252726Srpaulo if (dl_list_empty(&sm->interfaces)) { 1115252726Srpaulo os_free(sm->root_dir); 1116252726Srpaulo os_free(sm->desc_url); 1117252726Srpaulo os_free(sm); 1118252726Srpaulo shared_upnp_device = NULL; 1119252726Srpaulo } 1120189251Ssam} 1121189251Ssam 1122189251Ssam 1123189251Ssam/** 1124189251Ssam * upnp_wps_device_init - Initialize WPS UPnP 1125189251Ssam * @ctx: callback table; we must eventually free it 1126189251Ssam * @wps: Pointer to longterm WPS context 1127189251Ssam * @priv: External context data that will be used in callbacks 1128252726Srpaulo * @net_if: Selected network interface name 1129189251Ssam * Returns: WPS UPnP state or %NULL on failure 1130189251Ssam */ 1131189251Ssamstruct upnp_wps_device_sm * 1132189251Ssamupnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps, 1133252726Srpaulo void *priv, char *net_if) 1134189251Ssam{ 1135189251Ssam struct upnp_wps_device_sm *sm; 1136252726Srpaulo struct upnp_wps_device_interface *iface; 1137252726Srpaulo int start = 0; 1138189251Ssam 1139252726Srpaulo iface = os_zalloc(sizeof(*iface)); 1140252726Srpaulo if (iface == NULL) { 1141252726Srpaulo os_free(ctx->ap_pin); 1142252726Srpaulo os_free(ctx); 1143189251Ssam return NULL; 1144189251Ssam } 1145252726Srpaulo wpa_printf(MSG_DEBUG, "WPS UPnP: Init interface instance %p", iface); 1146189251Ssam 1147252726Srpaulo iface->ctx = ctx; 1148252726Srpaulo iface->wps = wps; 1149252726Srpaulo iface->priv = priv; 1150189251Ssam 1151252726Srpaulo if (shared_upnp_device) { 1152252726Srpaulo wpa_printf(MSG_DEBUG, "WPS UPnP: Share existing device " 1153252726Srpaulo "context"); 1154252726Srpaulo sm = shared_upnp_device; 1155252726Srpaulo } else { 1156252726Srpaulo wpa_printf(MSG_DEBUG, "WPS UPnP: Initialize device context"); 1157252726Srpaulo sm = os_zalloc(sizeof(*sm)); 1158252726Srpaulo if (!sm) { 1159252726Srpaulo wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init " 1160252726Srpaulo "failed"); 1161252726Srpaulo os_free(iface); 1162252726Srpaulo os_free(ctx->ap_pin); 1163252726Srpaulo os_free(ctx); 1164252726Srpaulo return NULL; 1165252726Srpaulo } 1166252726Srpaulo shared_upnp_device = sm; 1167252726Srpaulo 1168252726Srpaulo dl_list_init(&sm->msearch_replies); 1169252726Srpaulo dl_list_init(&sm->subscriptions); 1170252726Srpaulo dl_list_init(&sm->interfaces); 1171252726Srpaulo start = 1; 1172252726Srpaulo } 1173252726Srpaulo 1174252726Srpaulo dl_list_add(&sm->interfaces, &iface->list); 1175252726Srpaulo 1176252726Srpaulo if (start && upnp_wps_device_start(sm, net_if)) { 1177252726Srpaulo upnp_wps_device_deinit(sm, priv); 1178252726Srpaulo return NULL; 1179252726Srpaulo } 1180252726Srpaulo 1181252726Srpaulo 1182189251Ssam return sm; 1183189251Ssam} 1184189251Ssam 1185189251Ssam 1186189251Ssam/** 1187189251Ssam * upnp_wps_subscribers - Check whether there are any event subscribers 1188189251Ssam * @sm: WPS UPnP state machine from upnp_wps_device_init() 1189189251Ssam * Returns: 0 if no subscribers, 1 if subscribers 1190189251Ssam */ 1191189251Ssamint upnp_wps_subscribers(struct upnp_wps_device_sm *sm) 1192189251Ssam{ 1193214734Srpaulo return !dl_list_empty(&sm->subscriptions); 1194189251Ssam} 1195214734Srpaulo 1196214734Srpaulo 1197214734Srpauloint upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin) 1198214734Srpaulo{ 1199252726Srpaulo struct upnp_wps_device_interface *iface; 1200214734Srpaulo if (sm == NULL) 1201214734Srpaulo return 0; 1202214734Srpaulo 1203252726Srpaulo dl_list_for_each(iface, &sm->interfaces, 1204252726Srpaulo struct upnp_wps_device_interface, list) { 1205252726Srpaulo os_free(iface->ctx->ap_pin); 1206252726Srpaulo if (ap_pin) { 1207252726Srpaulo iface->ctx->ap_pin = os_strdup(ap_pin); 1208252726Srpaulo if (iface->ctx->ap_pin == NULL) 1209252726Srpaulo return -1; 1210252726Srpaulo } else 1211252726Srpaulo iface->ctx->ap_pin = NULL; 1212252726Srpaulo } 1213214734Srpaulo 1214214734Srpaulo return 0; 1215214734Srpaulo} 1216