2
* WPA Supplicant / Windows Named Pipe -based control interface
3
* Copyright (c) 2004-2006, Jouni Malinen <jkmaline@cc.hut.fi>
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License version 2 as
7
* published by the Free Software Foundation.
9
* Alternatively, this software may be distributed under the terms of BSD
12
* See README and COPYING for more details.
21
#include "wpa_supplicant_i.h"
22
#include "ctrl_iface.h"
25
#ifdef __MINGW32_VERSION
26
/* mingw-w32api v3.1 does not yet include sddl.h, so define needed parts here
28
#define SDDL_REVISION_1 1
29
BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorA(
30
LPCSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG);
31
BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorW(
32
LPCWSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG);
34
#define ConvertStringSecurityDescriptorToSecurityDescriptor \
35
ConvertStringSecurityDescriptorToSecurityDescriptorW
37
#define ConvertStringSecurityDescriptorToSecurityDescriptor \
38
ConvertStringSecurityDescriptorToSecurityDescriptorA
40
#else /* __MINGW32_VERSION */
42
#define _WIN32_WINNT 0x0500
45
#endif /* __MINGW32_VERSION */
47
#ifndef WPA_SUPPLICANT_NAMED_PIPE
48
#define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant"
50
#define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE)
52
/* Per-interface ctrl_iface */
54
#define REQUEST_BUFSIZE 256
55
#define REPLY_BUFSIZE 4096
57
struct ctrl_iface_priv;
60
* struct wpa_ctrl_dst - Internal data structure of control interface clients
62
* This structure is used to store information about registered control
63
* interface monitors into struct wpa_supplicant. This data is private to
64
* ctrl_iface_named_pipe.c and should not be touched directly from other files.
67
/* Note: OVERLAPPED must be the first member of struct wpa_ctrl_dst */
69
struct wpa_ctrl_dst *next, *prev;
70
struct ctrl_iface_priv *priv;
75
char req_buf[REQUEST_BUFSIZE];
80
struct ctrl_iface_priv {
81
struct wpa_supplicant *wpa_s;
82
struct wpa_ctrl_dst *ctrl_dst;
83
SECURITY_ATTRIBUTES attr;
88
static void ctrl_close_pipe(struct wpa_ctrl_dst *dst);
89
static void wpa_supplicant_ctrl_iface_receive(void *, void *);
90
static VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes,
91
LPOVERLAPPED overlap);
93
struct wpa_global_dst;
94
static void global_close_pipe(struct wpa_global_dst *dst);
95
static void wpa_supplicant_global_iface_receive(void *eloop_data,
97
static VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes,
98
LPOVERLAPPED overlap);
101
static int ctrl_broken_pipe(HANDLE pipe)
105
if (PeekNamedPipe(pipe, NULL, 0, NULL, NULL, NULL))
108
err = GetLastError();
109
if (err == ERROR_BAD_PIPE || err == ERROR_BROKEN_PIPE)
115
static void ctrl_flush_broken_pipes(struct ctrl_iface_priv *priv)
117
struct wpa_ctrl_dst *dst, *next;
119
dst = priv->ctrl_dst;
123
if (ctrl_broken_pipe(dst->pipe)) {
124
wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p",
126
ctrl_close_pipe(dst);
133
static int ctrl_open_pipe(struct ctrl_iface_priv *priv)
135
struct wpa_ctrl_dst *dst;
139
ctrl_flush_broken_pipes(priv);
140
dst = wpa_zalloc(sizeof(*dst));
143
wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst);
146
dst->debug_level = MSG_INFO;
147
dst->pipe = INVALID_HANDLE_VALUE;
149
dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
150
if (dst->overlap.hEvent == NULL) {
151
wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d",
152
(int) GetLastError());
156
eloop_register_event(dst->overlap.hEvent,
157
sizeof(dst->overlap.hEvent),
158
wpa_supplicant_ctrl_iface_receive, dst, NULL);
161
_snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"),
162
priv->wpa_s->ifname);
164
snprintf(name, 256, NAMED_PIPE_PREFIX "-%s",
165
priv->wpa_s->ifname);
168
/* TODO: add support for configuring access list for the pipe */
169
dst->pipe = CreateNamedPipe(name,
170
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
172
PIPE_READMODE_MESSAGE |
174
15, REPLY_BUFSIZE, REQUEST_BUFSIZE,
176
priv->sec_attr_set ? &priv->attr : NULL);
177
if (dst->pipe == INVALID_HANDLE_VALUE) {
178
wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d",
179
(int) GetLastError());
183
if (ConnectNamedPipe(dst->pipe, &dst->overlap)) {
184
wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d",
185
(int) GetLastError());
186
CloseHandle(dst->pipe);
191
err = GetLastError();
193
case ERROR_IO_PENDING:
194
wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in "
197
case ERROR_PIPE_CONNECTED:
198
wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already "
200
if (SetEvent(dst->overlap.hEvent))
204
wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d",
206
CloseHandle(dst->pipe);
211
dst->next = priv->ctrl_dst;
213
dst->next->prev = dst;
214
priv->ctrl_dst = dst;
219
ctrl_close_pipe(dst);
224
static void ctrl_close_pipe(struct wpa_ctrl_dst *dst)
226
wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst);
228
if (dst->overlap.hEvent) {
229
eloop_unregister_event(dst->overlap.hEvent,
230
sizeof(dst->overlap.hEvent));
231
CloseHandle(dst->overlap.hEvent);
234
if (dst->pipe != INVALID_HANDLE_VALUE) {
236
* Could use FlushFileBuffers() here to guarantee that all data
237
* gets delivered to the client, but that can block, so let's
238
* not do this for now.
239
* FlushFileBuffers(dst->pipe);
241
CloseHandle(dst->pipe);
245
dst->prev->next = dst->next;
247
dst->priv->ctrl_dst = dst->next;
249
dst->next->prev = dst->prev;
256
static VOID WINAPI ctrl_iface_write_completed(DWORD err, DWORD bytes,
257
LPOVERLAPPED overlap)
259
struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap;
260
wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p "
261
"err=%d bytes=%d", dst, (int) err, (int) bytes);
263
ctrl_close_pipe(dst);
270
if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf),
271
&dst->overlap, ctrl_iface_read_completed)) {
272
wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d",
273
(int) GetLastError());
274
ctrl_close_pipe(dst);
276
wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst);
280
static void wpa_supplicant_ctrl_iface_rx(struct wpa_ctrl_dst *dst, size_t len)
282
struct wpa_supplicant *wpa_s = dst->priv->wpa_s;
283
char *reply = NULL, *send_buf;
284
size_t reply_len = 0, send_len;
285
int new_attached = 0;
286
char *buf = dst->req_buf;
288
if (len >= REQUEST_BUFSIZE)
289
len = REQUEST_BUFSIZE - 1;
292
if (strcmp(buf, "ATTACH") == 0) {
294
wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached");
297
} else if (strcmp(buf, "DETACH") == 0) {
299
wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached");
301
} else if (strncmp(buf, "LEVEL ", 6) == 0) {
302
wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", buf + 6);
303
dst->debug_level = atoi(buf + 6);
306
reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
312
send_len = reply_len;
313
} else if (reply_len == 2) {
322
dst->rsp_buf = malloc(send_len);
323
if (dst->rsp_buf == NULL) {
324
ctrl_close_pipe(dst);
328
memcpy(dst->rsp_buf, send_buf, send_len);
331
if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap,
332
ctrl_iface_write_completed)) {
333
wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d",
334
(int) GetLastError());
335
ctrl_close_pipe(dst);
337
wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p", dst);
340
eapol_sm_notify_ctrl_attached(wpa_s->eapol);
344
static VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes,
345
LPOVERLAPPED overlap)
347
struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap;
348
wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d "
349
"bytes=%d", dst, (int) err, (int) bytes);
350
if (err == 0 && bytes > 0)
351
wpa_supplicant_ctrl_iface_rx(dst, bytes);
355
static void wpa_supplicant_ctrl_iface_receive(void *eloop_data, void *user_ctx)
357
struct wpa_ctrl_dst *dst = eloop_data;
358
struct ctrl_iface_priv *priv = dst->priv;
361
wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_ctrl_iface_receive");
362
ResetEvent(dst->overlap.hEvent);
364
if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) {
365
wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d",
366
(int) GetLastError());
369
wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client "
372
/* Open a new named pipe for the next client. */
373
ctrl_open_pipe(priv);
375
/* Use write completion function to start reading a command */
376
ctrl_iface_write_completed(0, 0, &dst->overlap);
380
static int ctrl_iface_parse(struct ctrl_iface_priv *priv, const char *params)
382
const char *sddl = NULL;
385
if (strncmp(params, "SDDL=", 5) == 0)
388
sddl = strstr(params, " SDDL=");
396
wpa_printf(MSG_DEBUG, "CTRL: SDDL='%s'", sddl);
397
memset(&priv->attr, 0, sizeof(priv->attr));
398
priv->attr.nLength = sizeof(priv->attr);
399
priv->attr.bInheritHandle = FALSE;
400
t_sddl = wpa_strdup_tchar(sddl);
401
if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
402
t_sddl, SDDL_REVISION_1,
403
(PSECURITY_DESCRIPTOR *) &priv->attr.lpSecurityDescriptor,
406
wpa_printf(MSG_ERROR, "CTRL: SDDL='%s' - could not convert to "
407
"security descriptor: %d",
408
sddl, (int) GetLastError());
413
priv->sec_attr_set = 1;
419
struct ctrl_iface_priv *
420
wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
422
struct ctrl_iface_priv *priv;
424
priv = wpa_zalloc(sizeof(*priv));
429
if (wpa_s->conf->ctrl_interface == NULL)
432
if (ctrl_iface_parse(priv, wpa_s->conf->ctrl_interface) < 0) {
437
if (ctrl_open_pipe(priv) < 0) {
446
void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
448
while (priv->ctrl_dst)
449
ctrl_close_pipe(priv->ctrl_dst);
450
if (priv->sec_attr_set)
451
LocalFree(priv->attr.lpSecurityDescriptor);
456
void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, int level,
457
const char *buf, size_t len)
459
struct wpa_ctrl_dst *dst, *next;
466
dst = priv->ctrl_dst;
470
snprintf(levelstr, sizeof(levelstr), "<%d>", level);
472
llen = strlen(levelstr);
473
sbuf = malloc(llen + len);
477
memcpy(sbuf, levelstr, llen);
478
memcpy(sbuf + llen, buf, len);
483
if (dst->attached && level >= dst->debug_level) {
484
wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %p",
486
if (!WriteFile(dst->pipe, sbuf, llen + len, &written,
488
wpa_printf(MSG_DEBUG, "CTRL: WriteFile to dst "
490
dst, (int) GetLastError());
492
if (dst->errors > 10)
493
ctrl_close_pipe(dst);
504
void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
506
wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor",
507
priv->wpa_s->ifname);
508
if (priv->ctrl_dst == NULL)
510
WaitForSingleObject(priv->ctrl_dst->pipe, INFINITE);
514
/* Global ctrl_iface */
516
struct ctrl_iface_global_priv;
518
struct wpa_global_dst {
519
/* Note: OVERLAPPED must be the first member of struct wpa_global_dst
522
struct wpa_global_dst *next, *prev;
523
struct ctrl_iface_global_priv *priv;
525
char req_buf[REQUEST_BUFSIZE];
529
struct ctrl_iface_global_priv {
530
struct wpa_global *global;
531
struct wpa_global_dst *ctrl_dst;
535
static void global_flush_broken_pipes(struct ctrl_iface_global_priv *priv)
537
struct wpa_global_dst *dst, *next;
539
dst = priv->ctrl_dst;
543
if (ctrl_broken_pipe(dst->pipe)) {
544
wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p",
546
global_close_pipe(dst);
553
static int global_open_pipe(struct ctrl_iface_global_priv *priv)
555
struct wpa_global_dst *dst;
558
global_flush_broken_pipes(priv);
559
dst = wpa_zalloc(sizeof(*dst));
562
wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst);
565
dst->pipe = INVALID_HANDLE_VALUE;
567
dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
568
if (dst->overlap.hEvent == NULL) {
569
wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d",
570
(int) GetLastError());
574
eloop_register_event(dst->overlap.hEvent,
575
sizeof(dst->overlap.hEvent),
576
wpa_supplicant_global_iface_receive, dst, NULL);
578
/* TODO: add support for configuring access list for the pipe */
579
dst->pipe = CreateNamedPipe(NAMED_PIPE_PREFIX,
580
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
582
PIPE_READMODE_MESSAGE |
584
10, REPLY_BUFSIZE, REQUEST_BUFSIZE,
586
if (dst->pipe == INVALID_HANDLE_VALUE) {
587
wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d",
588
(int) GetLastError());
592
if (ConnectNamedPipe(dst->pipe, &dst->overlap)) {
593
wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d",
594
(int) GetLastError());
595
CloseHandle(dst->pipe);
600
err = GetLastError();
602
case ERROR_IO_PENDING:
603
wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in "
606
case ERROR_PIPE_CONNECTED:
607
wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already "
609
if (SetEvent(dst->overlap.hEvent))
613
wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d",
615
CloseHandle(dst->pipe);
620
dst->next = priv->ctrl_dst;
622
dst->next->prev = dst;
623
priv->ctrl_dst = dst;
628
global_close_pipe(dst);
633
static void global_close_pipe(struct wpa_global_dst *dst)
635
wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst);
637
if (dst->overlap.hEvent) {
638
eloop_unregister_event(dst->overlap.hEvent,
639
sizeof(dst->overlap.hEvent));
640
CloseHandle(dst->overlap.hEvent);
643
if (dst->pipe != INVALID_HANDLE_VALUE) {
645
* Could use FlushFileBuffers() here to guarantee that all data
646
* gets delivered to the client, but that can block, so let's
647
* not do this for now.
648
* FlushFileBuffers(dst->pipe);
650
CloseHandle(dst->pipe);
654
dst->prev->next = dst->next;
656
dst->priv->ctrl_dst = dst->next;
658
dst->next->prev = dst->prev;
665
static VOID WINAPI global_iface_write_completed(DWORD err, DWORD bytes,
666
LPOVERLAPPED overlap)
668
struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap;
669
wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p "
670
"err=%d bytes=%d", dst, (int) err, (int) bytes);
672
global_close_pipe(dst);
679
if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf),
680
&dst->overlap, global_iface_read_completed)) {
681
wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d",
682
(int) GetLastError());
683
global_close_pipe(dst);
685
wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst);
689
static void wpa_supplicant_global_iface_rx(struct wpa_global_dst *dst,
692
struct wpa_global *global = dst->priv->global;
693
char *reply = NULL, *send_buf;
694
size_t reply_len = 0, send_len;
695
char *buf = dst->req_buf;
697
if (len >= REQUEST_BUFSIZE)
698
len = REQUEST_BUFSIZE - 1;
701
reply = wpa_supplicant_global_ctrl_iface_process(global, buf,
705
send_len = reply_len;
706
} else if (reply_len) {
716
dst->rsp_buf = malloc(send_len);
717
if (dst->rsp_buf == NULL) {
718
global_close_pipe(dst);
722
memcpy(dst->rsp_buf, send_buf, send_len);
725
if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap,
726
global_iface_write_completed)) {
727
wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d",
728
(int) GetLastError());
729
global_close_pipe(dst);
731
wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p", dst);
735
static VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes,
736
LPOVERLAPPED overlap)
738
struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap;
739
wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d "
740
"bytes=%d", dst, (int) err, (int) bytes);
741
if (err == 0 && bytes > 0)
742
wpa_supplicant_global_iface_rx(dst, bytes);
746
static void wpa_supplicant_global_iface_receive(void *eloop_data,
749
struct wpa_global_dst *dst = eloop_data;
750
struct ctrl_iface_global_priv *priv = dst->priv;
753
wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_global_iface_receive");
754
ResetEvent(dst->overlap.hEvent);
756
if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) {
757
wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d",
758
(int) GetLastError());
761
wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client "
764
/* Open a new named pipe for the next client. */
765
global_open_pipe(priv);
767
/* Use write completion function to start reading a command */
768
global_iface_write_completed(0, 0, &dst->overlap);
772
struct ctrl_iface_global_priv *
773
wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
775
struct ctrl_iface_global_priv *priv;
777
priv = wpa_zalloc(sizeof(*priv));
780
priv->global = global;
782
if (global_open_pipe(priv) < 0) {
792
wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
794
while (priv->ctrl_dst)
795
global_close_pipe(priv->ctrl_dst);