2 * Copyright (c) 2016, 2017 Cloudbase Solutions Srl
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <sys/types.h>
23 #include "openvswitch/poll-loop.h"
25 #include "fatal-signal.h"
27 #include "stream-provider.h"
28 #include "openvswitch/vlog.h"
30 VLOG_DEFINE_THIS_MODULE(stream_windows
);
32 static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(10, 25);
34 static void maybe_unlink_and_free(char *path
);
36 /* Suggested buffer size at the creation of the named pipe for reading and
37 * and writing operations. */
40 /* Default prefix of a local named pipe. */
41 #define LOCAL_PREFIX "\\\\.\\pipe\\"
43 /* Size of the allowed PSIDs for securing Named Pipe. */
44 #define ALLOWED_PSIDS_SIZE 2
46 /* This function has the purpose to remove all the slashes received in s. */
48 remove_slashes(char *s
)
54 if ((*p1
) == '\\' || (*p1
) == '/') {
66 /* Active named pipe. */
71 /* Overlapped operations used for reading/writing. */
74 /* Flag to check if a reading/writing operation is pending. */
77 /* Flag to check if fd is a server HANDLE. In the case of a server handle
78 * we have to issue a disconnect before closing the actual handle. */
84 static struct windows_stream
*
85 stream_windows_cast(struct stream
*stream
)
87 stream_assert_class(stream
, &windows_stream_class
);
88 return CONTAINER_OF(stream
, struct windows_stream
, stream
);
92 create_snpipe(char *path
)
94 return CreateFile(path
, GENERIC_READ
| GENERIC_WRITE
, 0, NULL
,
96 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_OVERLAPPED
|
97 FILE_FLAG_NO_BUFFERING
,
101 /* Active named pipe open. */
103 windows_open(const char *name
, char *suffix
, struct stream
**streamp
,
104 uint8_t dscp OVS_UNUSED
)
108 DWORD mode
= PIPE_READMODE_BYTE
;
112 /* If the path does not contain a ':', assume it is relative to
114 if (!strchr(suffix
, ':')) {
115 path
= xasprintf("%s/%s", ovs_rundir(), suffix
);
117 path
= xstrdup(suffix
);
120 /* In case of "unix:" argument, the assumption is that there is a file
121 * created in the path (name). */
122 file
= fopen(path
, "r");
125 VLOG_DBG_RL(&rl
, "%s: could not open %s (%s)", name
, suffix
,
126 ovs_strerror(errno
));
132 /* Valid pipe names do not have slashes. The assumption is that the named
133 * pipe was created with the name "path", with slashes removed and the
134 * default prefix \\.\pipe\ appended.
135 * Strip the slashes from the parameter name and append the default prefix.
137 connect_path
= xasprintf("%s%s", LOCAL_PREFIX
, remove_slashes(path
));
140 /* Try to connect to the named pipe. In case all pipe instances are
141 * busy we set the retry flag to true and retry again during the
142 * connect function. Use overlapped flag and file no buffering to ensure
143 * asynchronous operations. */
144 npipe
= create_snpipe(connect_path
);
146 if (npipe
== INVALID_HANDLE_VALUE
&& GetLastError() == ERROR_PIPE_BUSY
) {
150 if (!retry
&& npipe
== INVALID_HANDLE_VALUE
) {
151 VLOG_ERR_RL(&rl
, "Could not connect to named pipe: %s",
152 ovs_lasterror_to_string());
156 if (!retry
&& !SetNamedPipeHandleState(npipe
, &mode
, NULL
, NULL
)) {
157 VLOG_ERR_RL(&rl
, "Could not set named pipe options: %s",
158 ovs_lasterror_to_string());
163 struct windows_stream
*s
= xmalloc(sizeof *s
);
164 stream_init(&s
->stream
, &windows_stream_class
, 0, xstrdup(name
));
165 s
->pipe_path
= connect_path
;
167 /* This is an active stream. */
169 /* Create events for reading and writing to be signaled later. */
170 memset(&s
->read
, 0, sizeof(s
->read
));
171 memset(&s
->write
, 0, sizeof(s
->write
));
172 s
->read
.hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
173 s
->write
.hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
174 /* Initial read and write operations are not pending. */
175 s
->read_pending
= false;
176 s
->write_pending
= false;
177 s
->retry_connect
= retry
;
178 *streamp
= &s
->stream
;
182 /* Active named pipe close. */
184 windows_close(struct stream
*stream
)
186 struct windows_stream
*s
= stream_windows_cast(stream
);
187 /* Disconnect the named pipe in case it was created from a passive stream.
190 /* Flush the pipe to allow the client to read the pipe's contents
191 * before disconnecting. */
192 FlushFileBuffers(s
->fd
);
193 DisconnectNamedPipe(s
->fd
);
196 CloseHandle(s
->read
.hEvent
);
197 CloseHandle(s
->write
.hEvent
);
204 /* Active named pipe connect. */
206 windows_connect(struct stream
*stream
)
208 struct windows_stream
*s
= stream_windows_cast(stream
);
210 if (!s
->retry_connect
) {
214 npipe
= create_snpipe(s
->pipe_path
);
215 if (npipe
== INVALID_HANDLE_VALUE
) {
216 if (GetLastError() == ERROR_PIPE_BUSY
) {
219 s
->retry_connect
= false;
223 s
->retry_connect
= false;
229 /* Active named pipe receive. */
231 windows_recv(struct stream
*stream
, void *buffer
, size_t n
)
233 struct windows_stream
*s
= stream_windows_cast(stream
);
235 boolean result
= false;
236 DWORD last_error
= 0;
237 LPOVERLAPPED ov
= NULL
;
240 /* If the read operation was pending, we verify its result. */
241 if (s
->read_pending
) {
242 if (!GetOverlappedResult(s
->fd
, ov
, &(DWORD
)retval
, FALSE
)) {
243 last_error
= GetLastError();
244 if (last_error
== ERROR_IO_INCOMPLETE
) {
245 /* If the operation is still pending, retry again. */
246 s
->read_pending
= true;
248 } else if (last_error
== ERROR_PIPE_NOT_CONNECTED
249 || last_error
== ERROR_BAD_PIPE
250 || last_error
== ERROR_NO_DATA
251 || last_error
== ERROR_BROKEN_PIPE
) {
252 /* If the pipe was disconnected, return 0. */
255 VLOG_ERR_RL(&rl
, "Could not receive data on named pipe. Last "
256 "error: %s", ovs_lasterror_to_string());
260 s
->read_pending
= false;
264 result
= ReadFile(s
->fd
, buffer
, n
, &(DWORD
)retval
, ov
);
266 if (!result
&& GetLastError() == ERROR_IO_PENDING
) {
267 /* Mark the read operation as pending. */
268 s
->read_pending
= true;
270 } else if (!result
) {
271 last_error
= GetLastError();
272 if (last_error
== ERROR_PIPE_NOT_CONNECTED
273 || last_error
== ERROR_BAD_PIPE
274 || last_error
== ERROR_NO_DATA
275 || last_error
== ERROR_BROKEN_PIPE
) {
276 /* If the pipe was disconnected, return 0. */
279 VLOG_ERR_RL(&rl
, "Could not receive data synchronous on named pipe."
280 "Last error: %s", ovs_lasterror_to_string());
287 /* Active named pipe send. */
289 windows_send(struct stream
*stream
, const void *buffer
, size_t n
)
291 struct windows_stream
*s
= stream_windows_cast(stream
);
293 boolean result
= false;
294 DWORD last_error
= 0;
295 LPOVERLAPPED ov
= NULL
;
298 /* If the send operation was pending, we verify the result. */
299 if (s
->write_pending
) {
300 if (!GetOverlappedResult(s
->fd
, ov
, &(DWORD
)retval
, FALSE
)) {
301 last_error
= GetLastError();
302 if (last_error
== ERROR_IO_INCOMPLETE
) {
303 /* If the operation is still pending, retry again. */
304 s
->write_pending
= true;
306 } else if (last_error
== ERROR_PIPE_NOT_CONNECTED
307 || last_error
== ERROR_BAD_PIPE
308 || last_error
== ERROR_NO_DATA
309 || last_error
== ERROR_BROKEN_PIPE
) {
310 /* If the pipe was disconnected, return connection reset. */
313 VLOG_ERR_RL(&rl
, "Could not send data on named pipe. Last "
314 "error: %s", ovs_lasterror_to_string());
318 s
->write_pending
= false;
322 result
= WriteFile(s
->fd
, buffer
, n
, &(DWORD
)retval
, ov
);
323 last_error
= GetLastError();
324 if (!result
&& last_error
== ERROR_IO_PENDING
) {
325 /* Mark the send operation as pending. */
326 s
->write_pending
= true;
328 } else if (!result
&& (last_error
== ERROR_PIPE_NOT_CONNECTED
329 || last_error
== ERROR_BAD_PIPE
330 || last_error
== ERROR_NO_DATA
331 || last_error
== ERROR_BROKEN_PIPE
)) {
332 /* If the pipe was disconnected, return connection reset. */
334 } else if (!result
) {
335 VLOG_ERR_RL(&rl
, "Could not send data on synchronous named pipe. Last "
336 "error: %s", ovs_lasterror_to_string());
339 return (retval
> 0 ? retval
: -EAGAIN
);
342 /* Active named pipe wait. */
344 windows_wait(struct stream
*stream
, enum stream_wait_type wait
)
346 struct windows_stream
*s
= stream_windows_cast(stream
);
349 poll_wevent_wait(s
->write
.hEvent
);
353 poll_immediate_wake();
357 poll_wevent_wait(s
->read
.hEvent
);
365 /* Passive named pipe. */
366 const struct stream_class windows_stream_class
= {
368 false, /* needs_probes */
369 windows_open
, /* open */
370 windows_close
, /* close */
371 windows_connect
, /* connect */
372 windows_recv
, /* recv */
373 windows_send
, /* send */
376 windows_wait
, /* wait */
379 struct pwindows_pstream
381 struct pstream pstream
;
383 /* Unlink path to be deleted during close. */
385 /* Overlapped operation used for connect. */
387 /* Flag to check if an operation is pending. */
389 /* String used to create the named pipe. */
393 const struct pstream_class pwindows_pstream_class
;
395 static struct pwindows_pstream
*
396 pwindows_pstream_cast(struct pstream
*pstream
)
398 pstream_assert_class(pstream
, &pwindows_pstream_class
);
399 return CONTAINER_OF(pstream
, struct pwindows_pstream
, pstream
);
402 /* Create a named pipe with read/write access, overlapped, message mode for
403 * writing, byte mode for reading and with a maximum of 64 active instances. */
405 create_pnpipe(char *name
)
407 SECURITY_ATTRIBUTES sa
;
408 SID_IDENTIFIER_AUTHORITY sia
= SECURITY_NT_AUTHORITY
;
410 PSID allowedPsid
[ALLOWED_PSIDS_SIZE
];
411 PSID remoteAccessSid
;
413 PSECURITY_DESCRIPTOR psd
= NULL
;
416 /* Disable access over network. */
417 if (!AllocateAndInitializeSid(&sia
, 1, SECURITY_NETWORK_RID
,
418 0, 0, 0, 0, 0, 0, 0, &remoteAccessSid
)) {
419 VLOG_ERR_RL(&rl
, "Error creating Remote Access SID.");
423 aclSize
= sizeof(ACL
) + sizeof(ACCESS_DENIED_ACE
) +
424 GetLengthSid(remoteAccessSid
) - sizeof(DWORD
);
426 /* Allow Windows Services to access the Named Pipe. */
427 if (!AllocateAndInitializeSid(&sia
, 1, SECURITY_LOCAL_SYSTEM_RID
,
428 0, 0, 0, 0, 0, 0, 0, &allowedPsid
[0])) {
429 VLOG_ERR_RL(&rl
, "Error creating Services SID.");
433 /* Allow Administrators to access the Named Pipe. */
434 if (!AllocateAndInitializeSid(&sia
, 2, SECURITY_BUILTIN_DOMAIN_RID
,
435 DOMAIN_ALIAS_RID_ADMINS
, 0, 0, 0, 0, 0, 0,
437 VLOG_ERR_RL(&rl
, "Error creating Administrator SID.");
441 for (int i
= 0; i
< ALLOWED_PSIDS_SIZE
; i
++) {
442 aclSize
+= sizeof(ACCESS_ALLOWED_ACE
) +
443 GetLengthSid(allowedPsid
[i
]) -
447 acl
= xmalloc(aclSize
);
448 if (!InitializeAcl(acl
, aclSize
, ACL_REVISION
)) {
449 VLOG_ERR_RL(&rl
, "Error initializing ACL.");
453 /* Add denied ACL. */
454 if (!AddAccessDeniedAce(acl
, ACL_REVISION
,
455 GENERIC_ALL
, remoteAccessSid
)) {
456 VLOG_ERR_RL(&rl
, "Error adding remote access ACE.");
460 /* Add allowed ACLs. */
461 for (int i
= 0; i
< ALLOWED_PSIDS_SIZE
; i
++) {
462 if (!AddAccessAllowedAce(acl
, ACL_REVISION
,
463 GENERIC_ALL
, allowedPsid
[i
])) {
464 VLOG_ERR_RL(&rl
, "Error adding ACE.");
469 psd
= xmalloc(SECURITY_DESCRIPTOR_MIN_LENGTH
);
470 if (!InitializeSecurityDescriptor(psd
, SECURITY_DESCRIPTOR_REVISION
)) {
471 VLOG_ERR_RL(&rl
, "Error initializing Security Descriptor.");
476 if (!SetSecurityDescriptorDacl(psd
, TRUE
, acl
, FALSE
)) {
477 VLOG_ERR_RL(&rl
, "Error while setting DACL.");
481 sa
.nLength
= sizeof sa
;
482 sa
.bInheritHandle
= TRUE
;
483 sa
.lpSecurityDescriptor
= psd
;
485 if (strlen(name
) > 256) {
486 VLOG_ERR_RL(&rl
, "Named pipe name too long.");
490 npipe
= CreateNamedPipe(name
, PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
,
491 PIPE_TYPE_MESSAGE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
492 64, BUFSIZE
, BUFSIZE
, 0, &sa
);
500 return INVALID_HANDLE_VALUE
;
503 /* Passive named pipe connect. This function creates a new named pipe and
504 * passes the old handle to the active stream. */
506 pwindows_accept(struct pstream
*pstream
, struct stream
**new_streamp
)
508 struct pwindows_pstream
*p
= pwindows_pstream_cast(pstream
);
509 DWORD last_error
= 0;
513 /* If the connect operation was pending, verify the result. */
515 if (!GetOverlappedResult(p
->fd
, &p
->connect
, &cbRet
, FALSE
)) {
516 last_error
= GetLastError();
517 if (last_error
== ERROR_IO_INCOMPLETE
) {
518 /* If the operation is still pending, retry again. */
522 VLOG_ERR_RL(&rl
, "Could not connect named pipe. Last "
523 "error: %s", ovs_lasterror_to_string());
524 DisconnectNamedPipe(p
->fd
);
531 if (!p
->pending
&& !ConnectNamedPipe(p
->fd
, &p
->connect
)) {
532 last_error
= GetLastError();
533 if (last_error
== ERROR_IO_PENDING
) {
534 /* Mark the accept operation as pending. */
537 } else if (last_error
!= ERROR_PIPE_CONNECTED
) {
538 VLOG_ERR_RL(&rl
, "Could not connect synchronous named pipe. Last "
539 "error: %s", ovs_lasterror_to_string());
540 DisconnectNamedPipe(p
->fd
);
543 /* If the pipe is connected, signal an event. */
544 SetEvent(&p
->connect
.hEvent
);
548 npipe
= create_pnpipe(p
->pipe_path
);
549 if (npipe
== INVALID_HANDLE_VALUE
) {
550 VLOG_ERR_RL(&rl
, "Could not create a new named pipe after connect. ",
551 ovs_lasterror_to_string());
555 /* Give the handle p->fd to the new created active stream and specify it
556 * was created by an active stream. */
557 struct windows_stream
*p_temp
= xmalloc(sizeof *p_temp
);
558 stream_init(&p_temp
->stream
, &windows_stream_class
, 0, xstrdup("unix"));
560 /* Specify it was created by a passive stream. */
561 p_temp
->server
= true;
562 /* Create events for read/write operations. */
563 memset(&p_temp
->read
, 0, sizeof(p_temp
->read
));
564 memset(&p_temp
->write
, 0, sizeof(p_temp
->write
));
565 p_temp
->read
.hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
566 p_temp
->write
.hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
567 p_temp
->read_pending
= false;
568 p_temp
->write_pending
= false;
569 p_temp
->retry_connect
= false;
570 p_temp
->pipe_path
= NULL
;
571 *new_streamp
= &p_temp
->stream
;
573 /* The passive handle p->fd will be the new created handle. */
575 memset(&p
->connect
, 0, sizeof(p
->connect
));
576 p
->connect
.hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
581 /* Passive named pipe close. */
583 pwindows_close(struct pstream
*pstream
)
585 struct pwindows_pstream
*p
= pwindows_pstream_cast(pstream
);
586 DisconnectNamedPipe(p
->fd
);
588 CloseHandle(p
->connect
.hEvent
);
589 maybe_unlink_and_free(p
->unlink_path
);
594 /* Passive named pipe wait. */
596 pwindows_wait(struct pstream
*pstream
)
598 struct pwindows_pstream
*p
= pwindows_pstream_cast(pstream
);
599 poll_wevent_wait(p
->connect
.hEvent
);
602 /* Passive named pipe. */
604 pwindows_open(const char *name OVS_UNUSED
, char *suffix
,
605 struct pstream
**pstreamp
, uint8_t dscp OVS_UNUSED
)
613 if (!strchr(suffix
, ':')) {
614 path
= xasprintf("%s/%s", ovs_rundir(), suffix
);
616 path
= xstrdup(suffix
);
619 /* Try to create a file under the path location. */
620 FILE *file
= fopen(path
, "w");
624 VLOG_DBG_RL(&rl
, "could not open %s (%s)", path
, ovs_strerror(error
));
630 orig_path
= xstrdup(path
);
631 /* Strip slashes from path and create a named pipe using that newly created
633 bind_path
= xasprintf("%s%s", LOCAL_PREFIX
, remove_slashes(path
));
636 npipe
= create_pnpipe(bind_path
);
638 if (npipe
== INVALID_HANDLE_VALUE
) {
639 VLOG_ERR_RL(&rl
, "Could not create named pipe. Last error: %s",
640 ovs_lasterror_to_string());
644 struct pwindows_pstream
*p
= xmalloc(sizeof *p
);
645 pstream_init(&p
->pstream
, &pwindows_pstream_class
, xstrdup(name
));
647 p
->unlink_path
= orig_path
;
648 memset(&p
->connect
, 0, sizeof(p
->connect
));
649 p
->connect
.hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
651 p
->pipe_path
= bind_path
;
652 *pstreamp
= &p
->pstream
;
656 const struct pstream_class pwindows_pstream_class
= {
659 pwindows_open
, /* open */
660 pwindows_close
, /* close */
661 pwindows_accept
, /* accept */
662 pwindows_wait
, /* wait */
665 /* Helper functions. */
667 maybe_unlink_and_free(char *path
)
670 fatal_signal_unlink_file_now(path
);