1209139Srpaulo/*
2209139Srpaulo * WPA Supplicant / Windows Named Pipe -based control interface
3209139Srpaulo * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
4209139Srpaulo *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7209139Srpaulo */
8209139Srpaulo
9209139Srpaulo#include "includes.h"
10209139Srpaulo
11209139Srpaulo#include "common.h"
12209139Srpaulo#include "eloop.h"
13209139Srpaulo#include "config.h"
14209139Srpaulo#include "eapol_supp/eapol_supp_sm.h"
15209139Srpaulo#include "wpa_supplicant_i.h"
16209139Srpaulo#include "ctrl_iface.h"
17214734Srpaulo#include "common/wpa_ctrl.h"
18209139Srpaulo
19209139Srpaulo#ifdef __MINGW32_VERSION
20209139Srpaulo/* mingw-w32api v3.1 does not yet include sddl.h, so define needed parts here
21209139Srpaulo */
22209139Srpaulo#define SDDL_REVISION_1 1
23209139SrpauloBOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorA(
24209139Srpaulo	LPCSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG);
25209139SrpauloBOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorW(
26209139Srpaulo	LPCWSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG);
27209139Srpaulo#ifdef UNICODE
28209139Srpaulo#define ConvertStringSecurityDescriptorToSecurityDescriptor \
29209139SrpauloConvertStringSecurityDescriptorToSecurityDescriptorW
30209139Srpaulo#else
31209139Srpaulo#define ConvertStringSecurityDescriptorToSecurityDescriptor \
32209139SrpauloConvertStringSecurityDescriptorToSecurityDescriptorA
33209139Srpaulo#endif
34209139Srpaulo#else /* __MINGW32_VERSION */
35209139Srpaulo#ifndef _WIN32_WINNT
36209139Srpaulo#define _WIN32_WINNT 0x0500
37209139Srpaulo#endif
38209139Srpaulo#include <sddl.h>
39209139Srpaulo#endif /* __MINGW32_VERSION */
40209139Srpaulo
41209139Srpaulo#ifndef WPA_SUPPLICANT_NAMED_PIPE
42209139Srpaulo#define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant"
43209139Srpaulo#endif
44209139Srpaulo#define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE)
45209139Srpaulo
46209139Srpaulo/* Per-interface ctrl_iface */
47209139Srpaulo
48209139Srpaulo#define REQUEST_BUFSIZE 256
49209139Srpaulo#define REPLY_BUFSIZE 4096
50209139Srpaulo
51209139Srpaulostruct ctrl_iface_priv;
52209139Srpaulo
53209139Srpaulo/**
54209139Srpaulo * struct wpa_ctrl_dst - Internal data structure of control interface clients
55209139Srpaulo *
56209139Srpaulo * This structure is used to store information about registered control
57209139Srpaulo * interface monitors into struct wpa_supplicant. This data is private to
58209139Srpaulo * ctrl_iface_named_pipe.c and should not be touched directly from other files.
59209139Srpaulo */
60209139Srpaulostruct wpa_ctrl_dst {
61209139Srpaulo	/* Note: OVERLAPPED must be the first member of struct wpa_ctrl_dst */
62209139Srpaulo	OVERLAPPED overlap;
63209139Srpaulo	struct wpa_ctrl_dst *next, *prev;
64209139Srpaulo	struct ctrl_iface_priv *priv;
65209139Srpaulo	HANDLE pipe;
66209139Srpaulo	int attached;
67209139Srpaulo	int debug_level;
68209139Srpaulo	int errors;
69209139Srpaulo	char req_buf[REQUEST_BUFSIZE];
70209139Srpaulo	char *rsp_buf;
71209139Srpaulo	int used;
72209139Srpaulo};
73209139Srpaulo
74209139Srpaulo
75209139Srpaulostruct ctrl_iface_priv {
76209139Srpaulo	struct wpa_supplicant *wpa_s;
77209139Srpaulo	struct wpa_ctrl_dst *ctrl_dst;
78209139Srpaulo	SECURITY_ATTRIBUTES attr;
79209139Srpaulo	int sec_attr_set;
80209139Srpaulo};
81209139Srpaulo
82209139Srpaulo
83209139Srpaulostatic void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
84209139Srpaulo					   int level, const char *buf,
85209139Srpaulo					   size_t len);
86209139Srpaulo
87209139Srpaulostatic void ctrl_close_pipe(struct wpa_ctrl_dst *dst);
88209139Srpaulostatic void wpa_supplicant_ctrl_iface_receive(void *, void *);
89209139Srpaulostatic VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes,
90209139Srpaulo					     LPOVERLAPPED overlap);
91209139Srpaulo
92209139Srpaulostruct wpa_global_dst;
93209139Srpaulostatic void global_close_pipe(struct wpa_global_dst *dst);
94209139Srpaulostatic void wpa_supplicant_global_iface_receive(void *eloop_data,
95209139Srpaulo						void *user_ctx);
96209139Srpaulostatic VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes,
97209139Srpaulo					       LPOVERLAPPED overlap);
98209139Srpaulo
99209139Srpaulo
100209139Srpaulostatic int ctrl_broken_pipe(HANDLE pipe, int used)
101209139Srpaulo{
102209139Srpaulo	DWORD err;
103209139Srpaulo
104209139Srpaulo	if (PeekNamedPipe(pipe, NULL, 0, NULL, NULL, NULL))
105209139Srpaulo		return 0;
106209139Srpaulo
107209139Srpaulo	err = GetLastError();
108209139Srpaulo	if (err == ERROR_BROKEN_PIPE || (err == ERROR_BAD_PIPE && used))
109209139Srpaulo		return 1;
110209139Srpaulo	return 0;
111209139Srpaulo}
112209139Srpaulo
113209139Srpaulo
114209139Srpaulostatic void ctrl_flush_broken_pipes(struct ctrl_iface_priv *priv)
115209139Srpaulo{
116209139Srpaulo	struct wpa_ctrl_dst *dst, *next;
117209139Srpaulo
118209139Srpaulo	dst = priv->ctrl_dst;
119209139Srpaulo
120209139Srpaulo	while (dst) {
121209139Srpaulo		next = dst->next;
122209139Srpaulo		if (ctrl_broken_pipe(dst->pipe, dst->used)) {
123209139Srpaulo			wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p",
124209139Srpaulo				   dst);
125209139Srpaulo			ctrl_close_pipe(dst);
126209139Srpaulo		}
127209139Srpaulo		dst = next;
128209139Srpaulo	}
129209139Srpaulo}
130209139Srpaulo
131209139Srpaulo
132209139Srpaulostatic int ctrl_open_pipe(struct ctrl_iface_priv *priv)
133209139Srpaulo{
134209139Srpaulo	struct wpa_ctrl_dst *dst;
135209139Srpaulo	DWORD err;
136209139Srpaulo	TCHAR name[256];
137209139Srpaulo
138209139Srpaulo	dst = os_zalloc(sizeof(*dst));
139209139Srpaulo	if (dst == NULL)
140209139Srpaulo		return -1;
141209139Srpaulo	wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst);
142209139Srpaulo
143209139Srpaulo	dst->priv = priv;
144209139Srpaulo	dst->debug_level = MSG_INFO;
145209139Srpaulo	dst->pipe = INVALID_HANDLE_VALUE;
146209139Srpaulo
147209139Srpaulo	dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
148209139Srpaulo	if (dst->overlap.hEvent == NULL) {
149209139Srpaulo		wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d",
150209139Srpaulo			   (int) GetLastError());
151209139Srpaulo		goto fail;
152209139Srpaulo	}
153209139Srpaulo
154209139Srpaulo	eloop_register_event(dst->overlap.hEvent,
155209139Srpaulo			     sizeof(dst->overlap.hEvent),
156209139Srpaulo			     wpa_supplicant_ctrl_iface_receive, dst, NULL);
157209139Srpaulo
158209139Srpaulo#ifdef UNICODE
159209139Srpaulo	_snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"),
160209139Srpaulo		   priv->wpa_s->ifname);
161209139Srpaulo#else /* UNICODE */
162209139Srpaulo	os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s",
163209139Srpaulo		    priv->wpa_s->ifname);
164209139Srpaulo#endif /* UNICODE */
165209139Srpaulo
166209139Srpaulo	/* TODO: add support for configuring access list for the pipe */
167209139Srpaulo	dst->pipe = CreateNamedPipe(name,
168209139Srpaulo				    PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
169209139Srpaulo				    PIPE_TYPE_MESSAGE |
170209139Srpaulo				    PIPE_READMODE_MESSAGE |
171209139Srpaulo				    PIPE_WAIT,
172209139Srpaulo				    15, REPLY_BUFSIZE, REQUEST_BUFSIZE,
173209139Srpaulo				    1000,
174209139Srpaulo				    priv->sec_attr_set ? &priv->attr : NULL);
175209139Srpaulo	if (dst->pipe == INVALID_HANDLE_VALUE) {
176209139Srpaulo		wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d",
177209139Srpaulo			   (int) GetLastError());
178209139Srpaulo		goto fail;
179209139Srpaulo	}
180209139Srpaulo
181209139Srpaulo	if (ConnectNamedPipe(dst->pipe, &dst->overlap)) {
182209139Srpaulo		wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d",
183209139Srpaulo			   (int) GetLastError());
184209139Srpaulo		CloseHandle(dst->pipe);
185209139Srpaulo		os_free(dst);
186209139Srpaulo		return -1;
187209139Srpaulo	}
188209139Srpaulo
189209139Srpaulo	err = GetLastError();
190209139Srpaulo	switch (err) {
191209139Srpaulo	case ERROR_IO_PENDING:
192209139Srpaulo		wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in "
193209139Srpaulo			   "progress");
194209139Srpaulo		break;
195209139Srpaulo	case ERROR_PIPE_CONNECTED:
196209139Srpaulo		wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already "
197209139Srpaulo			   "connected");
198209139Srpaulo		if (SetEvent(dst->overlap.hEvent))
199209139Srpaulo			break;
200209139Srpaulo		/* fall through */
201209139Srpaulo	default:
202209139Srpaulo		wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d",
203209139Srpaulo			   (int) err);
204209139Srpaulo		CloseHandle(dst->pipe);
205209139Srpaulo		os_free(dst);
206209139Srpaulo		return -1;
207209139Srpaulo	}
208209139Srpaulo
209209139Srpaulo	dst->next = priv->ctrl_dst;
210209139Srpaulo	if (dst->next)
211209139Srpaulo		dst->next->prev = dst;
212209139Srpaulo	priv->ctrl_dst = dst;
213209139Srpaulo
214209139Srpaulo	return 0;
215209139Srpaulo
216209139Srpaulofail:
217209139Srpaulo	ctrl_close_pipe(dst);
218209139Srpaulo	return -1;
219209139Srpaulo}
220209139Srpaulo
221209139Srpaulo
222209139Srpaulostatic void ctrl_close_pipe(struct wpa_ctrl_dst *dst)
223209139Srpaulo{
224209139Srpaulo	wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst);
225209139Srpaulo
226209139Srpaulo	if (dst->overlap.hEvent) {
227209139Srpaulo		eloop_unregister_event(dst->overlap.hEvent,
228209139Srpaulo				       sizeof(dst->overlap.hEvent));
229209139Srpaulo		CloseHandle(dst->overlap.hEvent);
230209139Srpaulo	}
231209139Srpaulo
232209139Srpaulo	if (dst->pipe != INVALID_HANDLE_VALUE) {
233209139Srpaulo		/*
234209139Srpaulo		 * Could use FlushFileBuffers() here to guarantee that all data
235209139Srpaulo		 * gets delivered to the client, but that can block, so let's
236209139Srpaulo		 * not do this for now.
237209139Srpaulo		 * FlushFileBuffers(dst->pipe);
238209139Srpaulo		 */
239209139Srpaulo		CloseHandle(dst->pipe);
240209139Srpaulo	}
241209139Srpaulo
242209139Srpaulo	if (dst->prev)
243209139Srpaulo		dst->prev->next = dst->next;
244209139Srpaulo	else
245209139Srpaulo		dst->priv->ctrl_dst = dst->next;
246209139Srpaulo	if (dst->next)
247209139Srpaulo		dst->next->prev = dst->prev;
248209139Srpaulo
249209139Srpaulo	os_free(dst->rsp_buf);
250209139Srpaulo	os_free(dst);
251209139Srpaulo}
252209139Srpaulo
253209139Srpaulo
254209139Srpaulostatic VOID WINAPI ctrl_iface_write_completed(DWORD err, DWORD bytes,
255209139Srpaulo					      LPOVERLAPPED overlap)
256209139Srpaulo{
257209139Srpaulo	struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap;
258209139Srpaulo	wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p "
259209139Srpaulo		   "err=%d bytes=%d", dst, (int) err, (int) bytes);
260209139Srpaulo	if (err) {
261209139Srpaulo		ctrl_close_pipe(dst);
262209139Srpaulo		return;
263209139Srpaulo	}
264209139Srpaulo
265209139Srpaulo	os_free(dst->rsp_buf);
266209139Srpaulo	dst->rsp_buf = NULL;
267209139Srpaulo
268209139Srpaulo	if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf),
269209139Srpaulo			&dst->overlap, ctrl_iface_read_completed)) {
270209139Srpaulo		wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d",
271209139Srpaulo			   (int) GetLastError());
272209139Srpaulo		ctrl_close_pipe(dst);
273209139Srpaulo		return;
274209139Srpaulo	}
275209139Srpaulo	wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst);
276209139Srpaulo}
277209139Srpaulo
278209139Srpaulo
279209139Srpaulostatic void wpa_supplicant_ctrl_iface_rx(struct wpa_ctrl_dst *dst, size_t len)
280209139Srpaulo{
281209139Srpaulo	struct wpa_supplicant *wpa_s = dst->priv->wpa_s;
282209139Srpaulo	char *reply = NULL, *send_buf;
283209139Srpaulo	size_t reply_len = 0, send_len;
284209139Srpaulo	int new_attached = 0;
285209139Srpaulo	char *buf = dst->req_buf;
286209139Srpaulo
287209139Srpaulo	dst->used = 1;
288209139Srpaulo	if (len >= REQUEST_BUFSIZE)
289209139Srpaulo		len = REQUEST_BUFSIZE - 1;
290209139Srpaulo	buf[len] = '\0';
291209139Srpaulo
292209139Srpaulo	if (os_strcmp(buf, "ATTACH") == 0) {
293209139Srpaulo		dst->attached = 1;
294209139Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached");
295209139Srpaulo		new_attached = 1;
296209139Srpaulo		reply_len = 2;
297209139Srpaulo	} else if (os_strcmp(buf, "DETACH") == 0) {
298209139Srpaulo		dst->attached = 0;
299209139Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached");
300209139Srpaulo		reply_len = 2;
301209139Srpaulo	} else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
302209139Srpaulo		wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", buf + 6);
303209139Srpaulo		dst->debug_level = atoi(buf + 6);
304209139Srpaulo		reply_len = 2;
305209139Srpaulo	} else {
306209139Srpaulo		reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
307209139Srpaulo							  &reply_len);
308209139Srpaulo	}
309209139Srpaulo
310209139Srpaulo	if (reply) {
311209139Srpaulo		send_buf = reply;
312209139Srpaulo		send_len = reply_len;
313209139Srpaulo	} else if (reply_len == 2) {
314209139Srpaulo		send_buf = "OK\n";
315209139Srpaulo		send_len = 3;
316209139Srpaulo	} else {
317209139Srpaulo		send_buf = "FAIL\n";
318209139Srpaulo		send_len = 5;
319209139Srpaulo	}
320209139Srpaulo
321209139Srpaulo	os_free(dst->rsp_buf);
322346981Scy	dst->rsp_buf = os_memdup(send_buf, send_len);
323209139Srpaulo	if (dst->rsp_buf == NULL) {
324209139Srpaulo		ctrl_close_pipe(dst);
325209139Srpaulo		os_free(reply);
326209139Srpaulo		return;
327209139Srpaulo	}
328209139Srpaulo	os_free(reply);
329209139Srpaulo
330209139Srpaulo	if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap,
331209139Srpaulo			 ctrl_iface_write_completed)) {
332209139Srpaulo		wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d",
333209139Srpaulo			   (int) GetLastError());
334209139Srpaulo		ctrl_close_pipe(dst);
335209139Srpaulo	} else {
336209139Srpaulo		wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p",
337209139Srpaulo			   dst);
338209139Srpaulo	}
339209139Srpaulo
340209139Srpaulo	if (new_attached)
341209139Srpaulo		eapol_sm_notify_ctrl_attached(wpa_s->eapol);
342209139Srpaulo}
343209139Srpaulo
344209139Srpaulo
345209139Srpaulostatic VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes,
346209139Srpaulo					     LPOVERLAPPED overlap)
347209139Srpaulo{
348209139Srpaulo	struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap;
349209139Srpaulo	wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d "
350209139Srpaulo		   "bytes=%d", dst, (int) err, (int) bytes);
351209139Srpaulo	if (err == 0 && bytes > 0)
352209139Srpaulo		wpa_supplicant_ctrl_iface_rx(dst, bytes);
353209139Srpaulo}
354209139Srpaulo
355209139Srpaulo
356209139Srpaulostatic void wpa_supplicant_ctrl_iface_receive(void *eloop_data, void *user_ctx)
357209139Srpaulo{
358209139Srpaulo	struct wpa_ctrl_dst *dst = eloop_data;
359209139Srpaulo	struct ctrl_iface_priv *priv = dst->priv;
360209139Srpaulo	DWORD bytes;
361209139Srpaulo
362209139Srpaulo	wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_ctrl_iface_receive");
363209139Srpaulo	ResetEvent(dst->overlap.hEvent);
364209139Srpaulo
365209139Srpaulo	if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) {
366209139Srpaulo		wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d",
367209139Srpaulo			   (int) GetLastError());
368209139Srpaulo		return;
369209139Srpaulo	}
370209139Srpaulo	wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client "
371209139Srpaulo		   "connected");
372209139Srpaulo
373209139Srpaulo	/* Open a new named pipe for the next client. */
374209139Srpaulo	ctrl_open_pipe(priv);
375209139Srpaulo
376209139Srpaulo	/* Use write completion function to start reading a command */
377209139Srpaulo	ctrl_iface_write_completed(0, 0, &dst->overlap);
378209139Srpaulo
379209139Srpaulo	ctrl_flush_broken_pipes(priv);
380209139Srpaulo}
381209139Srpaulo
382209139Srpaulo
383209139Srpaulostatic int ctrl_iface_parse(struct ctrl_iface_priv *priv, const char *params)
384209139Srpaulo{
385209139Srpaulo	const char *sddl = NULL;
386209139Srpaulo	TCHAR *t_sddl;
387209139Srpaulo
388209139Srpaulo	if (os_strncmp(params, "SDDL=", 5) == 0)
389209139Srpaulo		sddl = params + 5;
390209139Srpaulo	if (!sddl) {
391209139Srpaulo		sddl = os_strstr(params, " SDDL=");
392209139Srpaulo		if (sddl)
393209139Srpaulo			sddl += 6;
394209139Srpaulo	}
395209139Srpaulo
396209139Srpaulo	if (!sddl)
397209139Srpaulo		return 0;
398209139Srpaulo
399209139Srpaulo	wpa_printf(MSG_DEBUG, "CTRL: SDDL='%s'", sddl);
400209139Srpaulo	os_memset(&priv->attr, 0, sizeof(priv->attr));
401209139Srpaulo	priv->attr.nLength = sizeof(priv->attr);
402209139Srpaulo	priv->attr.bInheritHandle = FALSE;
403209139Srpaulo	t_sddl = wpa_strdup_tchar(sddl);
404209139Srpaulo	if (t_sddl == NULL)
405209139Srpaulo		return -1;
406209139Srpaulo	if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
407209139Srpaulo		    t_sddl, SDDL_REVISION_1,
408209139Srpaulo		    (PSECURITY_DESCRIPTOR *) (void *)
409209139Srpaulo		    &priv->attr.lpSecurityDescriptor,
410209139Srpaulo		    NULL)) {
411209139Srpaulo		os_free(t_sddl);
412209139Srpaulo		wpa_printf(MSG_ERROR, "CTRL: SDDL='%s' - could not convert to "
413209139Srpaulo			   "security descriptor: %d",
414209139Srpaulo			   sddl, (int) GetLastError());
415209139Srpaulo		return -1;
416209139Srpaulo	}
417209139Srpaulo	os_free(t_sddl);
418209139Srpaulo
419209139Srpaulo	priv->sec_attr_set = 1;
420209139Srpaulo
421209139Srpaulo	return 0;
422209139Srpaulo}
423209139Srpaulo
424209139Srpaulo
425289549Srpaulostatic void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
426289549Srpaulo					     enum wpa_msg_type type,
427209139Srpaulo					     const char *txt, size_t len)
428209139Srpaulo{
429209139Srpaulo	struct wpa_supplicant *wpa_s = ctx;
430209139Srpaulo	if (wpa_s == NULL || wpa_s->ctrl_iface == NULL)
431209139Srpaulo		return;
432209139Srpaulo	wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len);
433209139Srpaulo}
434209139Srpaulo
435209139Srpaulo
436209139Srpaulostruct ctrl_iface_priv *
437209139Srpaulowpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
438209139Srpaulo{
439209139Srpaulo	struct ctrl_iface_priv *priv;
440209139Srpaulo
441209139Srpaulo	priv = os_zalloc(sizeof(*priv));
442209139Srpaulo	if (priv == NULL)
443209139Srpaulo		return NULL;
444209139Srpaulo	priv->wpa_s = wpa_s;
445209139Srpaulo
446209139Srpaulo	if (wpa_s->conf->ctrl_interface == NULL)
447209139Srpaulo		return priv;
448209139Srpaulo
449209139Srpaulo	if (ctrl_iface_parse(priv, wpa_s->conf->ctrl_interface) < 0) {
450209139Srpaulo		os_free(priv);
451209139Srpaulo		return NULL;
452209139Srpaulo	}
453209139Srpaulo
454209139Srpaulo	if (ctrl_open_pipe(priv) < 0) {
455209139Srpaulo		os_free(priv);
456209139Srpaulo		return NULL;
457209139Srpaulo	}
458209139Srpaulo
459209139Srpaulo	wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
460209139Srpaulo
461209139Srpaulo	return priv;
462209139Srpaulo}
463209139Srpaulo
464209139Srpaulo
465209139Srpaulovoid wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
466209139Srpaulo{
467209139Srpaulo	while (priv->ctrl_dst)
468209139Srpaulo		ctrl_close_pipe(priv->ctrl_dst);
469209139Srpaulo	if (priv->sec_attr_set)
470209139Srpaulo		LocalFree(priv->attr.lpSecurityDescriptor);
471209139Srpaulo	os_free(priv);
472209139Srpaulo}
473209139Srpaulo
474209139Srpaulo
475209139Srpaulostatic void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
476209139Srpaulo					   int level, const char *buf,
477209139Srpaulo					   size_t len)
478209139Srpaulo{
479209139Srpaulo	struct wpa_ctrl_dst *dst, *next;
480209139Srpaulo	char levelstr[10];
481209139Srpaulo	int idx;
482209139Srpaulo	char *sbuf;
483209139Srpaulo	int llen;
484209139Srpaulo	DWORD written;
485209139Srpaulo
486209139Srpaulo	dst = priv->ctrl_dst;
487209139Srpaulo	if (dst == NULL)
488209139Srpaulo		return;
489209139Srpaulo
490209139Srpaulo	os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
491209139Srpaulo
492209139Srpaulo	llen = os_strlen(levelstr);
493209139Srpaulo	sbuf = os_malloc(llen + len);
494209139Srpaulo	if (sbuf == NULL)
495209139Srpaulo		return;
496209139Srpaulo
497209139Srpaulo	os_memcpy(sbuf, levelstr, llen);
498209139Srpaulo	os_memcpy(sbuf + llen, buf, len);
499209139Srpaulo
500209139Srpaulo	idx = 0;
501209139Srpaulo	while (dst) {
502209139Srpaulo		next = dst->next;
503209139Srpaulo		if (dst->attached && level >= dst->debug_level) {
504209139Srpaulo			wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %p",
505209139Srpaulo				   dst);
506209139Srpaulo			if (!WriteFile(dst->pipe, sbuf, llen + len, &written,
507209139Srpaulo				       NULL)) {
508209139Srpaulo				wpa_printf(MSG_DEBUG, "CTRL: WriteFile to dst "
509209139Srpaulo					   "%p failed: %d",
510209139Srpaulo					   dst, (int) GetLastError());
511209139Srpaulo				dst->errors++;
512209139Srpaulo				if (dst->errors > 10)
513209139Srpaulo					ctrl_close_pipe(dst);
514209139Srpaulo			} else
515209139Srpaulo				dst->errors = 0;
516209139Srpaulo		}
517209139Srpaulo		idx++;
518209139Srpaulo		dst = next;
519209139Srpaulo	}
520209139Srpaulo	os_free(sbuf);
521209139Srpaulo}
522209139Srpaulo
523209139Srpaulo
524209139Srpaulovoid wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
525209139Srpaulo{
526209139Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor",
527209139Srpaulo		   priv->wpa_s->ifname);
528209139Srpaulo	if (priv->ctrl_dst == NULL)
529209139Srpaulo		return;
530209139Srpaulo	WaitForSingleObject(priv->ctrl_dst->pipe, INFINITE);
531209139Srpaulo}
532209139Srpaulo
533209139Srpaulo
534209139Srpaulo/* Global ctrl_iface */
535209139Srpaulo
536209139Srpaulostruct ctrl_iface_global_priv;
537209139Srpaulo
538209139Srpaulostruct wpa_global_dst {
539209139Srpaulo	/* Note: OVERLAPPED must be the first member of struct wpa_global_dst
540209139Srpaulo	 */
541209139Srpaulo	OVERLAPPED overlap;
542209139Srpaulo	struct wpa_global_dst *next, *prev;
543209139Srpaulo	struct ctrl_iface_global_priv *priv;
544209139Srpaulo	HANDLE pipe;
545209139Srpaulo	char req_buf[REQUEST_BUFSIZE];
546209139Srpaulo	char *rsp_buf;
547209139Srpaulo	int used;
548209139Srpaulo};
549209139Srpaulo
550209139Srpaulostruct ctrl_iface_global_priv {
551209139Srpaulo	struct wpa_global *global;
552209139Srpaulo	struct wpa_global_dst *ctrl_dst;
553209139Srpaulo};
554209139Srpaulo
555209139Srpaulo
556209139Srpaulostatic void global_flush_broken_pipes(struct ctrl_iface_global_priv *priv)
557209139Srpaulo{
558209139Srpaulo	struct wpa_global_dst *dst, *next;
559209139Srpaulo
560209139Srpaulo	dst = priv->ctrl_dst;
561209139Srpaulo
562209139Srpaulo	while (dst) {
563209139Srpaulo		next = dst->next;
564209139Srpaulo		if (ctrl_broken_pipe(dst->pipe, dst->used)) {
565209139Srpaulo			wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p",
566209139Srpaulo				   dst);
567209139Srpaulo			global_close_pipe(dst);
568209139Srpaulo		}
569209139Srpaulo		dst = next;
570209139Srpaulo	}
571209139Srpaulo}
572209139Srpaulo
573209139Srpaulo
574209139Srpaulostatic int global_open_pipe(struct ctrl_iface_global_priv *priv)
575209139Srpaulo{
576209139Srpaulo	struct wpa_global_dst *dst;
577209139Srpaulo	DWORD err;
578209139Srpaulo
579209139Srpaulo	dst = os_zalloc(sizeof(*dst));
580209139Srpaulo	if (dst == NULL)
581209139Srpaulo		return -1;
582209139Srpaulo	wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst);
583209139Srpaulo
584209139Srpaulo	dst->priv = priv;
585209139Srpaulo	dst->pipe = INVALID_HANDLE_VALUE;
586209139Srpaulo
587209139Srpaulo	dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
588209139Srpaulo	if (dst->overlap.hEvent == NULL) {
589209139Srpaulo		wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d",
590209139Srpaulo			   (int) GetLastError());
591209139Srpaulo		goto fail;
592209139Srpaulo	}
593209139Srpaulo
594209139Srpaulo	eloop_register_event(dst->overlap.hEvent,
595209139Srpaulo			     sizeof(dst->overlap.hEvent),
596209139Srpaulo			     wpa_supplicant_global_iface_receive, dst, NULL);
597209139Srpaulo
598209139Srpaulo	/* TODO: add support for configuring access list for the pipe */
599209139Srpaulo	dst->pipe = CreateNamedPipe(NAMED_PIPE_PREFIX,
600209139Srpaulo				    PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
601209139Srpaulo				    PIPE_TYPE_MESSAGE |
602209139Srpaulo				    PIPE_READMODE_MESSAGE |
603209139Srpaulo				    PIPE_WAIT,
604209139Srpaulo				    10, REPLY_BUFSIZE, REQUEST_BUFSIZE,
605209139Srpaulo				    1000, NULL);
606209139Srpaulo	if (dst->pipe == INVALID_HANDLE_VALUE) {
607209139Srpaulo		wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d",
608209139Srpaulo			   (int) GetLastError());
609209139Srpaulo		goto fail;
610209139Srpaulo	}
611209139Srpaulo
612209139Srpaulo	if (ConnectNamedPipe(dst->pipe, &dst->overlap)) {
613209139Srpaulo		wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d",
614209139Srpaulo			   (int) GetLastError());
615209139Srpaulo		CloseHandle(dst->pipe);
616209139Srpaulo		os_free(dst);
617209139Srpaulo		return -1;
618209139Srpaulo	}
619209139Srpaulo
620209139Srpaulo	err = GetLastError();
621209139Srpaulo	switch (err) {
622209139Srpaulo	case ERROR_IO_PENDING:
623209139Srpaulo		wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in "
624209139Srpaulo			   "progress");
625209139Srpaulo		break;
626209139Srpaulo	case ERROR_PIPE_CONNECTED:
627209139Srpaulo		wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already "
628209139Srpaulo			   "connected");
629209139Srpaulo		if (SetEvent(dst->overlap.hEvent))
630209139Srpaulo			break;
631209139Srpaulo		/* fall through */
632209139Srpaulo	default:
633209139Srpaulo		wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d",
634209139Srpaulo			   (int) err);
635209139Srpaulo		CloseHandle(dst->pipe);
636209139Srpaulo		os_free(dst);
637209139Srpaulo		return -1;
638209139Srpaulo	}
639209139Srpaulo
640209139Srpaulo	dst->next = priv->ctrl_dst;
641209139Srpaulo	if (dst->next)
642209139Srpaulo		dst->next->prev = dst;
643209139Srpaulo	priv->ctrl_dst = dst;
644209139Srpaulo
645209139Srpaulo	return 0;
646209139Srpaulo
647209139Srpaulofail:
648209139Srpaulo	global_close_pipe(dst);
649209139Srpaulo	return -1;
650209139Srpaulo}
651209139Srpaulo
652209139Srpaulo
653209139Srpaulostatic void global_close_pipe(struct wpa_global_dst *dst)
654209139Srpaulo{
655209139Srpaulo	wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst);
656209139Srpaulo
657209139Srpaulo	if (dst->overlap.hEvent) {
658209139Srpaulo		eloop_unregister_event(dst->overlap.hEvent,
659209139Srpaulo				       sizeof(dst->overlap.hEvent));
660209139Srpaulo		CloseHandle(dst->overlap.hEvent);
661209139Srpaulo	}
662209139Srpaulo
663209139Srpaulo	if (dst->pipe != INVALID_HANDLE_VALUE) {
664209139Srpaulo		/*
665209139Srpaulo		 * Could use FlushFileBuffers() here to guarantee that all data
666209139Srpaulo		 * gets delivered to the client, but that can block, so let's
667209139Srpaulo		 * not do this for now.
668209139Srpaulo		 * FlushFileBuffers(dst->pipe);
669209139Srpaulo		 */
670209139Srpaulo		CloseHandle(dst->pipe);
671209139Srpaulo	}
672209139Srpaulo
673209139Srpaulo	if (dst->prev)
674209139Srpaulo		dst->prev->next = dst->next;
675209139Srpaulo	else
676209139Srpaulo		dst->priv->ctrl_dst = dst->next;
677209139Srpaulo	if (dst->next)
678209139Srpaulo		dst->next->prev = dst->prev;
679209139Srpaulo
680209139Srpaulo	os_free(dst->rsp_buf);
681209139Srpaulo	os_free(dst);
682209139Srpaulo}
683209139Srpaulo
684209139Srpaulo
685209139Srpaulostatic VOID WINAPI global_iface_write_completed(DWORD err, DWORD bytes,
686209139Srpaulo						LPOVERLAPPED overlap)
687209139Srpaulo{
688209139Srpaulo	struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap;
689209139Srpaulo	wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p "
690209139Srpaulo		   "err=%d bytes=%d", dst, (int) err, (int) bytes);
691209139Srpaulo	if (err) {
692209139Srpaulo		global_close_pipe(dst);
693209139Srpaulo		return;
694209139Srpaulo	}
695209139Srpaulo
696209139Srpaulo	os_free(dst->rsp_buf);
697209139Srpaulo	dst->rsp_buf = NULL;
698209139Srpaulo
699209139Srpaulo	if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf),
700209139Srpaulo			&dst->overlap, global_iface_read_completed)) {
701209139Srpaulo		wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d",
702209139Srpaulo			   (int) GetLastError());
703209139Srpaulo		global_close_pipe(dst);
704209139Srpaulo		/* FIX: if this was the pipe waiting for new global
705209139Srpaulo		 * connections, at this point there are no open global pipes..
706209139Srpaulo		 * Should try to open a new pipe.. */
707209139Srpaulo		return;
708209139Srpaulo	}
709209139Srpaulo	wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst);
710209139Srpaulo}
711209139Srpaulo
712209139Srpaulo
713209139Srpaulostatic void wpa_supplicant_global_iface_rx(struct wpa_global_dst *dst,
714209139Srpaulo					   size_t len)
715209139Srpaulo{
716209139Srpaulo	struct wpa_global *global = dst->priv->global;
717209139Srpaulo	char *reply = NULL, *send_buf;
718209139Srpaulo	size_t reply_len = 0, send_len;
719209139Srpaulo	char *buf = dst->req_buf;
720209139Srpaulo
721209139Srpaulo	dst->used = 1;
722209139Srpaulo	if (len >= REQUEST_BUFSIZE)
723209139Srpaulo		len = REQUEST_BUFSIZE - 1;
724209139Srpaulo	buf[len] = '\0';
725209139Srpaulo
726209139Srpaulo	reply = wpa_supplicant_global_ctrl_iface_process(global, buf,
727209139Srpaulo							 &reply_len);
728209139Srpaulo	if (reply) {
729209139Srpaulo		send_buf = reply;
730209139Srpaulo		send_len = reply_len;
731209139Srpaulo	} else if (reply_len) {
732209139Srpaulo		send_buf = "FAIL\n";
733209139Srpaulo		send_len = 5;
734209139Srpaulo	} else {
735209139Srpaulo		os_free(dst->rsp_buf);
736209139Srpaulo		dst->rsp_buf = NULL;
737209139Srpaulo		return;
738209139Srpaulo	}
739209139Srpaulo
740209139Srpaulo	os_free(dst->rsp_buf);
741346981Scy	dst->rsp_buf = os_memdup(send_buf, send_len);
742209139Srpaulo	if (dst->rsp_buf == NULL) {
743209139Srpaulo		global_close_pipe(dst);
744209139Srpaulo		os_free(reply);
745209139Srpaulo		return;
746209139Srpaulo	}
747209139Srpaulo	os_free(reply);
748209139Srpaulo
749209139Srpaulo	if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap,
750209139Srpaulo			 global_iface_write_completed)) {
751209139Srpaulo		wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d",
752209139Srpaulo			   (int) GetLastError());
753209139Srpaulo		global_close_pipe(dst);
754209139Srpaulo	} else {
755209139Srpaulo		wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p",
756209139Srpaulo			   dst);
757209139Srpaulo	}
758209139Srpaulo}
759209139Srpaulo
760209139Srpaulo
761209139Srpaulostatic VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes,
762209139Srpaulo					       LPOVERLAPPED overlap)
763209139Srpaulo{
764209139Srpaulo	struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap;
765209139Srpaulo	wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d "
766209139Srpaulo		   "bytes=%d", dst, (int) err, (int) bytes);
767209139Srpaulo	if (err == 0 && bytes > 0)
768209139Srpaulo		wpa_supplicant_global_iface_rx(dst, bytes);
769209139Srpaulo}
770209139Srpaulo
771209139Srpaulo
772209139Srpaulostatic void wpa_supplicant_global_iface_receive(void *eloop_data,
773209139Srpaulo						void *user_ctx)
774209139Srpaulo{
775209139Srpaulo	struct wpa_global_dst *dst = eloop_data;
776209139Srpaulo	struct ctrl_iface_global_priv *priv = dst->priv;
777209139Srpaulo	DWORD bytes;
778209139Srpaulo
779209139Srpaulo	wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_global_iface_receive");
780209139Srpaulo	ResetEvent(dst->overlap.hEvent);
781209139Srpaulo
782209139Srpaulo	if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) {
783209139Srpaulo		wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d",
784209139Srpaulo			   (int) GetLastError());
785209139Srpaulo		return;
786209139Srpaulo	}
787209139Srpaulo	wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client "
788209139Srpaulo		   "connected");
789209139Srpaulo
790209139Srpaulo	/* Open a new named pipe for the next client. */
791209139Srpaulo	if (global_open_pipe(priv) < 0) {
792209139Srpaulo		wpa_printf(MSG_DEBUG, "CTRL: global_open_pipe failed");
793209139Srpaulo		return;
794209139Srpaulo	}
795209139Srpaulo
796209139Srpaulo	/* Use write completion function to start reading a command */
797209139Srpaulo	global_iface_write_completed(0, 0, &dst->overlap);
798209139Srpaulo
799209139Srpaulo	global_flush_broken_pipes(priv);
800209139Srpaulo}
801209139Srpaulo
802209139Srpaulo
803209139Srpaulostruct ctrl_iface_global_priv *
804209139Srpaulowpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
805209139Srpaulo{
806209139Srpaulo	struct ctrl_iface_global_priv *priv;
807209139Srpaulo
808209139Srpaulo	priv = os_zalloc(sizeof(*priv));
809209139Srpaulo	if (priv == NULL)
810209139Srpaulo		return NULL;
811209139Srpaulo	priv->global = global;
812209139Srpaulo
813209139Srpaulo	if (global_open_pipe(priv) < 0) {
814209139Srpaulo		os_free(priv);
815209139Srpaulo		return NULL;
816209139Srpaulo	}
817209139Srpaulo
818209139Srpaulo	return priv;
819209139Srpaulo}
820209139Srpaulo
821209139Srpaulo
822209139Srpaulovoid
823209139Srpaulowpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
824209139Srpaulo{
825209139Srpaulo	while (priv->ctrl_dst)
826209139Srpaulo		global_close_pipe(priv->ctrl_dst);
827209139Srpaulo	os_free(priv);
828209139Srpaulo}
829