Looks good to me. Acked-by: Paul Boca <[email protected]>
> -----Original Message----- > From: dev [mailto:[email protected]] On Behalf Of Alin Serdean > Sent: Wednesday, July 13, 2016 6:33 AM > To: [email protected] > Subject: [ovs-dev] [PATCH v2 1/3] Windows: Local named pipe > implementation > > Currently in the case of command line arguments punix/unix, on Windows > we create a file, write a TCP port number to connect. This is a security > concern. > > This patch adds support for the command line arguments punix/unix trying > to mimic AF_UNIX behind a local named pipe. > > Signed-off-by: Alin Gabriel Serdean <[email protected]> > --- > v2: Address comments, fix leaks. > --- > lib/stream-windows.c | 587 > +++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 587 insertions(+) > create mode 100644 lib/stream-windows.c > > diff --git a/lib/stream-windows.c b/lib/stream-windows.c > new file mode 100644 > index 0000000..97e6f02 > --- /dev/null > +++ b/lib/stream-windows.c > @@ -0,0 +1,587 @@ > +/* > + * Copyright (c) 2016 Cloudbase Solutions Srl > + * > + * Licensed under the Apache License, Version 2.0 (the "License"); > + * you may not use this file except in compliance with the License. > + * You may obtain a copy of the License at > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or > implied. > + * See the License for the specific language governing permissions and > + * limitations under the License. > + */ > + > +#include <config.h> > +#include <errno.h> > +#include <inttypes.h> > +#include <netdb.h> > +#include <poll.h> > +#include <sys/socket.h> > +#include <sys/types.h> > +#include <sys/un.h> > +#include <stdlib.h> > +#include <string.h> > +#include <strsafe.h> > +#include <unistd.h> > +#include "packets.h" > +#include "poll-loop.h" > +#include "socket-util.h" > +#include "dirs.h" > +#include "util.h" > +#include "stream-provider.h" > +#include "openvswitch/vlog.h" > + > +VLOG_DEFINE_THIS_MODULE(stream_windows); > + > +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25); > + > +static void maybe_unlink_and_free(char *path); > + > +/* Buffer size suggested at the creation of the named pipe for reading and > + * and writing operations */ > +#define BUFSIZE 65000 > + > +/* Default local prefix of a named pipe */ > +#define LOCAL_PREFIX "\\\\.\\pipe\\" > + > +/* This function has the purpose to remove all the slashes received in s */ > +char* > +remove_slashes(char *s) { > + char *p1, *p2; > + p1 = p2 = s; > + > + while (*p1) { > + if ((*p1) == '\\' || (*p1) == '/') { > + p1++; > + } else { > + *p2 = *p1; > + p2++; > + p1++; > + } > + } > + *p2 = '\0'; > + return s; > +} > + > +/* Active named pipe descriptor stream. */ > +struct windows_stream > +{ > + struct stream stream; > + HANDLE fd; > + /* Overlapped operations used for reading/writing */ > + OVERLAPPED read; > + OVERLAPPED write; > + /* Flag to check if a reading/writing is pending*/ > + bool read_pending; > + bool write_pending; > + /* Flag to check if fd is a server HANDLE. In the case of a server handle > + * we have to issue a disconnect before closing the actual handle */ > + bool server; > + bool retry_connect; > + char *pipe_path; > +}; > + > +static struct windows_stream * > +stream_windows_cast(struct stream *stream) > +{ > + stream_assert_class(stream, &windows_stream_class); > + return CONTAINER_OF(stream, struct windows_stream, stream); > +} > + > +HANDLE > +create_snpipe(char *path) { > + return CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, > + OPEN_EXISTING, > + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | > + FILE_FLAG_NO_BUFFERING, > + NULL); > +} > + > +/* Active named pipe open */ > +static int > +windows_open(const char *name, char *suffix, struct stream **streamp, > + uint8_t dscp OVS_UNUSED) > +{ > + char *connect_path; > + HANDLE npipe; > + DWORD mode = PIPE_READMODE_BYTE; > + char* path; > + FILE* file; > + bool retry = false; > + /* If the path does not contain a ':', assume it is relative to > + * OVS_RUNDIR. */ > + if (!strchr(suffix, ':')) { > + path = xasprintf("%s/%s", ovs_rundir(), suffix); > + } else { > + path = xstrdup(suffix); > + } > + > + /* In the punix/unix argument the assumption is that there is a file > + * created in the path (name) */ > + file = fopen(path, "r"); > + if (!file) { > + free(path); > + VLOG_DBG_RL(&rl, "%s: could not open %s (%s)", name, suffix, > + ovs_strerror(errno)); > + return ENOENT; > + } else { > + fclose(file); > + } > + > + /* Valid pipe names do not have slashes. The assumption is the named > pipe > + * was created on the same path with the slash stripped path and the > + * default prefix \\.\pipe\ appended. > + * Strip the slashed from the parameter name and append the default > prefix > + */ > + connect_path = xasprintf("%s%s", LOCAL_PREFIX, remove_slashes(path)); > + free(path); > + > + /* Try to connect to the named pipe. In the case all pipe instances are > + * busy we set the retry flag to true and we retry again during the > + * connect function. Use overlapped flag and no buffering to ensure > + * an asynchronous operations */ > + npipe = create_snpipe(connect_path); > + > + if (npipe == INVALID_HANDLE_VALUE && GetLastError() == > ERROR_PIPE_BUSY) { > + retry = true; > + } > + > + if (!retry && npipe == INVALID_HANDLE_VALUE) { > + VLOG_ERR_RL(&rl, "Could not connect to named pipe: %s", > + ovs_lasterror_to_string()); > + free(connect_path); > + return ENOENT; > + } > + if (!retry && !SetNamedPipeHandleState(npipe, &mode, NULL, NULL)) { > + VLOG_ERR_RL(&rl, "Could not set named pipe options: %s", > + ovs_lasterror_to_string()); > + free(connect_path); > + CloseHandle(npipe); > + return ENOENT; > + } > + struct windows_stream *s = xmalloc(sizeof *s); > + stream_init(&s->stream, &windows_stream_class, 0, name); > + s->pipe_path = connect_path; > + s->fd = npipe; > + /* This is a active stream */ > + s->server = false; > + /* Create events for reading and writing to be signaled later */ > + memset(&s->read, 0, sizeof(s->read)); > + memset(&s->write, 0, sizeof(s->write)); > + s->read.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); > + s->write.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); > + /* Initial read and write operations are not pending */ > + s->read_pending = false; > + s->write_pending = false; > + s->retry_connect = retry; > + *streamp = &s->stream; > + return 0; > +} > + > +/* Active named pipe close */ > +static void > +windows_close(struct stream *stream) > +{ > + struct windows_stream *s = stream_windows_cast(stream); > + /* Disconnect the named pipe in case it was a passive stream at first */ > + if (s->server) { > + DisconnectNamedPipe(s->fd); > + } > + CloseHandle(s->fd); > + CloseHandle(s->read.hEvent); > + CloseHandle(s->write.hEvent); > + if (s->pipe_path) { > + free(s->pipe_path); > + } > + free(s); > +} > + > +/* Active named pipe connect */ > +static int > +windows_connect(struct stream *stream) > +{ > + /* Stub function for connect. We already to the connect in the open > + * function */ > + struct windows_stream *s = stream_windows_cast(stream); > + > + if (!s->retry_connect) { > + return 0; > + } else { > + HANDLE npipe; > + npipe = create_snpipe(s->pipe_path); > + if (npipe == INVALID_HANDLE_VALUE) { > + if (GetLastError() == ERROR_PIPE_BUSY) { > + return EAGAIN; > + } else { > + s->retry_connect = false; > + return ENOENT; > + } > + } > + s->retry_connect = false; > + s->fd = npipe; > + return 0; > + } > +} > + > +/* Active named pipe receive */ > +static ssize_t > +windows_recv(struct stream *stream, void *buffer, size_t n) > +{ > + struct windows_stream *s = stream_windows_cast(stream); > + ssize_t retval = 0; > + boolean result = false; > + DWORD last_error = 0; > + LPOVERLAPPED ov = NULL; > + ov = &s->read; > + > + /* If the read operation was pending we verify its result */ > + if (s->read_pending) { > + if (!GetOverlappedResult(s->fd, ov, &(DWORD)retval, FALSE)) { > + last_error = GetLastError(); > + if (last_error == ERROR_IO_INCOMPLETE) { > + /* If the operation is still pending retry again */ > + s->read_pending = true; > + return -EAGAIN; > + } else if (last_error == ERROR_PIPE_NOT_CONNECTED || > + last_error == ERROR_BAD_PIPE || > + last_error == ERROR_NO_DATA || > + last_error == ERROR_BROKEN_PIPE) { > + /* If the pipe was disconnected return 0 */ > + return 0; > + } else { > + VLOG_ERR_RL(&rl, "Could not receive data on named pipe. Last > " > + "error: %s", ovs_lasterror_to_string()); > + return -EINVAL; > + } > + } > + s->read_pending = false; > + return retval; > + } > + > + result = ReadFile(s->fd, buffer, n, &(DWORD)retval, ov); > + > + if (!result && GetLastError() == ERROR_IO_PENDING) { > + /* Pend the read operation */ > + s->read_pending = true; > + return -EAGAIN; > + } else if (!result) { > + last_error = GetLastError(); > + if (last_error == ERROR_PIPE_NOT_CONNECTED || > + last_error == ERROR_BAD_PIPE || > + last_error == ERROR_NO_DATA || > + last_error == ERROR_BROKEN_PIPE) { > + /* If the pipe was disconnected return 0 */ > + return 0; > + } > + VLOG_ERR_RL(&rl, "Could not receive data synchronous on named > pipe." > + "Last error: %s", ovs_lasterror_to_string()); > + return -EINVAL; > + } > + > + return retval; > +} > + > +/* Active named pipe send */ > +static ssize_t > +windows_send(struct stream *stream, const void *buffer, size_t n) > +{ > + struct windows_stream *s = stream_windows_cast(stream); > + ssize_t retval = 0; > + boolean result = false; > + DWORD last_error = 0; > + LPOVERLAPPED ov = NULL; > + ov = &s->write; > + > + /* If the send operation was pending we verify the result */ > + if (s->write_pending) { > + if (!GetOverlappedResult(s->fd, ov, &(DWORD)retval, FALSE)) { > + last_error = GetLastError(); > + if (last_error == ERROR_IO_INCOMPLETE) { > + /* If the operation is still pending retry again */ > + s->write_pending = true; > + return -EAGAIN; > + } else if (last_error == ERROR_PIPE_NOT_CONNECTED || > + last_error == ERROR_BAD_PIPE || > + last_error == ERROR_NO_DATA || > + last_error == ERROR_BROKEN_PIPE) { > + /* If the pipe was disconnected return connection reset */ > + return -WSAECONNRESET; > + } else { > + VLOG_ERR_RL(&rl, "Could not send data on named pipe. Last " > + "error: %s", ovs_lasterror_to_string()); > + return -EINVAL; > + } > + } > + s->write_pending = false; > + return retval; > + } > + > + result = WriteFile(s->fd, buffer, n, &(DWORD)retval, ov); > + last_error = GetLastError(); > + if (!result && GetLastError() == ERROR_IO_PENDING) { > + /* Pend the send operation */ > + s->write_pending = true; > + return -EAGAIN; > + } else if (!result && (last_error == ERROR_PIPE_NOT_CONNECTED || > + last_error == ERROR_BAD_PIPE || > + last_error == ERROR_NO_DATA || > + last_error == ERROR_BROKEN_PIPE)) { > + /* If the pipe was disconnected return connection reset */ > + return -WSAECONNRESET; > + } else if (!result) { > + VLOG_ERR_RL(&rl, "Could not send data on synchronous named pipe. > Last " > + "error: %s", ovs_lasterror_to_string()); > + return -EINVAL; > + } > + return (retval > 0 ? retval : -EAGAIN); > +} > + > +/* Active named pipe wait */ > +static void > +windows_wait(struct stream *stream, enum stream_wait_type wait) > +{ > + struct windows_stream *s = stream_windows_cast(stream); > + switch (wait) { > + case STREAM_SEND: > + poll_wevent_wait(s->write.hEvent); > + break; > + > + case STREAM_CONNECT: > + poll_immediate_wake(); > + break; > + > + case STREAM_RECV: > + poll_wevent_wait(s->read.hEvent); > + break; > + > + default: > + OVS_NOT_REACHED(); > + } > +} > + > +/* Passive named pipe descriptor stream. */ > +const struct stream_class windows_stream_class = { > + "unix", /* name */ > + false, /* needs_probes */ > + windows_open, /* open */ > + windows_close, /* close */ > + windows_connect, /* connect */ > + windows_recv, /* recv */ > + windows_send, /* send */ > + NULL, /* run */ > + NULL, /* run_wait */ > + windows_wait, /* wait */ > +}; > + > +struct pwindows_pstream > +{ > + struct pstream pstream; > + HANDLE fd; > + /* Unlink path to be deleted during close */ > + char* unlink_path; > + /* Overlapped operation used for connect */ > + OVERLAPPED connect; > + /* Flag to check if an operation is pending */ > + bool pending; > + /* Named pipe path used for creation */ > + char* pipe_path; > +}; > + > +const struct pstream_class pwindows_pstream_class; > + > +static struct pwindows_pstream * > +pwindows_pstream_cast(struct pstream *pstream) > +{ > + pstream_assert_class(pstream, &pwindows_pstream_class); > + return CONTAINER_OF(pstream, struct pwindows_pstream, pstream); > +} > + > +/* Create a named pipe with read/write access, overlapped, message mode > for > + * writing, byte mode for reading with a maximum of 64 active instances */ > +HANDLE > +create_pnpipe(char *name) > +{ > + SECURITY_ATTRIBUTES sa; > + sa.nLength = sizeof(sa); > + sa.lpSecurityDescriptor = NULL; > + sa.bInheritHandle = TRUE; > + if (strlen(name) > 256) { > + VLOG_ERR_RL(&rl, "Named pipe name too long. Creation terminated"); > + return INVALID_HANDLE_VALUE; > + } > + return CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | > FILE_FLAG_OVERLAPPED, > + PIPE_TYPE_MESSAGE | PIPE_READMODE_BYTE | > PIPE_WAIT, > + 64, BUFSIZE, BUFSIZE, 0, &sa); > +} > + > +/* Passive named pipe connect. This function creates a new named pipe > and > + * passes the old handle to the active stream */ > +static int > +pwindows_accept(struct pstream *pstream, struct stream **new_streamp) > +{ > + struct pwindows_pstream *p = pwindows_pstream_cast(pstream); > + DWORD last_error = 0; > + DWORD cbRet; > + HANDLE npipe; > + > + /* If the connect operation was pending verify the result */ > + if (p->pending) { > + if (!GetOverlappedResult(p->fd, &p->connect, &cbRet, FALSE)) { > + last_error = GetLastError(); > + if (last_error == ERROR_IO_INCOMPLETE) { > + /* If the operation is still pending retry again */ > + p->pending = true; > + return EAGAIN; > + } else { > + VLOG_ERR_RL(&rl, "Could not connect named pipe. Last " > + "error: %s", ovs_lasterror_to_string()); > + return EINVAL; > + } > + } > + p->pending = false; > + } > + > + if (!p->pending && !ConnectNamedPipe(p->fd, &p->connect)) { > + last_error = GetLastError(); > + if (last_error == ERROR_IO_PENDING) { > + /* Pend the connect operation */ > + p->pending = true; > + return EAGAIN; > + } else if (last_error != ERROR_PIPE_CONNECTED) { > + VLOG_ERR_RL(&rl, "Could not connect synchronous named pipe. Last > " > + "error: %s", ovs_lasterror_to_string()); > + return EINVAL; > + } else { > + /* If the pipe is connected signal an event */ > + SetEvent(&p->connect.hEvent); > + } > + } > + > + npipe = create_pnpipe(p->pipe_path); > + if (npipe == INVALID_HANDLE_VALUE) { > + VLOG_ERR_RL(&rl, "Could not create a new named pipe after connect. > ", > + ovs_lasterror_to_string()); > + return ENOENT; > + } > + > + /* Give the handle p->fd to the new created active stream and specify it > + * was created by an active stream */ > + struct windows_stream *p_temp = xmalloc(sizeof *p_temp); > + stream_init(&p_temp->stream, &windows_stream_class, 0, "unix"); > + p_temp->fd = p->fd; > + /* Specify it was created by a passive stream */ > + p_temp->server = true; > + /* Create events for read/write operations */ > + memset(&p_temp->read, 0, sizeof(p_temp->read)); > + memset(&p_temp->write, 0, sizeof(p_temp->write)); > + p_temp->read.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); > + p_temp->write.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); > + p_temp->read_pending = false; > + p_temp->write_pending = false; > + p_temp->retry_connect = false; > + p_temp->pipe_path = NULL; > + *new_streamp = &p_temp->stream; > + > + /* The passive handle p->fd will be the new created handle */ > + p->fd = npipe; > + memset(&p->connect, 0, sizeof(p->connect)); > + p->connect.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); > + p->pending = false; > + return 0; > +} > + > +/* Passive named pipe close */ > +static void > +pwindows_close(struct pstream *pstream) > +{ > + struct pwindows_pstream *p = pwindows_pstream_cast(pstream); > + DisconnectNamedPipe(p->fd); > + CloseHandle(p->fd); > + CloseHandle(p->connect.hEvent); > + maybe_unlink_and_free(p->unlink_path); > + free(p->pipe_path); > + free(p); > +} > + > +/* Passive named pipe wait */ > +void > +pwindows_wait(struct pstream *pstream) > +{ > + struct pwindows_pstream *p = pwindows_pstream_cast(pstream); > + poll_wevent_wait(p->connect.hEvent); > +} > + > +/* Passive named pipe */ > +static int > +pwindows_open(const char *name OVS_UNUSED, char *suffix, > + struct pstream **pstreamp, uint8_t dscp OVS_UNUSED) > +{ > + char *bind_path; > + int error; > + HANDLE npipe; > + char* orig_path; > + > + char* path; > + if (!strchr(suffix, ':')) { > + path = xasprintf("%s/%s", ovs_rundir(), suffix); > + } else { > + path = xstrdup(suffix); > + } > + > + /* Try to create a file under the path location */ > + FILE *file = fopen(path, "w"); > + if (!file) { > + free(path); > + error = errno; > + VLOG_DBG_RL(&rl, "could not open %s (%s)", path, > ovs_strerror(error)); > + return error; > + } else { > + fclose(file); > + } > + > + orig_path = xstrdup(path); > + /* Strip slashes from path and create a named pipe using that name */ > + bind_path = xasprintf("%s%s", LOCAL_PREFIX, remove_slashes(path)); > + free(path); > + > + npipe = create_pnpipe(bind_path); > + > + if (npipe == INVALID_HANDLE_VALUE) { > + VLOG_ERR_RL(&rl, "Could not create named pipe. Last error: %s", > + ovs_lasterror_to_string()); > + return ENOENT; > + } > + > + struct pwindows_pstream *p = xmalloc(sizeof *p); > + pstream_init(&p->pstream, &pwindows_pstream_class, name); > + p->fd = npipe; > + p->unlink_path = orig_path; > + memset(&p->connect, 0, sizeof(p->connect)); > + p->connect.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); > + p->pending = false; > + p->pipe_path = bind_path; > + *pstreamp = &p->pstream; > + return 0; > +} > + > +const struct pstream_class pwindows_pstream_class = { > + "punix", > + false, /* probes */ > + pwindows_open, /* open */ > + pwindows_close, /* close */ > + pwindows_accept, /* accept */ > + pwindows_wait, /* wait */ > +}; > + > +/* Helper functions. */ > +static void > +maybe_unlink_and_free(char *path) > +{ > + if (path) { > + fatal_signal_unlink_file_now(path); > + free(path); > + } > +} > -- > 1.9.5.msysgit.0 > _______________________________________________ > dev mailing list > [email protected] > http://openvswitch.org/mailman/listinfo/dev _______________________________________________ dev mailing list [email protected] http://openvswitch.org/mailman/listinfo/dev
