• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
format_wps_device_xml(struct upnp_wps_device_interface * iface,struct upnp_wps_device_sm * sm,struct wpabuf * buf)182 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 	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 	s = iface->wps->friendly_name;
196 	s = ((s && *s) ? s : "WPS Access Point");
197 	xml_add_tagged_data(buf, "friendlyName", s);
198 
199 	s = iface->wps->dev.manufacturer;
200 	s = ((s && *s) ? s : "");
201 	xml_add_tagged_data(buf, "manufacturer", s);
202 
203 	if (iface->wps->manufacturer_url)
204 		xml_add_tagged_data(buf, "manufacturerURL",
205 				    iface->wps->manufacturer_url);
206 
207 	if (iface->wps->model_description)
208 		xml_add_tagged_data(buf, "modelDescription",
209 				    iface->wps->model_description);
210 
211 	s = iface->wps->dev.model_name;
212 	s = ((s && *s) ? s : "");
213 	xml_add_tagged_data(buf, "modelName", s);
214 
215 	if (iface->wps->dev.model_number)
216 		xml_add_tagged_data(buf, "modelNumber",
217 				    iface->wps->dev.model_number);
218 
219 	if (iface->wps->model_url)
220 		xml_add_tagged_data(buf, "modelURL", iface->wps->model_url);
221 
222 	if (iface->wps->dev.serial_number)
223 		xml_add_tagged_data(buf, "serialNumber",
224 				    iface->wps->dev.serial_number);
225 
226 	uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
227 	s = uuid_string;
228 	/* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
229 	 * easily...
230 	 */
231 	wpabuf_put_str(buf, "<UDN>uuid:");
232 	xml_data_encode(buf, s, os_strlen(s));
233 	wpabuf_put_str(buf, "</UDN>\n");
234 
235 	if (iface->wps->upc)
236 		xml_add_tagged_data(buf, "UPC", iface->wps->upc);
237 
238 	wpabuf_put_str(buf, wps_device_xml_postfix);
239 }
240 
241 
http_put_reply_code(struct wpabuf * buf,enum http_reply_code code)242 static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
243 {
244 	wpabuf_put_str(buf, "HTTP/1.1 ");
245 	switch (code) {
246 	case HTTP_OK:
247 		wpabuf_put_str(buf, "200 OK\r\n");
248 		break;
249 	case HTTP_BAD_REQUEST:
250 		wpabuf_put_str(buf, "400 Bad request\r\n");
251 		break;
252 	case HTTP_PRECONDITION_FAILED:
253 		wpabuf_put_str(buf, "412 Precondition failed\r\n");
254 		break;
255 	case HTTP_UNIMPLEMENTED:
256 		wpabuf_put_str(buf, "501 Unimplemented\r\n");
257 		break;
258 	case HTTP_INTERNAL_SERVER_ERROR:
259 	default:
260 		wpabuf_put_str(buf, "500 Internal server error\r\n");
261 		break;
262 	}
263 }
264 
265 
http_put_date(struct wpabuf * buf)266 static void http_put_date(struct wpabuf *buf)
267 {
268 	wpabuf_put_str(buf, "Date: ");
269 	format_date(buf);
270 	wpabuf_put_str(buf, "\r\n");
271 }
272 
273 
http_put_empty(struct wpabuf * buf,enum http_reply_code code)274 static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
275 {
276 	http_put_reply_code(buf, code);
277 	wpabuf_put_str(buf, http_server_hdr);
278 	wpabuf_put_str(buf, http_connection_close);
279 	wpabuf_put_str(buf, "Content-Length: 0\r\n"
280 		       "\r\n");
281 }
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  */
web_connection_parse_get(struct upnp_wps_device_sm * sm,struct http_request * hreq,const char * filename)302 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 	size_t extra_len = 0;
314 	int body_length;
315 	char len_buf[10];
316 	struct upnp_wps_device_interface *iface;
317 
318 	iface = dl_list_first(&sm->interfaces,
319 			      struct upnp_wps_device_interface, list);
320 	if (iface == NULL) {
321 		http_request_deinit(hreq);
322 		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 	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
330 		wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
331 		req = GET_DEVICE_XML_FILE;
332 		extra_len = 3000;
333 		if (iface->wps->friendly_name)
334 			extra_len += os_strlen(iface->wps->friendly_name);
335 		if (iface->wps->manufacturer_url)
336 			extra_len += os_strlen(iface->wps->manufacturer_url);
337 		if (iface->wps->model_description)
338 			extra_len += os_strlen(iface->wps->model_description);
339 		if (iface->wps->model_url)
340 			extra_len += os_strlen(iface->wps->model_url);
341 		if (iface->wps->upc)
342 			extra_len += os_strlen(iface->wps->upc);
343 	} else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
344 		wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
345 		req = GET_SCPD_XML_FILE;
346 		extra_len = os_strlen(wps_scpd_xml);
347 	} else {
348 		/* File not found */
349 		wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s",
350 			   filename);
351 		buf = wpabuf_alloc(200);
352 		if (buf == NULL) {
353 			http_request_deinit(hreq);
354 			return;
355 		}
356 		wpabuf_put_str(buf,
357 			       "HTTP/1.1 404 Not Found\r\n"
358 			       "Connection: close\r\n");
359 
360 		http_put_date(buf);
361 
362 		/* terminating empty line */
363 		wpabuf_put_str(buf, "\r\n");
364 
365 		goto send_buf;
366 	}
367 
368 	buf = wpabuf_alloc(1000 + extra_len);
369 	if (buf == NULL) {
370 		http_request_deinit(hreq);
371 		return;
372 	}
373 
374 	wpabuf_put_str(buf,
375 		       "HTTP/1.1 200 OK\r\n"
376 		       "Content-Type: text/xml; charset=\"utf-8\"\r\n");
377 	wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n");
378 	wpabuf_put_str(buf, "Connection: close\r\n");
379 	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 	put_length_here = wpabuf_put(buf, 0);
385 	wpabuf_put_str(buf, "        \r\n");
386 
387 	http_put_date(buf);
388 
389 	/* terminating empty line */
390 	wpabuf_put_str(buf, "\r\n");
391 
392 	body_start = wpabuf_put(buf, 0);
393 
394 	switch (req) {
395 	case GET_DEVICE_XML_FILE:
396 		format_wps_device_xml(iface, sm, buf);
397 		break;
398 	case GET_SCPD_XML_FILE:
399 		wpabuf_put_str(buf, wps_scpd_xml);
400 		break;
401 	}
402 
403 	/* Now patch in the content length at the end */
404 	body_length = (char *) wpabuf_put(buf, 0) - body_start;
405 	os_snprintf(len_buf, 10, "%d", body_length);
406 	os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
407 
408 send_buf:
409 	http_request_send_and_deinit(hreq, buf);
410 }
411 
412 
wps_upnp_peer_del(struct upnp_wps_peer * peer)413 static void wps_upnp_peer_del(struct upnp_wps_peer *peer)
414 {
415 	dl_list_del(&peer->list);
416 	if (peer->wps)
417 		wps_deinit(peer->wps);
418 	os_free(peer);
419 }
420 
421 
422 static enum http_reply_code
web_process_get_device_info(struct upnp_wps_device_sm * sm,struct wpabuf ** reply,const char ** replyname)423 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 	iface = dl_list_first(&sm->interfaces,
432 			      struct upnp_wps_device_interface, list);
433 
434 	wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
435 
436 	if (!iface || iface->ctx->ap_pin == NULL)
437 		return HTTP_INTERNAL_SERVER_ERROR;
438 
439 	peer = os_zalloc(sizeof(*peer));
440 	if (!peer)
441 		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 	os_memset(&cfg, 0, sizeof(cfg));
452 	cfg.wps = iface->wps;
453 	cfg.pin = (u8 *) iface->ctx->ap_pin;
454 	cfg.pin_len = os_strlen(iface->ctx->ap_pin);
455 	peer->wps = wps_init(&cfg);
456 	if (peer->wps) {
457 		enum wsc_op_code op_code;
458 		*reply = wps_get_msg(peer->wps, &op_code);
459 		if (*reply == NULL) {
460 			wps_deinit(peer->wps);
461 			peer->wps = NULL;
462 		}
463 	} else
464 		*reply = NULL;
465 	if (*reply == NULL) {
466 		wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
467 		os_free(peer);
468 		return HTTP_INTERNAL_SERVER_ERROR;
469 	}
470 
471 	if (dl_list_len(&iface->peers) > 3) {
472 		struct upnp_wps_peer *old;
473 
474 		old = dl_list_first(&iface->peers, struct upnp_wps_peer, list);
475 		if (old) {
476 			wpa_printf(MSG_DEBUG, "WPS UPnP: Drop oldest active session");
477 			wps_upnp_peer_del(old);
478 		}
479 	}
480 	dl_list_add_tail(&iface->peers, &peer->list);
481 	/* TODO: Could schedule a timeout to free the entry */
482 
483 	*replyname = name;
484 	return HTTP_OK;
485 }
486 
487 
488 static enum http_reply_code
web_process_put_message(struct upnp_wps_device_sm * sm,char * data,struct wpabuf ** reply,const char ** replyname)489 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 	iface = dl_list_first(&sm->interfaces,
502 			      struct upnp_wps_device_interface, list);
503 	if (!iface)
504 		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 	wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
512 	msg = xml_get_base64_item(data, "NewInMessage", &ret);
513 	if (msg == NULL)
514 		return ret;
515 
516 	if (wps_parse_msg(msg, &attr)) {
517 		wpa_printf(MSG_DEBUG,
518 			   "WPS UPnP: Could not parse PutMessage - NewInMessage");
519 		wpabuf_free(msg);
520 		return HTTP_BAD_REQUEST;
521 	}
522 
523 	/* Find a matching active peer session */
524 	peer = NULL;
525 	dl_list_for_each(tmp, &iface->peers, struct upnp_wps_peer, list) {
526 		if (!tmp->wps)
527 			continue;
528 		if (attr.enrollee_nonce &&
529 		    os_memcmp(tmp->wps->nonce_e, attr.enrollee_nonce,
530 			      WPS_NONCE_LEN) != 0)
531 			continue; /* Enrollee nonce mismatch */
532 		if (attr.msg_type &&
533 		    *attr.msg_type != WPS_M2 &&
534 		    *attr.msg_type != WPS_M2D &&
535 		    attr.registrar_nonce &&
536 		    os_memcmp(tmp->wps->nonce_r, attr.registrar_nonce,
537 			      WPS_NONCE_LEN) != 0)
538 			continue; /* Registrar nonce mismatch */
539 		peer = tmp;
540 		break;
541 	}
542 	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 		peer = dl_list_first(&iface->peers, struct upnp_wps_peer, list);
550 	}
551 	if (!peer || !peer->wps) {
552 		wpa_printf(MSG_DEBUG, "WPS UPnP: No active peer entry found");
553 		wpabuf_free(msg);
554 		return HTTP_BAD_REQUEST;
555 	}
556 
557 	res = wps_process_msg(peer->wps, WSC_UPnP, msg);
558 	if (res == WPS_FAILURE) {
559 		*reply = NULL;
560 		wpa_printf(MSG_DEBUG, "WPS UPnP: Drop active peer session");
561 		wps_upnp_peer_del(peer);
562 	} else {
563 		*reply = wps_get_msg(peer->wps, &op_code);
564 	}
565 	wpabuf_free(msg);
566 	if (*reply == NULL)
567 		return HTTP_INTERNAL_SERVER_ERROR;
568 	*replyname = name;
569 	return HTTP_OK;
570 }
571 
572 
573 static enum http_reply_code
web_process_put_wlan_response(struct upnp_wps_device_sm * sm,char * data,struct wpabuf ** reply,const char ** replyname)574 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 	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 	wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
592 	msg = xml_get_base64_item(data, "NewMessage", &ret);
593 	if (msg == NULL) {
594 		wpa_printf(MSG_DEBUG, "WPS UPnP: Could not extract NewMessage "
595 			   "from PutWLANResponse");
596 		return ret;
597 	}
598 	val = xml_get_first_item(data, "NewWLANEventType");
599 	if (val == NULL) {
600 		wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventType in "
601 			   "PutWLANResponse");
602 		wpabuf_free(msg);
603 		return UPNP_ARG_VALUE_INVALID;
604 	}
605 	ev_type = atol(val);
606 	os_free(val);
607 	val = xml_get_first_item(data, "NewWLANEventMAC");
608 	if (val == NULL) {
609 		wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventMAC in "
610 			   "PutWLANResponse");
611 		wpabuf_free(msg);
612 		return UPNP_ARG_VALUE_INVALID;
613 	}
614 	if (hwaddr_aton(val, macaddr)) {
615 		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 		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 			wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow "
633 				   "incorrect MAC address format in "
634 				   "NewWLANEventMAC: %s -> " MACSTR,
635 				   val, MAC2STR(macaddr));
636 		} else {
637 			wpabuf_free(msg);
638 			os_free(val);
639 			return UPNP_ARG_VALUE_INVALID;
640 		}
641 	}
642 	os_free(val);
643 	if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
644 		struct wps_parse_attr attr;
645 		if (wps_parse_msg(msg, &attr) < 0 ||
646 		    attr.msg_type == NULL)
647 			type = -1;
648 		else
649 			type = *attr.msg_type;
650 		wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
651 	} else
652 		type = -1;
653 	dl_list_for_each(iface, &sm->interfaces,
654 			 struct upnp_wps_device_interface, list) {
655 		if (iface->ctx->rx_req_put_wlan_response &&
656 		    iface->ctx->rx_req_put_wlan_response(iface->priv, ev_type,
657 							 macaddr, msg, type)
658 		    == 0)
659 			ok = 1;
660 	}
661 
662 	if (!ok) {
663 		wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
664 			   "rx_req_put_wlan_response");
665 		wpabuf_free(msg);
666 		return HTTP_INTERNAL_SERVER_ERROR;
667 	}
668 	wpabuf_free(msg);
669 	*replyname = NULL;
670 	*reply = NULL;
671 	return HTTP_OK;
672 }
673 
674 
find_er_addr(struct subscription * s,struct sockaddr_in * cli)675 static int find_er_addr(struct subscription *s, struct sockaddr_in *cli)
676 {
677 	struct subscr_addr *a;
678 
679 	dl_list_for_each(a, &s->addr_list, struct subscr_addr, list) {
680 		if (cli->sin_addr.s_addr == a->saddr.sin_addr.s_addr)
681 			return 1;
682 	}
683 	return 0;
684 }
685 
686 
find_er(struct upnp_wps_device_sm * sm,struct sockaddr_in * cli)687 static struct subscription * find_er(struct upnp_wps_device_sm *sm,
688 				     struct sockaddr_in *cli)
689 {
690 	struct subscription *s;
691 	dl_list_for_each(s, &sm->subscriptions, struct subscription, list)
692 		if (find_er_addr(s, cli))
693 			return s;
694 	return NULL;
695 }
696 
697 
698 static enum http_reply_code
web_process_set_selected_registrar(struct upnp_wps_device_sm * sm,struct sockaddr_in * cli,char * data,struct wpabuf ** reply,const char ** replyname)699 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 	int err = 0;
709 
710 	wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
711 	s = find_er(sm, cli);
712 	if (s == NULL) {
713 		wpa_printf(MSG_DEBUG, "WPS UPnP: Ignore SetSelectedRegistrar "
714 			   "from unknown ER");
715 		return UPNP_ACTION_FAILED;
716 	}
717 	msg = xml_get_base64_item(data, "NewMessage", &ret);
718 	if (msg == NULL)
719 		return ret;
720 	dl_list_for_each(iface, &sm->interfaces,
721 			 struct upnp_wps_device_interface, list) {
722 		if (upnp_er_set_selected_registrar(iface->wps->registrar, s,
723 						   msg))
724 			err = 1;
725 	}
726 	wpabuf_free(msg);
727 	if (err)
728 		return HTTP_INTERNAL_SERVER_ERROR;
729 	*replyname = NULL;
730 	*reply = NULL;
731 	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 
web_connection_send_reply(struct http_request * req,enum http_reply_code ret,const char * action,int action_len,const struct wpabuf * reply,const char * replyname)755 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 	char *put_length_here = NULL;
764 	char *body_start = NULL;
765 
766 	if (reply) {
767 		size_t len;
768 		replydata = base64_encode(wpabuf_head(reply), wpabuf_len(reply),
769 					  &len);
770 	} else
771 		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 	buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
779 			   (action_len > 0 ? action_len * 2 : 0));
780 	if (buf == NULL) {
781 		wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to "
782 			   "POST");
783 		os_free(replydata);
784 		http_request_deinit(req);
785 		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 	if (ret == HTTP_OK) {
794 		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 		wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
800 	}
801 	wpabuf_put_str(buf, http_connection_close);
802 
803 	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 	put_length_here = wpabuf_put(buf, 0);
809 	wpabuf_put_str(buf, "        \r\n");
810 
811 	http_put_date(buf);
812 
813 	/* terminating empty line */
814 	wpabuf_put_str(buf, "\r\n");
815 
816 	body_start = wpabuf_put(buf, 0);
817 
818 	if (ret == HTTP_OK) {
819 		wpabuf_put_str(buf, soap_prefix);
820 		wpabuf_put_str(buf, "<u:");
821 		wpabuf_put_data(buf, action, action_len);
822 		wpabuf_put_str(buf, "Response xmlns:u=\"");
823 		wpabuf_put_str(buf, urn_wfawlanconfig);
824 		wpabuf_put_str(buf, "\">\n");
825 		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 			wpabuf_printf(buf, "<%s>", replyname);
832 			wpabuf_put_str(buf, replydata);
833 			wpabuf_printf(buf, "</%s>\n", replyname);
834 		}
835 		wpabuf_put_str(buf, "</u:");
836 		wpabuf_put_data(buf, action, action_len);
837 		wpabuf_put_str(buf, "Response>\n");
838 		wpabuf_put_str(buf, soap_postfix);
839 	} else {
840 		/* Error case */
841 		wpabuf_put_str(buf, soap_prefix);
842 		wpabuf_put_str(buf, soap_error_prefix);
843 		wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
844 		wpabuf_put_str(buf, soap_error_postfix);
845 		wpabuf_put_str(buf, soap_postfix);
846 	}
847 	os_free(replydata);
848 
849 	/* Now patch in the content length at the end */
850 	if (body_start && put_length_here) {
851 		int body_length = (char *) wpabuf_put(buf, 0) - body_start;
852 		char len_buf[10];
853 		os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
854 		os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
855 	}
856 
857 	http_request_send_and_deinit(req, buf);
858 }
859 
860 
web_get_action(struct http_request * req,size_t * action_len)861 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 	*action_len = 0;
870 	/* The SOAPAction line of the header tells us what we want to do */
871 	b = http_request_get_hdr_line(req, "SOAPAction:");
872 	if (b == NULL)
873 		return NULL;
874 	if (*b == '"')
875 		b++;
876 	else
877 		return NULL;
878 	match = urn_wfawlanconfig;
879 	match_len = os_strlen(urn_wfawlanconfig) - 1;
880 	if (os_strncasecmp(b, match, match_len))
881 		return NULL;
882 	b += match_len;
883 	/* skip over version */
884 	while (isgraph(*b) && *b != '#')
885 		b++;
886 	if (*b != '#')
887 		return NULL;
888 	b++;
889 	/* Following the sharp(#) should be the action and a double quote */
890 	action = b;
891 	while (isgraph(*b) && *b != '"')
892 		b++;
893 	if (*b != '"')
894 		return NULL;
895 	*action_len = b - action;
896 	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  */
web_connection_parse_post(struct upnp_wps_device_sm * sm,struct sockaddr_in * cli,struct http_request * req,const char * filename)918 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 	char *data = http_request_get_data(req); /* body of http msg */
925 	const char *action = NULL;
926 	size_t action_len = 0;
927 	const char *replyname = NULL; /* argument name for the reply */
928 	struct wpabuf *reply = NULL; /* data for the reply */
929 
930 	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
931 		wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
932 			   filename);
933 		ret = HTTP_NOT_FOUND;
934 		goto bad;
935 	}
936 
937 	ret = UPNP_INVALID_ACTION;
938 	action = web_get_action(req, &action_len);
939 	if (action == NULL)
940 		goto bad;
941 
942 	if (!os_strncasecmp("GetDeviceInfo", action, action_len))
943 		ret = web_process_get_device_info(sm, &reply, &replyname);
944 	else if (!os_strncasecmp("PutMessage", action, action_len))
945 		ret = web_process_put_message(sm, data, &reply, &replyname);
946 	else if (!os_strncasecmp("PutWLANResponse", action, action_len))
947 		ret = web_process_put_wlan_response(sm, data, &reply,
948 						    &replyname);
949 	else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
950 		ret = web_process_set_selected_registrar(sm, cli, data, &reply,
951 							 &replyname);
952 	else
953 		wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type");
954 
955 bad:
956 	if (ret != HTTP_OK)
957 		wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
958 	web_connection_send_reply(req, ret, action, action_len, reply,
959 				  replyname);
960 	wpabuf_free(reply);
961 }
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  */
web_connection_parse_subscribe(struct upnp_wps_device_sm * sm,struct http_request * req,const char * filename)983 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 	char *hdr = http_request_get_hdr(req);
990 	char *h;
991 	char *match;
992 	int match_len;
993 	char *end;
994 	int len;
995 	int got_nt = 0;
996 	u8 uuid[UUID_LEN];
997 	int got_uuid = 0;
998 	char *callback_urls = NULL;
999 	struct subscription *s = NULL;
1000 	enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
1001 
1002 	buf = wpabuf_alloc(1000);
1003 	if (buf == NULL) {
1004 		http_request_deinit(req);
1005 		return;
1006 	}
1007 
1008 	wpa_hexdump_ascii(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE",
1009 			  (u8 *) hdr, os_strlen(hdr));
1010 
1011 	/* Parse/validate headers */
1012 	h = hdr;
1013 	/* First line: SUBSCRIBE /wps_event HTTP/1.1
1014 	 * has already been parsed.
1015 	 */
1016 	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
1017 		ret = HTTP_PRECONDITION_FAILED;
1018 		goto error;
1019 	}
1020 	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
1021 	end = os_strchr(h, '\n');
1022 
1023 	while (end) {
1024 		/* Option line by option line */
1025 		h = end + 1;
1026 		end = os_strchr(h, '\n');
1027 		if (end == NULL)
1028 			break; /* no unterminated lines allowed */
1029 
1030 		/* NT assures that it is our type of subscription;
1031 		 * not used for a renewal.
1032 		 **/
1033 		match = "NT:";
1034 		match_len = os_strlen(match);
1035 		if (os_strncasecmp(h, match, match_len) == 0) {
1036 			h += match_len;
1037 			while (*h == ' ' || *h == '\t')
1038 				h++;
1039 			match = "upnp:event";
1040 			match_len = os_strlen(match);
1041 			if (os_strncasecmp(h, match, match_len) != 0) {
1042 				ret = HTTP_BAD_REQUEST;
1043 				goto error;
1044 			}
1045 			got_nt = 1;
1046 			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 		match = "CALLBACK:";
1064 		match_len = os_strlen(match);
1065 		if (os_strncasecmp(h, match, match_len) == 0) {
1066 			h += match_len;
1067 			while (*h == ' ' || *h == '\t')
1068 				h++;
1069 			len = end - h;
1070 			os_free(callback_urls);
1071 			callback_urls = dup_binstr(h, len);
1072 			if (callback_urls == NULL) {
1073 				ret = HTTP_INTERNAL_SERVER_ERROR;
1074 				goto error;
1075 			}
1076 			if (len > 0 && callback_urls[len - 1] == '\r')
1077 				callback_urls[len - 1] = '\0';
1078 			continue;
1079 		}
1080 		/* SID is only for renewal */
1081 		match = "SID:";
1082 		match_len = os_strlen(match);
1083 		if (os_strncasecmp(h, match, match_len) == 0) {
1084 			h += match_len;
1085 			while (*h == ' ' || *h == '\t')
1086 				h++;
1087 			match = "uuid:";
1088 			match_len = os_strlen(match);
1089 			if (os_strncasecmp(h, match, match_len) != 0) {
1090 				ret = HTTP_BAD_REQUEST;
1091 				goto error;
1092 			}
1093 			h += match_len;
1094 			while (*h == ' ' || *h == '\t')
1095 				h++;
1096 			if (uuid_str2bin(h, uuid)) {
1097 				ret = HTTP_BAD_REQUEST;
1098 				goto error;
1099 			}
1100 			got_uuid = 1;
1101 			continue;
1102 		}
1103 		/* TIMEOUT is requested timeout, but apparently we can
1104 		 * just ignore this.
1105 		 */
1106 	}
1107 
1108 	if (got_uuid) {
1109 		/* renewal */
1110 		wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewal");
1111 		if (callback_urls) {
1112 			ret = HTTP_BAD_REQUEST;
1113 			goto error;
1114 		}
1115 		s = subscription_renew(sm, uuid);
1116 		if (s == NULL) {
1117 			char str[80];
1118 			uuid_bin2str(uuid, str, sizeof(str));
1119 			wpa_printf(MSG_DEBUG, "WPS UPnP: Could not find "
1120 				   "SID %s", str);
1121 			ret = HTTP_PRECONDITION_FAILED;
1122 			goto error;
1123 		}
1124 	} else if (callback_urls) {
1125 		wpa_printf(MSG_DEBUG, "WPS UPnP: New subscription");
1126 		if (!got_nt) {
1127 			ret = HTTP_PRECONDITION_FAILED;
1128 			goto error;
1129 		}
1130 		s = subscription_start(sm, callback_urls);
1131 		if (s == NULL) {
1132 			ret = HTTP_INTERNAL_SERVER_ERROR;
1133 			goto error;
1134 		}
1135 	} else {
1136 		ret = HTTP_PRECONDITION_FAILED;
1137 		goto error;
1138 	}
1139 
1140 	/* success */
1141 	http_put_reply_code(buf, HTTP_OK);
1142 	wpabuf_put_str(buf, http_server_hdr);
1143 	wpabuf_put_str(buf, http_connection_close);
1144 	wpabuf_put_str(buf, "Content-Length: 0\r\n");
1145 	wpabuf_put_str(buf, "SID: uuid:");
1146 	/* subscription id */
1147 	b = wpabuf_put(buf, 0);
1148 	uuid_bin2str(s->uuid, b, 80);
1149 	wpa_printf(MSG_DEBUG, "WPS UPnP: Assigned SID %s", b);
1150 	wpabuf_put(buf, os_strlen(b));
1151 	wpabuf_put_str(buf, "\r\n");
1152 	wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
1153 	http_put_date(buf);
1154 	/* And empty line to terminate header: */
1155 	wpabuf_put_str(buf, "\r\n");
1156 
1157 	os_free(callback_urls);
1158 	http_request_send_and_deinit(req, buf);
1159 	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 	wpa_printf(MSG_DEBUG, "WPS UPnP: SUBSCRIBE failed - return %d", ret);
1184 	http_put_empty(buf, ret);
1185 	http_request_send_and_deinit(req, buf);
1186 	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  */
web_connection_parse_unsubscribe(struct upnp_wps_device_sm * sm,struct http_request * req,const char * filename)1205 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 	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 	int got_uuid = 0;
1217 	struct subscription *s = NULL;
1218 	enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
1219 
1220 	/* Parse/validate headers */
1221 	h = hdr;
1222 	/* First line: UNSUBSCRIBE /wps_event HTTP/1.1
1223 	 * has already been parsed.
1224 	 */
1225 	if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
1226 		ret = HTTP_PRECONDITION_FAILED;
1227 		goto send_msg;
1228 	}
1229 	wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
1230 	end = os_strchr(h, '\n');
1231 
1232 	while (end) {
1233 		/* Option line by option line */
1234 		h = end + 1;
1235 		end = os_strchr(h, '\n');
1236 		if (end == NULL)
1237 			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 		match = "SID:";
1251 		match_len = os_strlen(match);
1252 		if (os_strncasecmp(h, match, match_len) == 0) {
1253 			h += match_len;
1254 			while (*h == ' ' || *h == '\t')
1255 				h++;
1256 			match = "uuid:";
1257 			match_len = os_strlen(match);
1258 			if (os_strncasecmp(h, match, match_len) != 0) {
1259 				ret = HTTP_BAD_REQUEST;
1260 				goto send_msg;
1261 			}
1262 			h += match_len;
1263 			while (*h == ' ' || *h == '\t')
1264 				h++;
1265 			if (uuid_str2bin(h, uuid)) {
1266 				ret = HTTP_BAD_REQUEST;
1267 				goto send_msg;
1268 			}
1269 			got_uuid = 1;
1270 			continue;
1271 		}
1272 
1273 		match = "NT:";
1274 		match_len = os_strlen(match);
1275 		if (os_strncasecmp(h, match, match_len) == 0) {
1276 			ret = HTTP_BAD_REQUEST;
1277 			goto send_msg;
1278 		}
1279 
1280 		match = "CALLBACK:";
1281 		match_len = os_strlen(match);
1282 		if (os_strncasecmp(h, match, match_len) == 0) {
1283 			ret = HTTP_BAD_REQUEST;
1284 			goto send_msg;
1285 		}
1286 	}
1287 
1288 	if (got_uuid) {
1289 		char str[80];
1290 
1291 		uuid_bin2str(uuid, str, sizeof(str));
1292 
1293 		s = subscription_find(sm, uuid);
1294 		if (s) {
1295 			struct subscr_addr *sa;
1296 			sa = dl_list_first(&s->addr_list, struct subscr_addr,
1297 					   list);
1298 			wpa_printf(MSG_DEBUG,
1299 				   "WPS UPnP: Unsubscribing %p (SID %s) %s",
1300 				   s, str, (sa && sa->domain_and_port) ?
1301 				   sa->domain_and_port : "-null-");
1302 			dl_list_del(&s->list);
1303 			subscription_destroy(s);
1304 		} else {
1305 			wpa_printf(MSG_INFO,
1306 				   "WPS UPnP: Could not find matching subscription to unsubscribe (SID %s)",
1307 				   str);
1308 			ret = HTTP_PRECONDITION_FAILED;
1309 			goto send_msg;
1310 		}
1311 	} else {
1312 		wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
1313 			   "found)");
1314 		ret = HTTP_PRECONDITION_FAILED;
1315 		goto send_msg;
1316 	}
1317 
1318 	ret = HTTP_OK;
1319 
1320 send_msg:
1321 	buf = wpabuf_alloc(200);
1322 	if (buf == NULL) {
1323 		http_request_deinit(req);
1324 		return;
1325 	}
1326 	http_put_empty(buf, ret);
1327 	http_request_send_and_deinit(req, buf);
1328 }
1329 
1330 
1331 /* Send error in response to unknown requests */
web_connection_unimplemented(struct http_request * req)1332 static void web_connection_unimplemented(struct http_request *req)
1333 {
1334 	struct wpabuf *buf;
1335 	buf = wpabuf_alloc(200);
1336 	if (buf == NULL) {
1337 		http_request_deinit(req);
1338 		return;
1339 	}
1340 	http_put_empty(buf, HTTP_UNIMPLEMENTED);
1341 	http_request_send_and_deinit(req, buf);
1342 }
1343 
1344 
1345 
1346 /* Called when we have gotten an apparently valid http request.
1347  */
web_connection_check_data(void * ctx,struct http_request * req)1348 static void web_connection_check_data(void *ctx, struct http_request *req)
1349 {
1350 	struct upnp_wps_device_sm *sm = ctx;
1351 	enum httpread_hdr_type htype = http_request_get_type(req);
1352 	char *filename = http_request_get_uri(req);
1353 	struct sockaddr_in *cli = http_request_get_cli_addr(req);
1354 
1355 	if (!filename) {
1356 		wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
1357 		http_request_deinit(req);
1358 		return;
1359 	}
1360 	/* Trim leading slashes from filename */
1361 	while (*filename == '/')
1362 		filename++;
1363 
1364 	wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
1365 		   htype, inet_ntoa(cli->sin_addr), htons(cli->sin_port));
1366 
1367 	switch (htype) {
1368 	case HTTPREAD_HDR_TYPE_GET:
1369 		web_connection_parse_get(sm, req, filename);
1370 		break;
1371 	case HTTPREAD_HDR_TYPE_POST:
1372 		web_connection_parse_post(sm, cli, req, filename);
1373 		break;
1374 	case HTTPREAD_HDR_TYPE_SUBSCRIBE:
1375 		web_connection_parse_subscribe(sm, req, filename);
1376 		break;
1377 	case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
1378 		web_connection_parse_unsubscribe(sm, req, filename);
1379 		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 		web_connection_unimplemented(req);
1389 		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 
web_listener_stop(struct upnp_wps_device_sm * sm)1400 void web_listener_stop(struct upnp_wps_device_sm *sm)
1401 {
1402 	http_server_deinit(sm->web_srv);
1403 	sm->web_srv = NULL;
1404 }
1405 
1406 
web_listener_start(struct upnp_wps_device_sm * sm)1407 int web_listener_start(struct upnp_wps_device_sm *sm)
1408 {
1409 	struct in_addr addr;
1410 	addr.s_addr = sm->ip_addr;
1411 	sm->web_srv = http_server_init(&addr, -1, web_connection_check_data,
1412 				       sm);
1413 	if (sm->web_srv == NULL) {
1414 		web_listener_stop(sm);
1415 		return -1;
1416 	}
1417 	sm->web_port = http_server_get_port(sm->web_srv);
1418 
1419 	return 0;
1420 }
1421