From: Oliver Kurth Date: Tue, 4 Sep 2018 22:40:56 +0000 (-0700) Subject: Add VMX logging support to vgauth X-Git-Tag: stable-11.0.0~417 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=47a6790d8b20c25a34488d2b279fe95ff36c44aa;p=thirdparty%2Fopen-vm-tools.git Add VMX logging support to vgauth Add support to log to the VMX. Currently this is just for special error conditions. --- diff --git a/open-vm-tools/vgauth/common/vmxlog.c b/open-vm-tools/vgauth/common/vmxlog.c new file mode 100644 index 000000000..8f4981d39 --- /dev/null +++ b/open-vm-tools/vgauth/common/vmxlog.c @@ -0,0 +1,636 @@ +/********************************************************* + * Copyright (C) 2018 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. + * + *********************************************************/ + +/* + * @file logvmx.c + * + * Simple guest->VMX RPC log support that assumes VMCI is available. + * + * Doesn't share any Tools code or headers; key bits that can't change + * are copied from vmci_sockets.h + * + */ + +#include "vmxlog.h" +#include + +#include +#ifdef _WIN32 +#include +#else +#include +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +/* + * VMX listening address + */ +const int VMX_CID = 0; +const int RPCI_PORT = 976; +#define VMADDR_PORT_ANY ((unsigned int) -1) + +static int gAddressFamily = -1; +#define LOG_RPC_CMD "log" +#define LOG_RPC_CMD_NEW "guest.log.text" + +static gboolean gDisableVMXLogging = TRUE; + +/* + * Error codes for SendString() and SendRpciPacket() + */ +#define VMX_RPC_OK 1 // success +#define VMX_RPC_UNKNOWN 0 // RPC disabled or not supported +#define VMX_RPC_ERROR -1 // failed to send RPC + +/* + * Some typedefs for portability. + */ +#ifdef _WIN32 +/* still have to care about pre-C++11 so use the old instead of stdint.h */ +typedef unsigned __int32 uint32; +typedef unsigned __int64 uint64; + +#endif +#ifdef linux +typedef __uint32_t uint32; +typedef __uint64_t uint64; + +#define SOCKET int +#endif +#ifdef sun +typedef uint32_t uint32; +typedef uint64_t uint64; + +#define SOCKET int +#endif + +#if defined(_WIN32) +typedef unsigned short sa_family_t; +#endif // _WIN32 + + +/* + * Wrapper for socket errnos + */ +static int +GetSocketErrCode(void) +{ +#ifdef _WIN32 + return WSAGetLastError(); +#else + return errno; +#endif +} + + +/* + * start code cut&paste from vmci_sockets.h + * + * This is the subset from vmci_sockets.h required for our purposes. + * this results in a few refs to other parts of the file that were + * left out. + */ + +#ifdef _WIN32 +# include +# define VMCI_SOCKETS_DEVICE L"\\\\.\\VMCI" +# define VMCI_SOCKETS_VERSION 0x81032058 +# define VMCI_SOCKETS_GET_AF_VALUE 0x81032068 +# define VMCI_SOCKETS_GET_LOCAL_CID 0x8103206c +# define VMCI_SOCKETS_UUID_2_CID 0x810320a4 + +static unsigned int +__VMCISock_DeviceIoControl(DWORD cmd) +{ + unsigned int val = (unsigned int)-1; + HANDLE device = CreateFileW(VMCI_SOCKETS_DEVICE, GENERIC_READ, 0, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (INVALID_HANDLE_VALUE != device) { + DWORD ioReturn; + DeviceIoControl(device, cmd, &val, sizeof val, &val, sizeof val, + &ioReturn, NULL); + CloseHandle(device); + device = INVALID_HANDLE_VALUE; + } + return val; +} +#endif //_WIN32 + + +/** + * \brief Address structure for vSockets. + * + * The address family should be set to whatever VMCISock_GetAFValueFd() + * returns. The structure members should all align on their natural + * boundaries without resorting to compiler packing directives. The total + * size of this structure should be exactly the same as that of \c struct + * \c sockaddr. + * + * \see VMCISock_GetAFValueFd() + */ + +struct sockaddr_vm { +#if defined(__APPLE__) || defined(__FreeBSD__) + unsigned char svm_len; +#endif // __APPLE__ || __FreeBSD__ + + /** \brief Address family. \see VMCISock_GetAFValueFd() */ + sa_family_t svm_family; + + /** \cond PRIVATE */ + unsigned short svm_reserved1; + /** \endcond */ + + /** \brief Port. \see VMADDR_PORT_ANY */ + unsigned int svm_port; + + /** \brief Context ID. \see VMADDR_CID_ANY */ + unsigned int svm_cid; + + /** \cond PRIVATE */ + unsigned char svm_zero[sizeof(struct sockaddr) - +#if defined(__APPLE__) + sizeof(unsigned char) - +#endif // __APPLE__ + sizeof(sa_family_t) - + sizeof(unsigned short) - + sizeof(unsigned int) - + sizeof(unsigned int)]; + /** \endcond */ +}; +/* end code copied from vmci_sockets.h */ + + +#if !defined(_MSC_VER) || _MSC_VER <= 1900 +/* + * htonll() for older compilers and non-Windows. + * Assumes the host is little-endian. + */ +static uint64 +htonll(uint64 s) +{ + uint64 out; + unsigned char *buf = (unsigned char *) &out; + + buf[0] = s >> 56 & 0xff; + buf[1] = s >> 48 & 0xff; + buf[2] = s >> 40 & 0xff; + buf[3] = s >> 32 & 0xff; + buf[4] = s >> 24 & 0xff; + buf[5] = s >> 16 & 0xff; + buf[6] = s >> 8 & 0xff; + buf[7] = s >> 0 & 0xff; + + return out; +} +#endif + + +/* + ****************************************************************************** + * MakePacket -- */ /** + * + * Takes 'cmd' and builds an RPC packet out of it, putting in the + * length and header info (properly byte swapped). + * + * See bora-vmsoft/lib/rpcChannel/simpleSocket.c:Socket_PackSendData() + * + * retPacket contains the new packet, which must be g_free()d. + * Returns the size of the new packet. + * + * Returns -1 on failure, size of packet on success. + * + ****************************************************************************** + */ + +static int +MakePacket(const char *cmd, + char **retPacket) +{ + int len; + int tlen; + uint32 plen; + uint32 slen; + char *packet = NULL; + char *p; + struct { + uint32 type; + uint32 fieldId; + uint64 value; + } hdr; + struct { + uint32 type; + uint32 fieldId; + uint32 len; + } payload; + + *retPacket = NULL; + if (cmd == NULL) { + return -1; + } + + len = (int) strlen(cmd); + + /* network byte order is important here */ + hdr.type = htonl(1); // DMFIELDTYPE_INT64 + hdr.fieldId = htonl(1); // GUESTRPCPKT_FIELD_TYPE + hdr.value = htonll(1); // GUESTRPCPKT_TYPE_DATA + + /* + * this part of the data doesn't seem to care about network byte + * order, but do it anyways. + */ + payload.type = htonl(2); // DMFIELDTYPE_STRING + payload.fieldId = htonl(2); // GUESTRPCPKT_FIELD_PAYLOAD + payload.len = htonl(len); // length of 'cmd' + + plen = sizeof(hdr) + sizeof(payload) + len; + + tlen = plen + sizeof(int); + packet = (char *) g_malloc(tlen); + p = packet; + + /* use network byte order overall length */ + slen = htonl(plen); + memcpy(p, (char *)&slen, sizeof slen); + p += sizeof(uint32); + + memcpy(p, &hdr, sizeof hdr); + p += sizeof hdr; + memcpy(p, &payload, sizeof payload); + p += sizeof payload; + memcpy(p, cmd, len); + + *retPacket = packet; + + return tlen; +} + + +/* + ****************************************************************************** + * GetAddressFamily -- */ /** + * + * Returns the vsock socket family on success, -1 on failure. + * + * This assumes modern vsock is in the kernel. + ****************************************************************************** + */ + +int +GetAddressFamily(void) +{ +#ifdef _WIN32 + return __VMCISock_DeviceIoControl(VMCI_SOCKETS_GET_AF_VALUE); +#else + const int AF_VSOCK_LOCAL = 40; + int s = socket(AF_VSOCK_LOCAL, SOCK_DGRAM, 0); + if (s != -1) { + close(s); + return AF_VSOCK_LOCAL; + } + + return -1; +#endif +} + + +/* + ****************************************************************************** + * CreateVMCISocket -- */ /** + * + * Creates, binds and connects a socket to the VMX. + * + * Returns a new socket that should be close()d or -1 on failure. + * + ****************************************************************************** + */ + +static SOCKET +CreateVMCISocket(void) +{ + struct sockaddr_vm localAddr; + struct sockaddr_vm addr; + int ret; + SOCKET fd = socket(gAddressFamily, SOCK_STREAM, 0); + if (fd < 0) { + g_warning("%s: socket() failed %d\n", __FUNCTION__, GetSocketErrCode()); + return -1; + } + + /* bind to a priviledged port */ + memset(&localAddr, 0, sizeof localAddr); + localAddr.svm_family = gAddressFamily; +#ifdef _WIN32 + localAddr.svm_cid = __VMCISock_DeviceIoControl(VMCI_SOCKETS_GET_LOCAL_CID); +#else + localAddr.svm_cid = -1; +#endif + localAddr.svm_port = VMADDR_PORT_ANY; + ret = bind(fd, (struct sockaddr *)&localAddr, sizeof localAddr); + if (ret != 0) { + g_warning("%s: bind() failed %d\n", __FUNCTION__, GetSocketErrCode()); + goto err; + } + + /* connect to destination */ + memset(&addr, 0, sizeof addr); + addr.svm_family = gAddressFamily; + addr.svm_cid = VMX_CID; + addr.svm_port = RPCI_PORT; + + ret = connect(fd, (struct sockaddr *)&addr, sizeof addr); + if (ret < 0) { + g_warning("%s: connect() failed %d\n", __FUNCTION__, GetSocketErrCode()); + goto err; + } + + return fd; +err: +#ifdef _WIN32 + closesocket(fd); +#else + close(fd); +#endif + + return -1; +} + + +/* + ****************************************************************************** + * SendRpciPacket -- */ /** + * + * Sends RPC packet to the VMX. Reads but ignores any response. + * + * @param[in] packet RPC packet. + * @param[in] packetLen Length of packet. + * + * Returns VMX_RPC_ERROR on failure, VMX_RPC_OK on success, VMX_RPC_UNKNOWN + * if RPC failed (doesn't exist or disabled). + * + ****************************************************************************** + */ + +static int +SendRpciPacket(const char *packet, + int packetLen) +{ + SOCKET sock; + char buf[1024]; + char *reply; + int ret; + int retVal = VMX_RPC_OK; + + /* + * Its inefficient to create/destroy the socket each time, but + * there's potential to run the VMX out of connections if we hold it open. + * Since performance isn't a major concern, play it safe. + */ + sock = CreateVMCISocket(); + if (sock < 0) { + g_warning("%s: failed to create VMCI socket\n", __FUNCTION__); + return VMX_RPC_ERROR; + } + ret = send(sock, packet, packetLen, 0); + if (ret != packetLen) { + g_warning("%s: failed to send packet (%d)\n", + __FUNCTION__, GetSocketErrCode()); + retVal = VMX_RPC_ERROR; + goto done; + } + + /* + * Read the answer to see if the RPC went through. + */ + ret = recv(sock, buf, sizeof buf, 0); + /* + * Cheat a bit here -- just get to the text and ignore the header. + * The string data starts 19 chars into the buffer. + * + * XXX should this only happen on 'Unknown'? If the VMX + * level changes on the fly, this can start working. + * + * Possible optimization -- every N minutes, retry the new RPC. + */ + buf[ret] = '\0'; + reply = &buf[18]; + g_debug("%s: RPC returned '%s'\n", __FUNCTION__, reply); + if (g_strcmp0(reply, "disabled") == 0 || + g_strcmp0(reply, "Unknown") == 0) { + g_warning("%s: RPC unknown or disabled\n", __FUNCTION__); + retVal = VMX_RPC_UNKNOWN; + } + +done: +#ifdef _WIN32 + closesocket(sock); +#else + close(sock); +#endif + + return retVal; +} + + +/* + ****************************************************************************** + * VMXLog_Init -- */ /** + * + * Initializes the VMX log facility. + * + * Returns -1 on error, 1 on success. + * + ****************************************************************************** + */ + +int +VMXLog_Init(void) +{ +#ifdef _WIN32 + int ret; + WSADATA wsaData; + + ret = WSAStartup(MAKEWORD(2,0), &wsaData); + if (ret != 0) { + g_warning("%s: Failed to init winsock (%d)\n", __FUNCTION__, ret); + return -1; + } +#endif + gAddressFamily = GetAddressFamily(); + if (gAddressFamily < 0) { + g_warning("%s: Failed to set up VMX logging\n", __FUNCTION__); + return -1; + } + + gDisableVMXLogging = FALSE; + return 1; +} + + +/* + ****************************************************************************** + * VMXLog_Shutdown -- */ /** + * + * Shuts down the VMX log facility. + * + ****************************************************************************** + */ + +void +VMXLog_Shutdown(void) +{ + gDisableVMXLogging = TRUE; + gAddressFamily = -1; +} + + +/* + ****************************************************************************** + * SendString -- */ /** + * + * Formats a message as an RPC and sends it to the VMX. + * + * @param[in] cmd The message to send. + * + * Returns VMX_RPC_ERROR on failure, VMX_RPC_OK on success, VMX_RPC_UNKNOWN + * if RPC failed (doesn't exist or disabled). + ****************************************************************************** + */ + +static int +SendString(const gchar *cmd) +{ + gchar *packet = NULL; + int packetLen; + int ret; + + packetLen = MakePacket(cmd, &packet); + if (packetLen <= 0) { + g_warning("%s: failed to build RPC packet\n", __FUNCTION__); + return VMX_RPC_ERROR; + } + + ret = SendRpciPacket(packet, packetLen); + if (ret == VMX_RPC_ERROR) { + g_warning("%s: failed to send RPC packet\n", __FUNCTION__); + } + + g_free(packet); + return ret; +} + + +/* + ****************************************************************************** + * VMXLog_LogV -- */ /** + * + * Logs to the VMX using va_list arguments. + * + * @param[in] level Logging level (currently unused). + * @param[in] fmt The format message for the event. + * @param[in] args The arguments for @a fmt. + * + ****************************************************************************** + */ + +void +VMXLog_LogV(int level, + const char *fmt, + va_list args) +{ + gchar *msg = NULL; + gchar *cmd = NULL; + int ret; + static gboolean useNewRpc = TRUE; + static gboolean rpcBroken = FALSE; + + /* + * RPCs don't work -- not in a VM or no vmci -- so drop any messages. + */ + if (gDisableVMXLogging || rpcBroken) { + return; + } + + msg = g_strdup_vprintf(fmt, args); +again: + /* + * Try the new logging RPC, fail over to the old + * + * Possible optimization -- every N minutes, retry the new RPC in + * case its been enabled dynamically. + */ + if (useNewRpc) { + /* XXX TODO use the level */ + cmd = g_strdup_printf("%s %s", LOG_RPC_CMD_NEW, msg); + } else { + cmd = g_strdup_printf("%s %s", LOG_RPC_CMD, msg); + } + + ret = SendString(cmd); + g_free(cmd); + cmd = NULL; + if ((ret == VMX_RPC_UNKNOWN) && useNewRpc) { + g_debug("%s: new RPC Failed, using old\n", __FUNCTION__); + useNewRpc = FALSE; + goto again; + } else if (ret == VMX_RPC_ERROR) { + rpcBroken = TRUE; + g_debug("%s: Error sending RPC, assume they aren't supported\n", + __FUNCTION__); + } + + g_free(msg); + msg = NULL; +} + + +/* + ****************************************************************************** + * VMXLog_Log -- */ /** + * + * Logs to the VMX. + * + * @param[in] level Logging level (currently unused). + * @param[in] fmt The format message for the event. + * @param[in] args The arguments for @a fmt. + * + ****************************************************************************** + */ + +void +VMXLog_Log(int level, + const char *fmt, + ...) +{ + va_list args; + + va_start(args, fmt); + VMXLog_LogV(level, fmt, args); + va_end(args); +} diff --git a/open-vm-tools/vgauth/common/vmxlog.h b/open-vm-tools/vgauth/common/vmxlog.h new file mode 100644 index 000000000..963530e3d --- /dev/null +++ b/open-vm-tools/vgauth/common/vmxlog.h @@ -0,0 +1,44 @@ +/********************************************************* + * Copyright (C) 2018 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 _VMXLOG_H_ +#define _VMXLOG_H_ + +/* + * @file vmxlog.h + * + * Logging to the VMX (vmware.log) + */ + +#include "VGAuthBasicDefs.h" + +int VMXLog_Init(void); + +void VMXLog_Shutdown(void); + +void VMXLog_Log(int level, const char *fmt, ...) PRINTF_DECL(2, 3); + +/* + * XXX placeholder until we define levels for the new log RPC + */ +#define VMXLOG_LEVEL_INFO 1 + +void VMXLog_LogV(int level, const char *fmt, va_list args); + +#endif // _VMXLOG_H_ + diff --git a/open-vm-tools/vgauth/service/Makefile.am b/open-vm-tools/vgauth/service/Makefile.am index 575cf3864..c1cee1e66 100644 --- a/open-vm-tools/vgauth/service/Makefile.am +++ b/open-vm-tools/vgauth/service/Makefile.am @@ -1,5 +1,5 @@ ################################################################################ -### Copyright (C) 2014-2017 VMware, Inc. All rights reserved. +### Copyright (C) 2014-2018 VMware, Inc. All rights reserved. ### ### This program is free software; you can redistribute it and/or modify ### it under the terms of version 2 of the GNU General Public License as @@ -46,6 +46,7 @@ VGAuthService_SOURCES += ../common/prefs.c VGAuthService_SOURCES += ../common/usercheck.c VGAuthService_SOURCES += ../common/VGAuthLog.c VGAuthService_SOURCES += ../common/VGAuthUtil.c +VGAuthService_SOURCES += ../common/vmxlog.c VGAuthServicedir = /etc/vmware-tools/vgauth/schemas VGAuthService_SCRIPTS = diff --git a/open-vm-tools/vgauth/service/gio.c b/open-vm-tools/vgauth/service/gio.c index 653bbcd41..6f315cacc 100644 --- a/open-vm-tools/vgauth/service/gio.c +++ b/open-vm-tools/vgauth/service/gio.c @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2011-2016 VMware, Inc. All rights reserved. + * Copyright (C) 2011-2016,2018 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 @@ -203,6 +203,9 @@ ServiceSigtermHandler(gpointer data) (void) ServiceEndMainLoop(NULL); Service_Shutdown(); Log("END SERVICE"); + VMXLog_Log(VMXLOG_LEVEL_INFO, "%s END SERVICE", + VGAUTH_SERVICE_NAME); + VMXLog_Shutdown(); /* * It's safe to just exit here, since we've been called by the glib diff --git a/open-vm-tools/vgauth/service/main.c b/open-vm-tools/vgauth/service/main.c index 7b432b8d5..1e64cd32d 100644 --- a/open-vm-tools/vgauth/service/main.c +++ b/open-vm-tools/vgauth/service/main.c @@ -25,6 +25,7 @@ #include #include "serviceInt.h" #include "service.h" +#include "buildNumber.h" #ifdef _WIN32 #include #include "winCoreDump.h" @@ -144,6 +145,10 @@ ServiceStartAndRun(void) Log("INIT SERVICE\n"); + VMXLog_Init(); + VMXLog_Log(VMXLOG_LEVEL_INFO, "%s %s starting up", + VGAUTH_SERVICE_NAME, BUILD_NUMBER); + #ifdef _WIN32 if (ServiceOldInstanceExists()) { Warning("%s: another instance is running; exiting\n", __FUNCTION__); diff --git a/open-vm-tools/vgauth/service/service.h b/open-vm-tools/vgauth/service/service.h index 00350baa7..87587655c 100644 --- a/open-vm-tools/vgauth/service/service.h +++ b/open-vm-tools/vgauth/service/service.h @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2011-2016 VMware, Inc. All rights reserved. + * Copyright (C) 2011-2018 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 @@ -41,6 +41,7 @@ #include "VGAuthLog.h" #include "serviceInt.h" #include "audit.h" +#include "vmxlog.h" #define VGAUTH_SERVICE_NAME "VGAuthService"