From: VMware, Inc <> Date: Wed, 18 Sep 2013 03:13:02 +0000 (-0700) Subject: Secure GuestRPC channels X-Git-Tag: 2013.09.16-1328054~120 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=35453dc666791c5fa87dbcc442839f0491e31d17;p=thirdparty%2Fopen-vm-tools.git Secure GuestRPC channels Use VMCI/Vsocket instead of backdoor for GuestRPC RPCI channels. For privileged channels, the guest side can bind port to less than 1024, VMX can then verify the binding and enforce privileged commands can only be ran by privileged users. Signed-off-by: Dmitry Torokhov --- diff --git a/open-vm-tools/lib/include/vmware/guestrpc/tclodefs.h b/open-vm-tools/lib/include/vmware/guestrpc/tclodefs.h index e53f16a26..e88d169d9 100644 --- a/open-vm-tools/lib/include/vmware/guestrpc/tclodefs.h +++ b/open-vm-tools/lib/include/vmware/guestrpc/tclodefs.h @@ -50,7 +50,8 @@ /** Reply from host when the command is not recognized. */ #define RPCI_UNKNOWN_COMMAND "Unknown command" -#define GUESTRPC_VSOCK_LISTEN_PORT 975 +#define GUESTRPC_TCLO_VSOCK_LISTEN_PORT 975 +#define GUESTRPC_RPCI_VSOCK_LISTEN_PORT 976 /* * Tools options. @@ -119,13 +120,13 @@ #define RPCIN_TCLO_PING 0x1 enum { - RPCINPKT_TYPE_DATA = 1, - RPCINPKT_TYPE_PING + GUESTRPCPKT_TYPE_DATA = 1, + GUESTRPCPKT_TYPE_PING }; enum { - RPCINPKT_FIELD_TYPE = 1, - RPCINPKT_FIELD_PAYLOAD + GUESTRPCPKT_FIELD_TYPE = 1, + GUESTRPCPKT_FIELD_PAYLOAD }; #endif /* _TCLODEFS_H_ */ diff --git a/open-vm-tools/lib/include/vmware/tools/guestrpc.h b/open-vm-tools/lib/include/vmware/tools/guestrpc.h index a12767b5f..4cb6907d0 100644 --- a/open-vm-tools/lib/include/vmware/tools/guestrpc.h +++ b/open-vm-tools/lib/include/vmware/tools/guestrpc.h @@ -45,7 +45,7 @@ G_BEGIN_DECLS #define RPCIN_SETRETVALS RpcChannel_SetRetVals #define RPCIN_SETRETVALSF RpcChannel_SetRetValsF -struct RpcChannel; +typedef struct _RpcChannel RpcChannel; /** Data structure passed to RPC callbacks. */ typedef struct RpcInData { @@ -76,6 +76,12 @@ typedef struct RpcInData { void *clientData; } RpcInData; +typedef enum RpcChannelType { + RPCCHANNEL_TYPE_INACTIVE, + RPCCHANNEL_TYPE_BKDOOR, + RPCCHANNEL_TYPE_PRIV_VSOCK, + RPCCHANNEL_TYPE_UNPRIV_VSOCK +} RpcChannelType; /** * Type for RpcIn callbacks. The callback function is responsible for @@ -108,21 +114,6 @@ typedef struct RpcChannelCallback { size_t xdrInSize; } RpcChannelCallback; - -typedef gboolean (*RpcChannelStartFn)(struct RpcChannel *); -typedef void (*RpcChannelStopFn)(struct RpcChannel *); -typedef void (*RpcChannelShutdownFn)(struct RpcChannel *); -typedef gboolean (*RpcChannelSendFn)(struct RpcChannel *, - char const *data, - size_t dataLen, - char **result, - size_t *resultLen); -typedef void (*RpcChannelSetupFn)(struct RpcChannel *chan, - GMainContext *mainCtx, - const char *appName, - gpointer appCtx); - - /** * Signature for the callback function called after a channel reset. * @@ -130,80 +121,25 @@ typedef void (*RpcChannelSetupFn)(struct RpcChannel *chan, * @param[in] success Whether reset was successful. * @param[in] data Client data. */ -typedef void (*RpcChannelResetCb)(struct RpcChannel *chan, +typedef void (*RpcChannelResetCb)(RpcChannel *chan, gboolean success, gpointer data); +gboolean +RpcChannel_Start(RpcChannel *chan); -/** Defines the interface between the application and the RPC channel. */ -typedef struct RpcChannel { - RpcChannelStartFn start; - RpcChannelStopFn stop; - RpcChannelSendFn send; - RpcChannelSetupFn setup; - RpcChannelShutdownFn shutdown; - gpointer _private; -} RpcChannel; - - -/** - * Wrapper for the start function of an RPC channel struct. - * - * @param[in] chan The RPC channel instance. - * - * @return TRUE on success. - */ - -G_INLINE_FUNC gboolean -RpcChannel_Start(RpcChannel *chan) -{ - g_return_val_if_fail(chan != NULL, FALSE); - g_return_val_if_fail(chan->start != NULL, FALSE); - - return chan->start(chan); -} - - -/** - * Wrapper for the stop function of an RPC channel struct. - * - * @param[in] chan The RPC channel instance. - */ - -G_INLINE_FUNC void -RpcChannel_Stop(RpcChannel *chan) -{ - g_return_if_fail(chan != NULL); - g_return_if_fail(chan->stop != NULL); - - chan->stop(chan); -} - +void +RpcChannel_Stop(RpcChannel *chan); -/** - * Wrapper for the send function of an RPC channel struct. - * - * @param[in] chan The RPC channel instance. - * @param[in] data Data to send. - * @param[in] dataLen Number of bytes to send. - * @param[out] result Response from other side (should be freed by caller). - * @param[out] resultLen Number of bytes in response. - * - * @return The status from the remote end (TRUE if call was successful). - */ +RpcChannelType +RpcChannel_GetType(RpcChannel *chan); -G_INLINE_FUNC gboolean +gboolean RpcChannel_Send(RpcChannel *chan, char const *data, size_t dataLen, char **result, - size_t *resultLen) -{ - g_return_val_if_fail(chan != NULL, FALSE); - g_return_val_if_fail(chan->send != NULL, FALSE); - - return chan->send(chan, data, dataLen, result, resultLen); -} + size_t *resultLen); gboolean RpcChannel_BuildXdrCommand(const char *cmd, @@ -215,6 +151,9 @@ RpcChannel_BuildXdrCommand(const char *cmd, RpcChannel * RpcChannel_Create(void); +void +RpcChannel_Shutdown(RpcChannel *chan); + gboolean RpcChannel_Destroy(RpcChannel *chan); @@ -247,7 +186,16 @@ void RpcChannel_UnregisterCallback(RpcChannel *chan, RpcChannelCallback *rpc); +gboolean +RpcChannel_SendOneRaw(const char *data, + size_t dataLen, + char **result, + size_t *resultLen); + +RpcChannel * +RpcChannel_New(void); +/* This is deprecated, call RpcChannel_New instead */ RpcChannel * BackdoorChannel_New(void); diff --git a/open-vm-tools/lib/rpcChannel/Makefile.am b/open-vm-tools/lib/rpcChannel/Makefile.am index 96e956a9a..f3d54873a 100644 --- a/open-vm-tools/lib/rpcChannel/Makefile.am +++ b/open-vm-tools/lib/rpcChannel/Makefile.am @@ -20,6 +20,8 @@ noinst_LTLIBRARIES = libRpcChannel.la libRpcChannel_la_SOURCES = libRpcChannel_la_SOURCES += bdoorChannel.c libRpcChannel_la_SOURCES += rpcChannel.c +libRpcChannel_la_SOURCES += vsockChannel.c +libRpcChannel_la_SOURCES += simpleSocket.c libRpcChannel_la_CPPFLAGS = libRpcChannel_la_CPPFLAGS += @VMTOOLS_CPPFLAGS@ diff --git a/open-vm-tools/lib/rpcChannel/bdoorChannel.c b/open-vm-tools/lib/rpcChannel/bdoorChannel.c index c3e11a3f7..c709c53d0 100644 --- a/open-vm-tools/lib/rpcChannel/bdoorChannel.c +++ b/open-vm-tools/lib/rpcChannel/bdoorChannel.c @@ -29,41 +29,11 @@ #include "rpcout.h" #include "util.h" -/** Max amount of time (in .01s) that the RpcIn loop will sleep for. */ -#define RPCIN_MAX_DELAY 10 - typedef struct BackdoorChannel { - GMainContext *mainCtx; - GStaticMutex outLock; - RpcIn *in; - RpcOut *out; - gboolean inStarted; - gboolean outStarted; + RpcOut *out; } BackdoorChannel; -/** - * Initializes internal state for the inbound channel. - * - * @param[in] chan The RPC channel instance. - * @param[in] ctx Main application context. - * @param[in] appName Unused. - * @param[in] appCtx Unused. - */ - -static void -RpcInSetup(RpcChannel *chan, - GMainContext *ctx, - const char *appName, - gpointer appCtx) -{ - BackdoorChannel *bdoor = chan->_private; - bdoor->mainCtx = g_main_context_ref(ctx); - bdoor->in = RpcIn_Construct(ctx, RpcChannel_Dispatch, chan); - ASSERT(bdoor->in != NULL); -} - - /** * Starts the RpcIn loop and the RpcOut channel. * @@ -75,30 +45,22 @@ RpcInSetup(RpcChannel *chan, */ static gboolean -RpcInStart(RpcChannel *chan) +BkdoorChannelStart(RpcChannel *chan) { gboolean ret = TRUE; BackdoorChannel *bdoor = chan->_private; - if (bdoor->outStarted) { - /* Already started. Make sure both channels are in sync and return. */ - ASSERT(bdoor->in == NULL || bdoor->inStarted); - return ret; - } else { - ASSERT(bdoor->in == NULL || !bdoor->inStarted); - } - - if (bdoor->in != NULL) { - ret = RpcIn_start(bdoor->in, RPCIN_MAX_DELAY, RpcChannel_Error, chan); - } + ret = chan->in == NULL || chan->inStarted; if (ret) { ret = RpcOut_start(bdoor->out); if (!ret) { - RpcIn_stop(bdoor->in); + if (chan->inStarted) { + RpcIn_stop(chan->in); + chan->inStarted = FALSE; + } } } - bdoor->inStarted = (bdoor->in != NULL); - bdoor->outStarted = TRUE; + chan->outStarted = ret; return ret; } @@ -115,28 +77,17 @@ RpcInStart(RpcChannel *chan) */ static void -RpcInStop(RpcChannel *chan) +BkdoorChannelStop(RpcChannel *chan) { BackdoorChannel *bdoor = chan->_private; - g_static_mutex_lock(&bdoor->outLock); if (bdoor->out != NULL) { - if (bdoor->outStarted) { + if (chan->outStarted) { RpcOut_stop(bdoor->out); } - bdoor->outStarted = FALSE; + chan->outStarted = FALSE; } else { - ASSERT(!bdoor->outStarted); - } - g_static_mutex_unlock(&bdoor->outLock); - - if (bdoor->in != NULL) { - if (bdoor->inStarted) { - RpcIn_stop(bdoor->in); - } - bdoor->inStarted = FALSE; - } else { - ASSERT(!bdoor->inStarted); + ASSERT(!chan->outStarted); } } @@ -150,19 +101,13 @@ RpcInStop(RpcChannel *chan) */ static void -RpcInShutdown(RpcChannel *chan) +BkdoorChannelShutdown(RpcChannel *chan) { BackdoorChannel *bdoor = chan->_private; - RpcInStop(chan); - if (bdoor->in != NULL) { - RpcIn_Destruct(bdoor->in); - } + BkdoorChannelStop(chan); RpcOut_Destruct(bdoor->out); - g_static_mutex_free(&bdoor->outLock); - if (bdoor->mainCtx != NULL) { - g_main_context_unref(bdoor->mainCtx); - } g_free(bdoor); + chan->_private = NULL; } @@ -179,19 +124,18 @@ RpcInShutdown(RpcChannel *chan) */ static gboolean -RpcInSend(RpcChannel *chan, - char const *data, - size_t dataLen, - char **result, - size_t *resultLen) +BkdoorChannelSend(RpcChannel *chan, + char const *data, + size_t dataLen, + char **result, + size_t *resultLen) { gboolean ret = FALSE; const char *reply; size_t replyLen; BackdoorChannel *bdoor = chan->_private; - g_static_mutex_lock(&bdoor->outLock); - if (!bdoor->outStarted) { + if (!chan->outStarted) { goto exit; } @@ -225,7 +169,7 @@ RpcInSend(RpcChannel *chan, } else { g_warning("Couldn't restart RpcOut channel; bad things may happen " "until the RPC channel is reset.\n"); - bdoor->outStarted = FALSE; + chan->outStarted = FALSE; } } @@ -248,11 +192,50 @@ RpcInSend(RpcChannel *chan, } exit: - g_static_mutex_unlock(&bdoor->outLock); return ret; } +/** + * Return the channel type. + * + * @param[in] chan RpcChannel + * + * @return backdoor channel type. + */ + +static RpcChannelType +BkdoorChannelGetType(RpcChannel *chan) +{ + return RPCCHANNEL_TYPE_BKDOOR; +} + + +/** + * Helper function to setup RpcChannel callbacks. + * + * @param[in] chan RpcChannel + */ + +static void +BackdoorChannelSetCallbacks(RpcChannel *chan) +{ + static RpcChannelFuncs funcs = { + BkdoorChannelStart, + BkdoorChannelStop, + BkdoorChannelSend, + NULL, + BkdoorChannelShutdown, + BkdoorChannelGetType, + NULL, + NULL + }; + + ASSERT(chan); + chan->funcs = &funcs; +} + + /** * Creates a new RpcChannel channel that uses the backdoor for communication. * @@ -268,20 +251,42 @@ BackdoorChannel_New(void) ret = RpcChannel_Create(); bdoor = g_malloc0(sizeof *bdoor); - g_static_mutex_init(&bdoor->outLock); bdoor->out = RpcOut_Construct(); ASSERT(bdoor->out != NULL); - bdoor->inStarted = FALSE; - bdoor->outStarted = FALSE; + ret->inStarted = FALSE; + ret->outStarted = FALSE; - ret->start = RpcInStart; - ret->stop = RpcInStop; - ret->send = RpcInSend; - ret->setup = RpcInSetup; - ret->shutdown = RpcInShutdown; + BackdoorChannelSetCallbacks(ret); ret->_private = bdoor; return ret; } + +/** + * Fall back to backdoor when another type of RpcChannel fails to start. + * + * @param[in] chan RpcChannel + * + * @return TRUE on success. + */ + +gboolean +BackdoorChannel_Fallback(RpcChannel *chan) +{ + BackdoorChannel *bdoor; + + ASSERT(chan); + ASSERT(chan->_private == NULL); + + bdoor = g_malloc0(sizeof *bdoor); + bdoor->out = RpcOut_Construct(); + ASSERT(bdoor->out != NULL); + + BackdoorChannelSetCallbacks(chan); + chan->_private = bdoor; + + return chan->funcs->start(chan); +} + diff --git a/open-vm-tools/lib/rpcChannel/rpcChannel.c b/open-vm-tools/lib/rpcChannel/rpcChannel.c index 8c4b4ef2d..9ae9393ae 100644 --- a/open-vm-tools/lib/rpcChannel/rpcChannel.c +++ b/open-vm-tools/lib/rpcChannel/rpcChannel.c @@ -30,6 +30,7 @@ #include "strutil.h" #include "vmxrpc.h" #include "xdrutil.h" +#include "rpcin.h" /** Internal state of a channel. */ typedef struct RpcChannelInt { @@ -46,10 +47,11 @@ typedef struct RpcChannelInt { guint rpcErrorCount; } RpcChannelInt; - /** Max number of times to attempt a channel restart. */ #define RPCIN_MAX_RESTARTS 60 +#define LGPFX "RpcChannel: " + static gboolean RpcChannelPing(RpcInData *data); @@ -409,8 +411,8 @@ RpcChannel_Destroy(RpcChannel *chan) size_t i; RpcChannelInt *cdata = (RpcChannelInt *) chan; - if (cdata->impl.shutdown != NULL) { - cdata->impl.shutdown(chan); + if (cdata->impl.funcs != NULL && cdata->impl.funcs->shutdown != NULL) { + cdata->impl.funcs->shutdown(chan); } RpcChannel_UnregisterCallback(chan, &cdata->resetReg); @@ -512,8 +514,12 @@ RpcChannel_Setup(RpcChannel *chan, RpcChannel_RegisterCallback(chan, &gRpcHandlers[i]); } - if (cdata->impl.setup != NULL) { - cdata->impl.setup(&cdata->impl, mainCtx, appName, appCtx); + if (chan->funcs != NULL && chan->funcs->setup != NULL) { + chan->funcs->setup(chan, mainCtx, appName, appCtx); + } else { + chan->mainCtx = g_main_context_ref(mainCtx); + chan->in = RpcIn_Construct(mainCtx, RpcChannel_Dispatch, chan); + ASSERT(chan->in != NULL); } } @@ -615,3 +621,281 @@ RpcChannel_UnregisterCallback(RpcChannel *chan, } } + +/** + * Create an RpcChannel instance using a prefered channel implementation, + * currently this is VSockChannel. + * + * @return RpcChannel + */ + +RpcChannel * +RpcChannel_New(void) +{ + RpcChannel *chan; +#if defined(linux) || defined(_WIN32) + chan = VSockChannel_New(); +#else + chan = BackdoorChannel_New(); +#endif + if (chan) { + g_static_mutex_init(&chan->outLock); + } + return chan; +} + + +/** + * Wrapper for the shutdown function of an RPC channel struct. + * + * @param[in] chan The RPC channel instance. + */ + +void +RpcChannel_Shutdown(RpcChannel *chan) +{ + if (chan != NULL) { + g_static_mutex_free(&chan->outLock); + } + + if (chan != NULL && chan->funcs != NULL && chan->funcs->shutdown != NULL) { + if (chan->in != NULL) { + RpcIn_Destruct(chan->in); + } + if (chan->mainCtx != NULL) { + g_main_context_unref(chan->mainCtx); + } + chan->funcs->shutdown(chan); + } +} + + +/** + * Start an RPC channel. We may fallback to backdoor channel when other type + * of channel fails to start. + * + * @param[in] chan The RPC channel instance. + * + * @return TRUE on success. + */ + +gboolean +RpcChannel_Start(RpcChannel *chan) +{ + gboolean ok; + const RpcChannelFuncs *funcs; + + if (chan == NULL || chan->funcs == NULL || chan->funcs->start == NULL) { + return FALSE; + } + + if (chan->outStarted) { + /* Already started. Make sure both channels are in sync and return. */ + ASSERT(chan->in == NULL || chan->inStarted); + return TRUE; + } else { + ASSERT(chan->in == NULL || !chan->inStarted); + } + + if (chan->in != NULL) { + ok = RpcIn_start(chan->in, RPCIN_MAX_DELAY, RpcChannel_Error, chan); + chan->inStarted = ok; + } + + funcs = chan->funcs; + ok = funcs->start(chan); + + if (!ok && funcs->onStartErr != NULL) { + g_warning(LGPFX "Fallback to backdoor ...\n"); + funcs->onStartErr(chan); + ok = BackdoorChannel_Fallback(chan); + } + + return ok; +} + + +/** + * Wrapper for the stop function of an RPC channel struct. + * + * @param[in] chan The RPC channel instance. + */ + +void +RpcChannel_Stop(RpcChannel *chan) +{ + g_return_if_fail(chan != NULL); + g_return_if_fail(chan->funcs != NULL); + g_return_if_fail(chan->funcs->stop != NULL); + + g_static_mutex_lock(&chan->outLock); + chan->funcs->stop(chan); + + if (chan->in != NULL) { + if (chan->inStarted) { + RpcIn_stop(chan->in); + } + chan->inStarted = FALSE; + } else { + ASSERT(!chan->inStarted); + } + g_static_mutex_unlock(&chan->outLock); +} + + +/** + * Wrapper for get channel type function of an RPC channel struct. + * + * @param[in] chan The RPC channel instance. + */ + +RpcChannelType +RpcChannel_GetType(RpcChannel *chan) +{ + if (chan == NULL || chan->funcs == NULL || chan->funcs->getType == NULL) { + return RPCCHANNEL_TYPE_INACTIVE; + } + return chan->funcs->getType(chan); +} + + +/** + * Send function of an RPC channel struct. Retry once if it fails for + * non-backdoor Channels. Backdoor channel already tries inside. A second try + * may create a different type of channel. + * + * @param[in] chan The RPC channel instance. + * @param[in] data Data to send. + * @param[in] dataLen Number of bytes to send. + * @param[out] result Response from other side (should be freed by caller). + * @param[out] resultLen Number of bytes in response. + * + * @return The status from the remote end (TRUE if call was successful). + */ + +gboolean +RpcChannel_Send(RpcChannel *chan, + char const *data, + size_t dataLen, + char **result, + size_t *resultLen) +{ + gboolean ok; + char *res = NULL; + size_t resLen = 0; + const RpcChannelFuncs *funcs; + + g_debug(LGPFX "Sending: %"FMTSZ"u bytes\n", dataLen); + + ASSERT(chan && chan->funcs); + + g_static_mutex_lock(&chan->outLock); + + funcs = chan->funcs; + ASSERT(funcs->send); + + if (result != NULL) { + *result = NULL; + } + if (resultLen != NULL) { + *resultLen = 0; + } + + ok = funcs->send(chan, data, dataLen, &res, &resLen); + + if (!ok && (funcs->getType(chan) != RPCCHANNEL_TYPE_BKDOOR) && + (funcs->stopRpcOut != NULL)) { + + free(res); + res = NULL; + resLen = 0; + + /* retry once */ + g_debug(LGPFX "Stop RpcOut channel and try to send again ...\n"); + funcs->stopRpcOut(chan); + if (RpcChannel_Start(chan)) { + ok = funcs->send(chan, data, dataLen, &res, &resLen); + goto done; + } + + ok = FALSE; + goto exit; + } + +done: + if (ok) { + g_debug(LGPFX "Recved %"FMTSZ"u bytes\n", resLen); + } + + if (result != NULL) { + *result = res; + } + if (resultLen != NULL) { + *resultLen = resLen; + } + +exit: + g_static_mutex_unlock(&chan->outLock); + return ok; +} + + +/** + * Open/close RpcChannel each time for sending a Rpc message, this is a wrapper + * for RpcChannel APIs. + * + * @param[in] data request data + * @param[in] dataLen data length + * @param[in] result reply + * @param[in] resultLen reply length + + * @returns TRUE on success. + */ + +gboolean +RpcChannel_SendOneRaw(const char *data, + size_t dataLen, + char **result, + size_t *resultLen) +{ + RpcChannel *chan; + gboolean status; + + status = FALSE; + + chan = RpcChannel_New(); + if (chan == NULL) { + if (result != NULL) { + *result = Util_SafeStrdup("RpcChannel: Unable to create " + "the RpcChannel object"); + if (resultLen != NULL) { + *resultLen = strlen(*result); + } + } + goto sent; + } else if (!RpcChannel_Start(chan)) { + if (result != NULL) { + *result = Util_SafeStrdup("RpcChannel: Unable to open the " + "communication channel"); + if (resultLen != NULL) { + *resultLen = strlen(*result); + } + } + goto sent; + } else if (!RpcChannel_Send(chan, data, dataLen, result, resultLen)) { + /* We already have the description of the error */ + goto sent; + } + + status = TRUE; + +sent: + g_debug(LGPFX "Request %s: reqlen=%"FMTSZ"u, replyLen=%"FMTSZ"u\n", + status ? "OK" : "FAILED", dataLen, *resultLen); + if (chan) { + RpcChannel_Stop(chan); + RpcChannel_Destroy(chan); + } + + return status; +} diff --git a/open-vm-tools/lib/rpcChannel/rpcChannelInt.h b/open-vm-tools/lib/rpcChannel/rpcChannelInt.h index 67aef37a4..ccc48cae2 100644 --- a/open-vm-tools/lib/rpcChannel/rpcChannelInt.h +++ b/open-vm-tools/lib/rpcChannel/rpcChannelInt.h @@ -27,9 +27,45 @@ #include "vmware/tools/guestrpc.h" +/** Max amount of time (in .01s) that the RpcIn loop will sleep for. */ +#define RPCIN_MAX_DELAY 10 + +struct RpcIn; + +/** a list of interface functions for a channel implementation */ +typedef struct _RpcChannelFuncs{ + gboolean (*start)(RpcChannel *); + void (*stop)(RpcChannel *); + gboolean (*send)(RpcChannel *, char const *data, size_t dataLen, + char **result, size_t *resultLen); + void (*setup)(RpcChannel *chan, GMainContext *mainCtx, + const char *appName, gpointer appCtx); + void (*shutdown)(RpcChannel *); + RpcChannelType (*getType)(RpcChannel *chan); + void (*onStartErr)(RpcChannel *); + gboolean (*stopRpcOut)(RpcChannel *); +} RpcChannelFuncs; + +/** Defines the interface between the application and the RPC channel. */ +struct _RpcChannel { + const RpcChannelFuncs *funcs; + gpointer _private; + GMainContext *mainCtx; + const char *appName; + gpointer appCtx; + GStaticMutex outLock; + struct RpcIn *in; + gboolean inStarted; + gboolean outStarted; +}; + void RpcChannel_Error(void *_state, char const *status); +RpcChannel *VSockChannel_New(void); +RpcChannel *BackdoorChannel_New(void); +gboolean +BackdoorChannel_Fallback(RpcChannel *chan); #endif /* _RPCCHANNELINT_H_ */ diff --git a/open-vm-tools/lib/rpcChannel/simpleSocket.c b/open-vm-tools/lib/rpcChannel/simpleSocket.c new file mode 100644 index 000000000..2a7d5db79 --- /dev/null +++ b/open-vm-tools/lib/rpcChannel/simpleSocket.c @@ -0,0 +1,625 @@ +/********************************************************* + * Copyright (C) 2013 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation version 2.1 and no later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + *********************************************************/ + +/* + * simpleSocket.c -- + * + * Simple wrappers for socket. + * + */ + +#include +#if defined(linux) +#include +#endif + +#include "simpleSocket.h" +#include "vmci_defs.h" +#include "vmci_sockets.h" +#include "dataMap.h" +#include "err.h" + +#define LGPFX "SimpleSock: " + + +static int +SocketGetLastError(void); + +/* + *----------------------------------------------------------------------------- + * + * SocketStartup -- + * + * Win32 special socket init. + * + * Results: + * TRUE on success + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static gboolean +SocketStartup(void) +{ +#if defined(_WIN32) + int err; + WSADATA wsaData; + + err = WSAStartup(MAKEWORD(2, 0), &wsaData); + if (err) { + g_warning(LGPFX "Error in WSAStartup: %d[%s]\n", err, + Err_Errno2String(err)); + return FALSE; + } + + if (2 != LOBYTE(wsaData.wVersion) || 0 != HIBYTE(wsaData.wVersion)) { + g_warning(LGPFX "Unsupported Winsock version %d.%d\n", + LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion)); + return FALSE; + } +#endif + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * SocketCleanup -- + * + * Win32 special socket cleanup. + * + * Results: + * TRUE on success, FALSE on failure. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static gboolean +SocketCleanup(void) +{ +#if defined(_WIN32) + int err = WSACleanup(); + if (err) { + g_warning(LGPFX "Error in WSACleanup: %d[%s]\n", err, + Err_Errno2String(err)); + return FALSE; + } +#endif + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * Socket_Close -- + * + * wrapper for socket close. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +Socket_Close(SOCKET sock) +{ + int res; + +#if defined(_WIN32) + res = closesocket(sock); +#else + res = close(sock); +#endif + + if (res == SOCKET_ERROR) { + int err = SocketGetLastError(); + g_warning(LGPFX "Error in closing socket %d: %d[%s]\n", + sock, err, Err_Errno2String(err)); + } + + SocketCleanup(); +} + + +/* + *----------------------------------------------------------------------------- + * + * SocketGetLastError -- + * + * Get the last error code. + * + * Results: + * error code. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static int +SocketGetLastError(void) +{ +#if defined(_WIN32) + return WSAGetLastError(); +#else + return errno; +#endif +} + + +/* + *----------------------------------------------------------------------------- + * + * Socket_Recv -- + * + * Block until given number of bytes of data is received or error occurs. + * + * Results: + * TRUE on success, FALSE on failure. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +gboolean +Socket_Recv(SOCKET fd, // IN + char *buf, // OUT + int len) // IN +{ + int remaining = len; + int rv; + int sysErr; + + while (remaining > 0) { + rv = recv(fd, buf , remaining, 0); + if (rv == 0) { + g_warning(LGPFX "Socket %d closed by peer.", fd); + return FALSE; + } + if (rv == SOCKET_ERROR) { + sysErr = SocketGetLastError(); + if (sysErr == SYSERR_EINTR) { + continue; + } + g_warning(LGPFX "Recv error for socket %d: %d[%s]", fd, sysErr, + Err_Errno2String(sysErr)); + return FALSE; + } + remaining -= rv; + buf += rv; + } + + g_debug(LGPFX "Recved %d bytes from socket %d\n", len, fd); + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * Socket_Send -- + * + * Block until the given number of bytes of data is sent or error occurs. + * + * Results: + * TRUE on success, FALSE on failure. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +gboolean +Socket_Send(SOCKET fd, // IN + char *buf, // IN + int len) // IN +{ + int left = len; + int sent = 0; + int rv; + int sysErr; + + while (left > 0) { + rv = send(fd, buf + sent, left, 0); + if (rv == SOCKET_ERROR) { + sysErr = SocketGetLastError(); + if (sysErr == SYSERR_EINTR) { + continue; + } + g_warning(LGPFX "Send error for socket %d: %d[%s]", fd, sysErr, + Err_Errno2String(sysErr)); + return FALSE; + } + left -= rv; + sent += rv; + } + + g_debug(LGPFX "Sent %d bytes from socket %d\n", len, fd); + return TRUE; +} + + +/* + *---------------------------------------------------------------------------- + * + * Socket_ConnectVMCI -- + * + * Connect to VMCI port in blocking mode. + * If isPriv is true, we will try to bind the local port to a port that + * is less than 1024. + * + * Results: + * returns the raw socket on sucess, otherwise INVALID_SOCKET; + * + * Side effects: + * None + * + *---------------------------------------------------------------------------- + */ + +SOCKET +Socket_ConnectVMCI(unsigned int cid, // IN + unsigned int port, // IN + gboolean isPriv, // IN + SockConnError *outError) // OUT +{ + struct sockaddr_vm addr; + SOCKET fd; + SockConnError error = SOCKERR_GENERIC; + int sysErr; + socklen_t addrLen = sizeof addr; + + if (outError) { + *outError = SOCKERR_SUCCESS; + } + + if (!SocketStartup()) { + goto error; + } + + memset((char *)&addr, 0, sizeof addr); + addr.svm_family = VMCISock_GetAFValue(); + addr.svm_cid = cid; + addr.svm_port = port; + + g_debug(LGPFX "creating new socket, connecting to %u:%u\n", cid, port); + + fd = socket(addr.svm_family, SOCK_STREAM, 0); + if (fd == INVALID_SOCKET) { + sysErr = SocketGetLastError(); + g_warning(LGPFX "failed to create socket, error %d: %s\n", + sysErr, Err_Errno2String(sysErr)); + goto error; + } + + if (isPriv) { + struct sockaddr_vm localAddr; + gboolean bindOk = FALSE; + int localPort; + + memset(&localAddr, 0, sizeof localAddr); + localAddr.svm_family = addr.svm_family; + localAddr.svm_cid = VMCISock_GetLocalCID(); + + /* Try to bind to port 1~1023 for a privileged user. */ + for (localPort = PRIVILEGED_PORT_MAX; + localPort >= PRIVILEGED_PORT_MIN; localPort--) { + + localAddr.svm_port = localPort; + + if (bind(fd, (struct sockaddr *)&localAddr, sizeof localAddr) != 0) { + sysErr = SocketGetLastError(); + if (sysErr == SYSERR_EACCESS) { + g_warning(LGPFX "Couldn't bind to privileged port for " + "socket %d\n", fd); + error = SOCKERR_EACCESS; + Socket_Close(fd); + goto error; + } + if (sysErr == SYSERR_EADDRINUSE) { + continue; + } + g_warning(LGPFX "could not bind socket, error %d: %s\n", sysErr, + Err_Errno2String(sysErr)); + Socket_Close(fd); + error = SOCKERR_BIND; + goto error; + } else { + bindOk = TRUE; + break; + } + } + + if (!bindOk) { + g_warning(LGPFX "Failed to bind to privileged port for socket %d, " + "no port available\n", fd); + error = SOCKERR_BIND; + Socket_Close(fd); + goto error; + } else { + g_debug(LGPFX "Successfully bound to port %d for socket %d\n", + localAddr.svm_port, fd); + } + } + + if (connect(fd, (struct sockaddr *)&addr, addrLen) != 0) { + sysErr = SocketGetLastError(); + g_warning(LGPFX "socket connect failed, error %d: %s\n", + sysErr, Err_Errno2String(sysErr)); + Socket_Close(fd); + error = SOCKERR_CONNECT; + goto error; + } + + g_debug(LGPFX "socket %d connected\n", fd); + return fd; + +error: + if (outError) { + *outError = error; + } + + return INVALID_SOCKET; +} + + +/* + *----------------------------------------------------------------------------- + * + * Socket_DecodePacket -- + * + * Helper function to decode received packet in DataMap encoding format. + * + * Result: + * None + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static gboolean +Socket_DecodePacket(const char *recvBuf, // IN + int fullPktLen, // IN + char **payload, // OUT + int32 *payloadLen) // OUT +{ + ErrorCode res; + DataMap map; + char *buf; + int32 len; + + *payload = NULL; + *payloadLen = 0; + + /* decoding the packet */ + res = DataMap_Deserialize(recvBuf, fullPktLen, &map); + if (res != DMERR_SUCCESS) { + g_debug(LGPFX "Error in dataMap decoding, error=%d\n", res); + return FALSE; + } + + res = DataMap_GetString(&map, GUESTRPCPKT_FIELD_PAYLOAD, &buf, &len); + if (res == DMERR_SUCCESS) { + char *tmpPtr = malloc(len + 1); + if (tmpPtr == NULL) { + g_debug(LGPFX "Error in allocating memory\n"); + goto error; + } + memcpy(tmpPtr, buf, len); + /* add a trailing 0 for backward compatibility */ + tmpPtr[len] = '\0'; + + *payload = tmpPtr; + *payloadLen = len; + } else { + g_debug(LGPFX "Error in decoding payload, error=%d\n", res); + goto error; + } + + DataMap_Destroy(&map); + return TRUE; + +error: + DataMap_Destroy(&map); + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * Socket_PackSendData -- + * + * Helper function for building send packet and serialize it. + * + * Result: + * TRUE on sucess, FALSE otherwise. + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static gboolean +Socket_PackSendData(const char *buf, // IN + int len, // IN + char **serBuf, // OUT + int32 *serBufLen) // OUT +{ + DataMap map; + ErrorCode res; + char *newBuf; + gboolean mapCreated = FALSE; + int64 pktType = GUESTRPCPKT_TYPE_DATA; + + res = DataMap_Create(&map); + if (res != DMERR_SUCCESS) { + goto error; + } + + mapCreated = TRUE; + res = DataMap_SetInt64(&map, GUESTRPCPKT_FIELD_TYPE, + pktType, TRUE); + if (res != DMERR_SUCCESS) { + goto error; + } + + newBuf = malloc(len); + if (newBuf == NULL) { + g_debug(LGPFX "Error in allocating memory.\n"); + goto error; + } + memcpy(newBuf, buf, len); + res = DataMap_SetString(&map, GUESTRPCPKT_FIELD_PAYLOAD, newBuf, + len, TRUE); + if (res != DMERR_SUCCESS) { + free(newBuf); + goto error; + } + + res = DataMap_Serialize(&map, serBuf, serBufLen); + if (res != DMERR_SUCCESS) { + goto error; + } + + DataMap_Destroy(&map); + return TRUE; + +error: + if (mapCreated) { + DataMap_Destroy(&map); + } + g_debug(LGPFX "Error in dataMap encoding\n"); + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * Socket_RecvPacket -- + * + * Helper function to recv a dataMap packet over the socket. + * The caller has to *free* the payload to avoid memory leak. + * + * Result: + * TRUE on sucess, FALSE otherwise. + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +gboolean +Socket_RecvPacket(SOCKET sock, // IN + char **payload, // OUT + int *payloadLen) // OUT +{ + gboolean ok; + int32 packetLen; + int packetLenSize = sizeof packetLen; + int fullPktLen; + char *recvBuf; + int recvBufLen; + + ok = Socket_Recv(sock, (char *)&packetLen, packetLenSize); + if (!ok) { + g_debug(LGPFX "error in recving packet header, err=%d\n", + SocketGetLastError()); + return FALSE; + } + + fullPktLen = ntohl(packetLen) + packetLenSize; + recvBufLen = fullPktLen; + recvBuf = malloc(recvBufLen); + if (recvBuf == NULL) { + g_debug(LGPFX "Could not allocate recv buffer.\n"); + return FALSE; + } + + memcpy(recvBuf, &packetLen, packetLenSize); + ok = Socket_Recv(sock, recvBuf + packetLenSize, + fullPktLen - packetLenSize); + if (!ok) { + g_debug(LGPFX "error in recving packet, err=%d\n", + SocketGetLastError()); + free(recvBuf); + return FALSE; + } + + ok = Socket_DecodePacket(recvBuf, fullPktLen, payload, payloadLen); + free(recvBuf); + return ok; +} + + +/* + *----------------------------------------------------------------------------- + * + * Socket_SendPacket -- + * + * Helper function to send a dataMap packet over the socket. + * + * Result: + * TRUE on sucess, FALSE otherwise. + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +gboolean +Socket_SendPacket(SOCKET sock, // IN + const char *payload, // IN + int payloadLen) // IN +{ + gboolean ok; + char *sendBuf; + int sendBufLen; + + if (!Socket_PackSendData(payload, payloadLen, &sendBuf, &sendBufLen)) { + return FALSE; + } + + ok = Socket_Send(sock, sendBuf, sendBufLen); + free(sendBuf); + + return ok; +} diff --git a/open-vm-tools/lib/rpcChannel/simpleSocket.h b/open-vm-tools/lib/rpcChannel/simpleSocket.h new file mode 100644 index 000000000..cff9df58f --- /dev/null +++ b/open-vm-tools/lib/rpcChannel/simpleSocket.h @@ -0,0 +1,89 @@ +/********************************************************* + * Copyright (C) 2013 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation version 2.1 and no later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + *********************************************************/ + +#ifndef _SIMPLESOCKET_H_ +#define _SIMPLESOCKET_H_ + +/** + * @file simpleSocket.h + * + * header of simple socket wrappers. + */ + +#include + +#if defined(_WIN32) +#include +#include +#else +#include +#endif + +#include "vmci_defs.h" +#include "vmware/guestrpc/tclodefs.h" + +typedef enum { + SOCKERR_SUCCESS, + SOCKERR_GENERIC, + SOCKERR_CONNECT, + SOCKERR_BIND, + SOCKERR_EACCESS +} SockConnError; + +#if defined(_WIN32) + +#define SYSERR_EADDRINUSE WSAEADDRINUSE +#define SYSERR_EACCESS WSAEACCES +#define SYSERR_EINTR WSAEINTR + +typedef int socklen_t; + +#else /* !_WIN32 */ + +#define SYSERR_EADDRINUSE EADDRINUSE +#define SYSERR_EACCESS EACCES +#define SYSERR_EINTR EINTR + +typedef int SOCKET; +#define SOCKET_ERROR (-1) +#define INVALID_SOCKET ((SOCKET) -1) + +#endif + +#define PRIVILEGED_PORT_MAX 1023 +#define PRIVILEGED_PORT_MIN 1 + +void Socket_Close(SOCKET sock); +SOCKET Socket_ConnectVMCI(unsigned int cid, + unsigned int port, + gboolean isPriv, + SockConnError *outError); +gboolean Socket_Recv(SOCKET fd, + char *buf, + int len); +gboolean Socket_Send(SOCKET fd, + char *buf, + int len); +gboolean Socket_RecvPacket(SOCKET sock, + char **payload, + int *payloadLen); +gboolean Socket_SendPacket(SOCKET sock, + const char *payload, + int payloadLen); + +#endif /* _SIMPLESOCKET_H_ */ diff --git a/open-vm-tools/lib/rpcChannel/vsockChannel.c b/open-vm-tools/lib/rpcChannel/vsockChannel.c new file mode 100644 index 000000000..85a934880 --- /dev/null +++ b/open-vm-tools/lib/rpcChannel/vsockChannel.c @@ -0,0 +1,581 @@ +/********************************************************* + * Copyright (C) 2013 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation version 2.1 and no later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + *********************************************************/ + +/* + * vsockChannel.c -- + * + * Implement RpcChannel using vsocket. + * + */ + +#include +#include + +#include "simpleSocket.h" +#include "rpcChannelInt.h" +#include "rpcin.h" +#include "util.h" + +#define LGPFX "VSockChan: " + +typedef struct VSockOut { + SOCKET fd; + char *payload; + int payloadLen; + RpcChannelType type; +} VSockOut; + +typedef struct VSockChannel { + VSockOut *out; +} VSockChannel; + +static void VSockChannelShutdown(RpcChannel *chan); + + +/* + *----------------------------------------------------------------------------- + * + * VSockCreateConn -- + * + * Create vsocket connection. we try a privileged connection first, + * fallback to unprivileged one if that fails. + * + * Result: + * a valid socket/fd on success or INVALID_SOCKET on failure. + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static SOCKET +VSockCreateConn(gboolean *isPriv) // OUT +{ + SockConnError err; + SOCKET fd; + + g_debug(LGPFX "Creating privileged vsocket ...\n"); + fd = Socket_ConnectVMCI(VMCI_HYPERVISOR_CONTEXT_ID, + GUESTRPC_RPCI_VSOCK_LISTEN_PORT, + TRUE, &err); + + if (fd != INVALID_SOCKET) { + g_debug(LGPFX "Successfully created priv vsocket %d\n", fd); + *isPriv = TRUE; + return fd; + } + + if (err == SOCKERR_EACCESS) { + g_debug(LGPFX "Creating unprivileged vsocket ...\n"); + fd = Socket_ConnectVMCI(VMCI_HYPERVISOR_CONTEXT_ID, + GUESTRPC_RPCI_VSOCK_LISTEN_PORT, + FALSE, &err); + if (fd != INVALID_SOCKET) { + g_debug(LGPFX "Successfully created unpriv vsocket %d\n", fd); + *isPriv = FALSE; + return fd; + } + } + + g_warning(LGPFX "Failed to create vsocket channel, err=%d\n", err); + return INVALID_SOCKET; +} + + +/* + *----------------------------------------------------------------------------- + * + * VSockOutConstruct -- + * + * Constructor for the VSockOut object + * + * Results: + * New VSockOut object. + * + * Side effects: + * Allocates memory. + * + *----------------------------------------------------------------------------- + */ + +static VSockOut * +VSockOutConstruct(void) +{ + VSockOut *out = calloc(1, sizeof *out); + + if (out != NULL) { + out->fd = INVALID_SOCKET; + out->type = RPCCHANNEL_TYPE_INACTIVE; + } + return out; +} + + +/* + *----------------------------------------------------------------------------- + * + * VSockOutDestruct -- + * + * Destructor for the VSockOut object. + * + * Results: + * None. + * + * Side effects: + * Frees VSockOut object memory. + * + *----------------------------------------------------------------------------- + */ + +static void +VSockOutDestruct(VSockOut *out) // IN +{ + + ASSERT(out); + ASSERT(out->fd == INVALID_SOCKET); + + free(out->payload); + free(out); +} + + +/* + *----------------------------------------------------------------------------- + * + * VSockOutStart -- + * + * Open the channel + * + * Result: + * TRUE on success + * FALSE on failure + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static gboolean +VSockOutStart(VSockOut *out) // IN +{ + gboolean isPriv; + + ASSERT(out); + ASSERT(out->fd == INVALID_SOCKET); + + out->fd = VSockCreateConn(&isPriv); + if (out->fd != INVALID_SOCKET) { + out->type = isPriv ? RPCCHANNEL_TYPE_PRIV_VSOCK : + RPCCHANNEL_TYPE_UNPRIV_VSOCK; + } + return out->fd != INVALID_SOCKET; +} + + +/* + *----------------------------------------------------------------------------- + * + * VSockOutStop -- + * + * Close the channel + * + * Result + * TRUE on success + * FALSE on failure + * + * Side-effects + * None. + * + *----------------------------------------------------------------------------- + */ + +static gboolean +VSockOutStop(VSockOut *out) // IN +{ + ASSERT(out); + + if (out->fd != INVALID_SOCKET) { + Socket_Close(out->fd); + out->fd = INVALID_SOCKET; + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * VSockOutSend -- + * + * Make VMware synchronously execute a TCLO command + * + * Unlike the other send varieties, VSockOutSend requires that the + * caller pass non-NULL reply and repLen arguments. + * + * Result + * TRUE on success. 'reply' contains the result of the rpc + * FALSE on error. 'reply' will contain a description of the error + * + * In both cases, the caller should not free the reply. + * + * Side-effects + * None + * + *----------------------------------------------------------------------------- + */ + +static gboolean +VSockOutSend(VSockOut *out, // IN + const char *request, // IN + size_t reqLen, // IN + const char **reply, // OUT + size_t *repLen) // OUT +{ + ASSERT(out); + ASSERT(out->fd != INVALID_SOCKET); + + *reply = NULL; + *repLen = 0; + + g_debug(LGPFX "Sending request for conn %d, reqLen=%d\n", + out->fd, (int)reqLen); + + if (!Socket_SendPacket(out->fd, request, reqLen)) { + *reply = "VSockOut: Unable to send data for the RPCI command"; + goto error; + } + + free(out->payload); + out->payload = NULL; + + if (!Socket_RecvPacket(out->fd, &out->payload, &out->payloadLen)) { + *reply = "VSockOut: Unable to receive the result of the RPCI command"; + goto error; + } + + if (out->payloadLen < 2 || + ((out->payload[0] != '1') && (out->payload[0] != '0')) || + out->payload[1] != ' ') { + *reply = "VSockOut: Invalid format for the result of the RPCI command"; + goto error; + } + + *reply = out->payload + 2; + *repLen = out->payloadLen - 2; + + g_debug("VSockOut: recved %d bytes for conn %d\n", out->payloadLen, out->fd); + + return out->payload[0] == '1'; + +error: + *repLen = strlen(*reply); + return FALSE; +} + + +/* + *----------------------------------------------------------------------------- + * + * VSockChannelOnStartErr -- + * + * Callback function to cleanup after channel start failure. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static void +VSockChannelOnStartErr(RpcChannel *chan) // IN +{ + VSockChannel *vsock = chan->_private; + + /* destroy VSockOut part only */ + VSockOutDestruct(vsock->out); + chan->_private = NULL; +} + + +/* + *----------------------------------------------------------------------------- + * + * VSockChannelStart -- + * + * Starts the RpcIn loop and the VSockOut channel. + * + * Results: + * TRUE on success. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static gboolean +VSockChannelStart(RpcChannel *chan) // IN +{ + gboolean ret = TRUE; + VSockChannel *vsock = chan->_private; + + ret = chan->in == NULL || chan->inStarted; + + if (ret) { + ret = VSockOutStart(vsock->out); + if (!ret) { + if (chan->inStarted) { + RpcIn_stop(chan->in); + chan->inStarted = FALSE; + } + } + } + chan->outStarted = ret; + + return ret; +} + + +/* + *----------------------------------------------------------------------------- + * + * VSockChannelStop -- + * + * Stops a channel, keeping internal state so that it can be restarted + * later. It's safe to call this function more than once. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static void +VSockChannelStop(RpcChannel *chan) // IN +{ + VSockChannel *vsock = chan->_private; + + if (vsock->out != NULL) { + if (chan->outStarted) { + VSockOutStop(vsock->out); + } + chan->outStarted = FALSE; + } else { + ASSERT(!chan->outStarted); + } +} + + +/* + *----------------------------------------------------------------------------- + * + * VSockChannelShutdown -- + * + * Shuts down the Rpc channel. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static void +VSockChannelShutdown(RpcChannel *chan) // IN +{ + VSockChannel *vsock = chan->_private; + + VSockChannelStop(chan); + VSockOutDestruct(vsock->out); + g_free(vsock); + chan->_private = NULL; +} + + +/* + *----------------------------------------------------------------------------- + * + * VSockChannelSend -- + * + * Sends the data using the vsocket channel. + * + * Result: + * TRUE on success + * FALSE on failure + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static gboolean +VSockChannelSend(RpcChannel *chan, // IN + char const *data, // IN + size_t dataLen, // IN + char **result, // OUT + size_t *resultLen) // OUT +{ + gboolean ret = FALSE; + VSockChannel *vsock = chan->_private; + const char *reply; + size_t replyLen; + + if (!chan->outStarted) { + goto exit; + } + + ret = VSockOutSend(vsock->out, data, dataLen, &reply, &replyLen); + + if (!ret) { + goto exit; + } + + if (result != NULL) { + if (reply != NULL) { + *result = Util_SafeMalloc(replyLen + 1); + memcpy(*result, reply, replyLen); + (*result)[replyLen] = '\0'; + } else { + *result = NULL; + } + } + + if (resultLen != NULL) { + *resultLen = replyLen; + } + +exit: + return ret; +} + + +/* + *----------------------------------------------------------------------------- + * + * VSockChannel_GetType -- + * + * Return the channel type that being used. + * + * Result: + * return the channel type. + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static RpcChannelType +VSockChannelGetType(RpcChannel *chan) +{ + VSockChannel *vsock = chan->_private; + + if (vsock->out != NULL) { + return vsock->out->type; + } else { + return RPCCHANNEL_TYPE_INACTIVE; + } +} + + +/* + *----------------------------------------------------------------------------- + * + * VSockChannelStopRpcOut -- + * + * Stop the RpcOut channel + * + * Result: + * return TRUE on success. + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static gboolean +VSockChannelStopRpcOut(RpcChannel *chan) +{ + VSockChannel *vsock = chan->_private; + return VSockOutStop(vsock->out); +} + + + + +/* + *----------------------------------------------------------------------------- + * + * VSockChannel_New -- + * + * Creates a new RpcChannel channel that uses the vsocket for + * communication. + * + * Result: + * return A new channel instance (never NULL). + * + * Side-effects: + * None + * + *----------------------------------------------------------------------------- + */ + +RpcChannel * +VSockChannel_New(void) +{ + RpcChannel *chan; + VSockChannel *vsock; + + static RpcChannelFuncs funcs = { + VSockChannelStart, + VSockChannelStop, + VSockChannelSend, + NULL, + VSockChannelShutdown, + VSockChannelGetType, + VSockChannelOnStartErr, + VSockChannelStopRpcOut + }; + + chan = RpcChannel_Create(); + vsock = g_malloc0(sizeof *vsock); + + vsock->out = VSockOutConstruct(); + ASSERT(vsock->out != NULL); + + chan->inStarted = FALSE; + chan->outStarted = FALSE; + + chan->_private = vsock; + chan->funcs = &funcs; + + return chan; +} diff --git a/open-vm-tools/lib/rpcIn/rpcin.c b/open-vm-tools/lib/rpcIn/rpcin.c index 183528ca1..b9534283c 100644 --- a/open-vm-tools/lib/rpcIn/rpcin.c +++ b/open-vm-tools/lib/rpcIn/rpcin.c @@ -625,7 +625,7 @@ RpcInPackSendData(int fd, // IN char *newBuf; gboolean mapCreated = FALSE; int64 pktType = (flags & RPCIN_TCLO_PING) ? - RPCINPKT_TYPE_PING : RPCINPKT_TYPE_DATA; + GUESTRPCPKT_TYPE_PING : GUESTRPCPKT_TYPE_DATA; res = DataMap_Create(&map); if (res != DMERR_SUCCESS) { @@ -633,7 +633,7 @@ RpcInPackSendData(int fd, // IN } mapCreated = TRUE; - res = DataMap_SetInt64(&map, RPCINPKT_FIELD_TYPE, + res = DataMap_SetInt64(&map, GUESTRPCPKT_FIELD_TYPE, pktType, TRUE); if (res != DMERR_SUCCESS) { goto quit; @@ -646,9 +646,10 @@ RpcInPackSendData(int fd, // IN goto quit; } memcpy(newBuf, buf, len); - res = DataMap_SetString(&map, RPCINPKT_FIELD_PAYLOAD, newBuf, + res = DataMap_SetString(&map, GUESTRPCPKT_FIELD_PAYLOAD, newBuf, len, TRUE); if (res != DMERR_SUCCESS) { + free(newBuf); goto quit; } } @@ -854,7 +855,7 @@ RpcInDecodePacket(ConnInfo *conn, // IN return FALSE; } - res = DataMap_GetString(&map, RPCINPKT_FIELD_PAYLOAD, &buf, &len); + res = DataMap_GetString(&map, GUESTRPCPKT_FIELD_PAYLOAD, &buf, &len); if (res == DMERR_SUCCESS) { char *tmpPtr = (char *)malloc(len + 1); if (tmpPtr == NULL) { @@ -1621,7 +1622,7 @@ RpcInOpenChannel(RpcIn *in, // IN } in->conn->in = in; asock = AsyncSocket_ConnectVMCI(VMCI_HYPERVISOR_CONTEXT_ID, - GUESTRPC_VSOCK_LISTEN_PORT, + GUESTRPC_TCLO_VSOCK_LISTEN_PORT, RpcInConnectDone, in->conn, 0, NULL, &res); if (asock == NULL) { diff --git a/open-vm-tools/services/plugins/dndcp/copyPasteDnDX11.cpp b/open-vm-tools/services/plugins/dndcp/copyPasteDnDX11.cpp index 8c97508c4..b5ab52903 100644 --- a/open-vm-tools/services/plugins/dndcp/copyPasteDnDX11.cpp +++ b/open-vm-tools/services/plugins/dndcp/copyPasteDnDX11.cpp @@ -160,8 +160,7 @@ BlockService::ShutdownSignalHandler(const siginfo_t *siginfo, /* shutdown rpc channel to free up VMCI/VSOCKET module usage */ if (ctx->rpc != NULL) { - ASSERT(ctx->rpc->shutdown != NULL); - ctx->rpc->shutdown(ctx->rpc); + RpcChannel_Shutdown(ctx->rpc); ctx->rpc = NULL; } diff --git a/open-vm-tools/services/vmtoolsd/toolsRpc.c b/open-vm-tools/services/vmtoolsd/toolsRpc.c index e8a0bdc69..b29749221 100644 --- a/open-vm-tools/services/vmtoolsd/toolsRpc.c +++ b/open-vm-tools/services/vmtoolsd/toolsRpc.c @@ -45,7 +45,7 @@ */ static void -ToolsCoreCheckReset(struct RpcChannel *chan, +ToolsCoreCheckReset(RpcChannel *chan, gboolean success, gpointer _state) { @@ -245,7 +245,7 @@ ToolsCore_InitRpc(ToolsServiceState *state) state->name); state->ctx.rpc = NULL; } else { - state->ctx.rpc = BackdoorChannel_New(); + state->ctx.rpc = RpcChannel_New(); } app = ToolsCore_GetTcloName(state); if (app == NULL) { diff --git a/open-vm-tools/tests/vmrpcdbg/Makefile.am b/open-vm-tools/tests/vmrpcdbg/Makefile.am index 8d5f2e5b0..ca55ef22f 100644 --- a/open-vm-tools/tests/vmrpcdbg/Makefile.am +++ b/open-vm-tools/tests/vmrpcdbg/Makefile.am @@ -21,6 +21,7 @@ libvmrpcdbg_la_CPPFLAGS = libvmrpcdbg_la_CPPFLAGS += @CUNIT_CPPFLAGS@ libvmrpcdbg_la_CPPFLAGS += @GMODULE_CPPFLAGS@ libvmrpcdbg_la_CPPFLAGS += @VMTOOLS_CPPFLAGS@ +libvmrpcdbg_la_CPPFLAGS += -I$(top_builddir)/lib/rpcChannel libvmrpcdbg_la_LDFLAGS = libvmrpcdbg_la_LDFLAGS += @PLUGIN_LDFLAGS@ diff --git a/open-vm-tools/tests/vmrpcdbg/debugChannel.c b/open-vm-tools/tests/vmrpcdbg/debugChannel.c index 3f8727f6b..7b74dfb9e 100644 --- a/open-vm-tools/tests/vmrpcdbg/debugChannel.c +++ b/open-vm-tools/tests/vmrpcdbg/debugChannel.c @@ -32,6 +32,7 @@ #include "strutil.h" #include "util.h" #include "vmrpcdbgInt.h" +#include "rpcChannelInt.h" #include "vmxrpc.h" #include "xdrutil.h" #include "vmware/tools/utils.h" @@ -313,14 +314,20 @@ RpcDebug_NewDebugChannel(ToolsAppCtx *ctx, { DbgChannelData *cdata; RpcChannel *ret; + static RpcChannelFuncs funcs = { + RpcDebugStart, + RpcDebugStop, + RpcDebugSend, + RpcDebugSetup, + RpcDebugShutdown, + NULL, + NULL, + NULL + }; ASSERT(data != NULL); ret = RpcChannel_Create(); - ret->start = RpcDebugStart; - ret->stop = RpcDebugStop; - ret->send = RpcDebugSend; - ret->setup = RpcDebugSetup; - ret->shutdown = RpcDebugShutdown; + ret->funcs = &funcs; cdata = g_malloc0(sizeof *cdata); cdata->plugin = data->debugPlugin;