From: Kruti Pendharkar Date: Wed, 10 Dec 2025 05:31:19 +0000 (-0800) Subject: VMTools: services/apps use config env settings to override unhandled system env X-Git-Tag: stable-13.1.0~32 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=2ad2ca8ff6538faedb18106fb230ce2a03e18db5;p=thirdparty%2Fopen-vm-tools.git VMTools: services/apps use config env settings to override unhandled system env settings Previously, only the VMToolsd service and user process would use the config file set or unset environment settings, and only when the process name was known (vmsvc, vmusr). The code to process the config environment variables has moved from tools core to vmtools library. --- diff --git a/open-vm-tools/lib/include/vmware/tools/utils.h b/open-vm-tools/lib/include/vmware/tools/utils.h index d31a03eba..8fec8af3f 100644 --- a/open-vm-tools/lib/include/vmware/tools/utils.h +++ b/open-vm-tools/lib/include/vmware/tools/utils.h @@ -177,6 +177,11 @@ VMTools_GetLibdir(void); #endif +void +VMTools_SetupEnv(const gchar *appName, + GKeyFile *config, + gboolean globalVars); + GSource * VMTools_CreateTimer(gint timeout); diff --git a/open-vm-tools/libvmtools/Makefile.am b/open-vm-tools/libvmtools/Makefile.am index 3e0ea0f32..12f4d9643 100644 --- a/open-vm-tools/libvmtools/Makefile.am +++ b/open-vm-tools/libvmtools/Makefile.am @@ -1,5 +1,7 @@ ################################################################################ -### Copyright (c) 2008-2021,2023 VMware, Inc. All rights reserved. +### Copyright (c) 2008-2025 Broadcom. All Rights Reserved. +### Broadcom Confidential. The term "Broadcom" refers to Broadcom Inc. +### and/or its subsidiaries. ### ### 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 @@ -73,6 +75,7 @@ libvmtools_la_SOURCES += signalSource.c libvmtools_la_SOURCES += vmtools.c libvmtools_la_SOURCES += vmtoolsConfig.c libvmtools_la_SOURCES += vmtoolsLog.c +libvmtools_la_SOURCES += vmtoolsMisc.c libvmtools_la_SOURCES += vmxLogger.c # Recompile the stub for Log_* functions, but not Log() itself (see -DNO_LOG_STUB). @@ -80,7 +83,7 @@ libvmtools_la_SOURCES += $(top_srcdir)/lib/stubs/stub-log.c libvmtools_la_CPPFLAGS = libvmtools_la_CPPFLAGS += -DVMTOOLS_USE_GLIB -libvmtools_la_CPPFLAGS += -DNO_LOG_STUB +libvmtools_la_CPPFLAGS += -DNO_LOG_STUB -DG_LOG_DOMAIN=\"vmtoolslib\" libvmtools_la_CPPFLAGS += -DVMTOOLS_DATA_DIR=\"$(datadir)/open-vm-tools\" libvmtools_la_CPPFLAGS += @GLIB2_CPPFLAGS@ diff --git a/open-vm-tools/libvmtools/i18n.c b/open-vm-tools/libvmtools/i18n.c index bbed57ed4..4caeb16e7 100644 --- a/open-vm-tools/libvmtools/i18n.c +++ b/open-vm-tools/libvmtools/i18n.c @@ -482,8 +482,9 @@ MsgLoadCatalog(const char *path) stream = g_io_channel_new_file(localPath, "r", &err); VMTOOLS_RELEASE_FILENAME_LOCAL(localPath); - if (err != NULL) { - g_debug("Unable to open '%s': %s\n", path, err->message); + if (stream == NULL) { + g_debug("Unable to open '%s': %s\n", path, + err != NULL ? err->message : "No GError"); g_clear_error(&err); return NULL; } @@ -495,21 +496,22 @@ MsgLoadCatalog(const char *path) gboolean eof = FALSE; char *name = NULL; char *value = NULL; - gchar *line; /* Read the next key / value pair. */ for (;;) { gsize i; gsize len; gsize term; + gchar *line = NULL; char *unused = NULL; gboolean cont = FALSE; + GIOStatus status; - g_io_channel_read_line(stream, &line, &len, &term, &err); + status = g_io_channel_read_line(stream, &line, &len, &term, &err); - if (err != NULL) { + if (status == G_IO_STATUS_ERROR) { g_warning("Unable to read a line from '%s': %s\n", - path, err->message); + path, err != NULL ? err->message : "No GError"); g_clear_error(&err); error = TRUE; g_free(line); diff --git a/open-vm-tools/libvmtools/vmtoolsMisc.c b/open-vm-tools/libvmtools/vmtoolsMisc.c new file mode 100644 index 000000000..a9d6020ba --- /dev/null +++ b/open-vm-tools/libvmtools/vmtoolsMisc.c @@ -0,0 +1,657 @@ +/********************************************************* + * Copyright (c) 2012-2025 Broadcom. All Rights Reserved. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * 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 vmtoolsMisc.c + * + * Convenience functions for retrieving the information about the + * Tools Insallation (Eg: library files location). + */ +#if defined(_WIN32) +#include "windowsu.h" +#else +#include "posix.h" +#endif +#include "vmware/tools/utils.h" +#include +#include +#include +#ifdef _WIN32 +#include +#include "w32Messages.h" +#endif +#include "conf.h" +#include "err.h" +#include "util.h" +#include "dictll.h" +#include "guestApp.h" + +#ifndef _WIN32 + +/** + * Reads the /etc/vmware-tools/config file and returns the vlaue of + * 'libdir' property. + * + * @return The value of 'libdir' property. NULL if the 'libdir' property is + * not found in the config file. + */ + +gchar * +VMTools_GetLibdir(void) +{ + gchar *confDirPath = NULL; + gchar *confFilePath = NULL; + gchar *localPath = NULL; + FILE *stream = NULL; + char *libdirValue = NULL; + char *line = NULL; + char *name = NULL; + char *value = NULL; + + confDirPath = GuestApp_GetConfPath(); + + if (confDirPath == NULL) { + Panic("%s: Could not get path to the configuration file.\n", __FUNCTION__); + } + + confFilePath = g_strdup_printf("%s%sconfig", confDirPath, DIRSEPS); + localPath = VMTOOLS_GET_FILENAME_LOCAL(confFilePath, NULL); + if (localPath == NULL) { + g_warning("Error converting path to local encoding."); + goto exit; + } + + stream = g_fopen(localPath, "r"); + if (stream == NULL) { + g_debug("%s: Failed to open file \"%s\": %s\n", + __FUNCTION__, confFilePath, Err_ErrString()); + goto exit; + } + + while (DictLL_ReadLine(stream, &line, &name, &value) + == DictLL_ReadLineSuccess) { + if (strcmp(name, "libdir") == 0) { + libdirValue = Util_SafeStrdup(value); + break; + } + + free(line); + line = NULL; + free(name); + name = NULL; + free(value); + value = NULL; + } + +exit: + + free(line); + line = NULL; + free(name); + name = NULL; + free(value); + value = NULL; + + if (stream != NULL && fclose(stream)) { + g_warning("%s: Unable to close \"%s\": %s\n", + __FUNCTION__, confFilePath, Err_ErrString()); + } + + VMTOOLS_RELEASE_FILENAME_LOCAL(localPath); + g_free(confFilePath); + g_free(confDirPath); + + return libdirValue; +} +#else // _WIN32 +#define POWERSHELL_GPO_REG_KEY \ + "Software\\Policies\\Microsoft\\Windows\\PowerShell" +#define POWERSHELL_GPO_EXEC_POLICY "ExecutionPolicy" +#define VMW_CERT_ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) + + +/* + ****************************************************************************** + * VMTools_IsGPOExecPolicyAllSigned -- + * + * Check the powershell GPO machine execution policy + * + * @return TRUE if powershell GPO MachinePolicy is set to AllSigned. + * FALSE in case of error or if MachinePolicy is not AllSigned. + * + * Side effects: + * None. + * + ****************************************************************************** + */ + +gboolean +VMTools_IsGPOExecPolicyAllSigned(void) +{ + LONG ret; + HKEY hKey; + DWORD regSz; + gchar buf[32]; + /* Set len to one less than sizeof(buf) to null terminate buf */ + DWORD len = sizeof(buf) - 1; + + ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, + POWERSHELL_GPO_REG_KEY, + 0, + KEY_READ, + &hKey); + if (ret != ERROR_SUCCESS) { + g_debug("%s: Registry open failed.\n", __FUNCTION__); + return FALSE; + } + + ret = RegQueryValueExA(hKey, + POWERSHELL_GPO_EXEC_POLICY, + NULL, + ®Sz, + (LPBYTE)buf, + &len); + + RegCloseKey(hKey); + + if (ret != ERROR_SUCCESS || regSz != REG_SZ || len == 0) { + g_debug("%s: Failed to get powershell GPO policy. ret: %d\n", + __FUNCTION__, + ret); + return FALSE; + } + + buf[len] = '\0'; + g_debug("%s: Powershell GPO execution policy: \"%s\"\n", __FUNCTION__, buf); + + return (g_strcmp0(buf, "AllSigned") == 0); +} + + +/* + ****************************************************************************** + * VMTools_IsCertPresent -- + * + * Checks if our certificate is present in TrustedPublisher cert store. + * + * @return + * TRUE if certificate is found. + * FALSE otherwise. + * + * Side effects: + * None. + * + ****************************************************************************** + */ + +gboolean +VMTools_IsCertPresent() +{ + HCERTSTORE trustedPublisher; + static const wchar_t *certStoreW = L"TrustedPublisher"; + static const wchar_t *certSubjectW = L"Broadcom Inc"; + PCCERT_CONTEXT certContext; + + trustedPublisher = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, + 0, + (HCRYPTPROV)NULL, + CERT_SYSTEM_STORE_LOCAL_MACHINE | + CERT_STORE_OPEN_EXISTING_FLAG, + certStoreW); + + if (trustedPublisher == NULL) { + g_debug("%s Failed to open TrustedPublisher store.\n", __FUNCTION__); + return FALSE; + } + + certContext = CertFindCertificateInStore(trustedPublisher, + VMW_CERT_ENCODING, + 0, + CERT_FIND_SUBJECT_STR, + certSubjectW, + NULL); + + if (certContext == NULL) { + g_debug("%s: No certificate found.\n", __FUNCTION__); + CertCloseStore(trustedPublisher, 0); + return FALSE; + } + + CertFreeCertificateContext(certContext); + CertCloseStore(trustedPublisher, 0); + + g_debug("%s: A valid certificate found.\n", __FUNCTION__); + return TRUE; +} + + +/* + ****************************************************************************** + * VMTools_LogWinEvent -- + * + * Logs a windows event for message. + * + * @param[in] msg message to be logged in windows event. + * + * @return + * None. + * + * Side effects: + * None. + * + ****************************************************************************** + */ + +void +VMTools_LogWinEvent(const gchar *msg) +{ + HANDLE h; + wchar_t *msgBufW; + + h = RegisterEventSourceW(NULL, L"VMware Tools"); + if (h == NULL) { + g_debug("%s: Register windows event failed.\n", __FUNCTION__); + return; + } + + msgBufW = (wchar_t *)g_utf8_to_utf16(msg, -1, NULL, NULL, NULL); + if (msgBufW == NULL) { + g_warning("%s: Error converting \"%s\".\n", __FUNCTION__, msg); + goto done; + } + + ReportEventW(h, + EVENTLOG_INFORMATION_TYPE, + 0, // category + VMTOOLS_EVENT_LOG_MESSAGE,// Event ID + NULL, // user + 1, // numStrings + 0, // data size + &msgBufW, // string array + NULL); // any binary data + + g_free(msgBufW); + +done: + DeregisterEventSource(h); +} + + +/** + * Gets error message for the last error. + * + * @param[in] error Error code to be converted to string message. + * + * @return The error message, or NULL in case of failure. + */ + +static char * +VMToolsGetLastErrorMsg(DWORD error) +{ + char *msg = Win32U_FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + error, + 0, // Default language + NULL); + if (msg == NULL) { + g_warning("Failed to get error message for %d, error=%d.\n", + error, GetLastError()); + return NULL; + } + + return msg; +} + +#endif // _WIN32 + + +/** + * Gets an environment variable for the current process. + * + * @param[in] name Name of the env variable. + * + * @return The value of env variable, or NULL in case of error. + * The caller is expected to free the returned value. + */ + +static gchar * +VMToolsEnvGetVar(const char *name) // IN +{ + gchar *value; + +#if defined(_WIN32) + DWORD valueSize; + /* + * Win32U_GetEnvironmentVariable requires buffer to be accurate size. + * So, we need to get the value size first. + * + * Windows bug: GetEnvironmentVariable() does not clear stale + * error when the return value is 0 because of env variable + * holding empty string value (just NUL-char). So, we need to + * clear it before we call the Win32 API. + */ + SetLastError(ERROR_SUCCESS); + valueSize = Win32U_GetEnvironmentVariable(name, NULL, 0); + if (valueSize == 0) { + goto error; + } + + value = g_malloc(valueSize); + SetLastError(ERROR_SUCCESS); + if (Win32U_GetEnvironmentVariable(name, value, valueSize) == 0) { + g_free(value); + goto error; + } + + return value; + +error: +{ + DWORD error = GetLastError(); + if (error == ERROR_SUCCESS) { + g_message("Env variable %s is empty.\n", name); + } else if (error == ERROR_ENVVAR_NOT_FOUND) { + g_message("Env variable %s not found.\n", name); + } else { + char *errorMsg = VMToolsGetLastErrorMsg(error); + if (errorMsg != NULL) { + g_warning("Failed to get env variable size %s, error=%s.\n", + name, errorMsg); + free(errorMsg); + } else { + g_warning("Failed to get env variable size %s, error=%d.\n", + name, error); + } + } + return NULL; +} +#else + value = Posix_Getenv(name); + return value == NULL ? value : g_strdup(value); +#endif +} + + +/** + * Sets an environment variable for the current process. + * + * @param[in] name Name of the env variable. + * @param[in] value Value for the env variable. + * + * @return gboolean, TRUE on success or FALSE in case of error. + */ + +static gboolean +VMToolsEnvSetVar(const char *name, // IN + const char *value) // IN +{ +#if defined(_WIN32) + if (!Win32U_SetEnvironmentVariable(name, value)) { + char *errorMsg; + DWORD error = GetLastError(); + + errorMsg = VMToolsGetLastErrorMsg(error); + if (errorMsg != NULL) { + g_warning("Failed to set env variable %s=%s, error=%s.\n", + name, value, errorMsg); + free(errorMsg); + } else { + g_warning("Failed to set env variable %s=%s, error=%d.\n", + name, value, error); + } + return FALSE; + } +#else + if (Posix_Setenv(name, value, TRUE) != 0) { + g_warning("Failed to set env variable %s=%s, error=%s.\n", + name, value, strerror(errno)); + return FALSE; + } +#endif + return TRUE; +} + +/** + * Unsets an environment variable for the current process. + * + * @param[in] name Name of the env variable. + * + * @return gboolean, TRUE on success or FALSE in case of error. + */ + +static gboolean +VMToolsEnvUnsetVar(const char *name) // IN +{ +#if defined(_WIN32) + if (!Win32U_SetEnvironmentVariable(name, NULL)) { + char *errorMsg; + DWORD error = GetLastError(); + + errorMsg = VMToolsGetLastErrorMsg(error); + if (errorMsg != NULL) { + g_warning("Failed to unset env variable %s, error=%s.\n", + name, errorMsg); + free(errorMsg); + } else { + g_warning("Failed to unset env variable %s, error=%d.\n", + name, error); + } + return FALSE; + } +#else + if (Posix_Unsetenv(name) != 0) { + g_warning("Failed to unset env variable %s, error=%s.\n", + name, strerror(errno)); + return FALSE; + } +#endif + return TRUE; +} + + +/** + * Setup environment variables for the current process from + * a given config group. + * + * @param[in] appName app name. + * @param[in] config config dictionary. + * @param[in] globalVars global variables. + * @param[in] group Configuration group to be read. + * @param[in] doUnset Whether to unset the environment vars. + */ + +static void +VMToolsInitEnvGroup(const gchar *appName,// IN + GKeyFile *config, // IN + gboolean globalVars, // IN + const gchar *group, // IN + gboolean doUnset) // IN +{ + gsize i; + gsize length; + GError *err = NULL; + gchar **keys = g_key_file_get_keys(config, group, &length, &err); + if (keys == NULL) { + if (err != NULL) { + if (err->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) { + g_warning("Failed to get keys for config group %s (err=%d).\n", + group, err->code); + } + g_clear_error(&err); + } else { + g_warning("Failed to get keys for config group %s (err=unknown).\n", + group); + } + g_info("Skipping environment initialization for %s from %s config.\n", + appName, group); + return; + } + + g_info("Found %"FMTSZ"d environment variable(s) in %s config.\n", + length, group); + + /* + * Following 2 formats are supported: + * 1. = + * 2. . = + * + * Variables specified in format #1 are applied to all services and + * variables specified in format #2 are applied to specified service only. + * + * When the globalVars is TRUE, only format #1 settings will be applied. + * All service-specific, format #2, settings will be skipped. + * When the globalVars is FALSE, only format #2 settings will be applied. + * All global, format #1, settings will be skipped. + * + * Services and applications requiring both formats to be applied must + * call the VMTools_SetupEnv twice with globalVars TRUE, then FALSE. + * These are processed separately to allow the global variables to be + * set very early on in the process start, to fix GLib, when the service + * name is unknown. + */ + for (i = 0; i < length; i++) { + const gchar *name = NULL; + const gchar *key = keys[i]; + const gchar *delim; + + /* + * Pick the keys that have service name prefix or no prefix. + */ + delim = strchr(key, '.'); + if (globalVars) { + if (delim == NULL) { + name = key; + } + } else if (delim != NULL) { + if (strncmp(key, appName, delim - key) == 0) { + name = delim + 1; + } + } + + /* + * Ignore entries with empty env variable names. + */ + if (name != NULL && *name != '\0') { + gchar *oldValue = VMToolsEnvGetVar(name); + if (doUnset) { + /* + * We can't avoid duplicate removals, but removing a non-existing + * environment variable is a no-op anyway. + */ + if (VMToolsEnvUnsetVar(name)) { + g_message("Removed env var %s=[%s]\n", + name, oldValue == NULL ? "(null)" : oldValue); + } + } else { + gchar *value = VMTools_ConfigGetString(config, group, + key, NULL); + if (value != NULL) { + /* + * Get rid of trailing space. + */ + g_strchomp(value); + + /* + * Avoid updating environment var if it is already set to + * the same value. + * + * Also, g_key_file_get_keys() does not filter out duplicates + * but, VMTools_ConfigGetString returns only last entry + * for the key. So, by comparing old value, we avoid setting + * the environment multiple times when there are duplicates. + * + * NOTE: Need to use g_strcmp0 because oldValue can be NULL. + * As value can't be NULL but oldValue can be NULL, we might + * still do an unnecessary update in cases like setting a + * variable to empty/no value twice. However, it does not harm + * and is not worth avoiding it. + */ + if (g_strcmp0(oldValue, value) == 0) { + g_info("Env var %s already set to [%s], skipping.\n", + name, oldValue); + g_free(oldValue); + g_free(value); + continue; + } + g_debug("Changing env var %s from [%s] -> [%s]\n", + name, oldValue == NULL ? "(null)" : oldValue, value); + if (VMToolsEnvSetVar(name, value)) { + g_message("Updated env var %s from [%s] -> [%s]\n", + name, oldValue == NULL ? "(null)" : oldValue, + value); + } + g_free(value); + } + } + g_free(oldValue); + } + } + + g_info("Initialized environment for %s from %s config.\n", + appName, group); + g_strfreev(keys); +} + + +/** + * Setup environment variables for the current process. + * + * @param[in] appName app name. + * @param[in] config config dictionary. + * @param[in] globalVars global variables. + */ + +static void +VMToolsInitEnv(const gchar *appName, + GKeyFile *config, + gboolean globalVars) +{ + /* + * First apply unset environment configuration to start clean. + */ + VMToolsInitEnvGroup(appName, + config, + globalVars, + CONFGROUPNAME_UNSET_ENVIRONMENT, + TRUE); + VMToolsInitEnvGroup(appName, + config, + globalVars, + CONFGROUPNAME_SET_ENVIRONMENT, + FALSE); +} + + +/** + * Setup environment variables for the current process. + * + * @param[in] appName app name. + * @param[in] config config dictionary. + * @param[in] globalVars global variables. + */ + +void +VMTools_SetupEnv(const gchar *appName, + GKeyFile *config, + gboolean globalVars) +{ + /* Initialize the environment from config. */ + VMToolsInitEnv(appName, config, globalVars); +} diff --git a/open-vm-tools/services/vmtoolsd/cmdLine.c b/open-vm-tools/services/vmtoolsd/cmdLine.c index a6a2a8028..55dab01a1 100644 --- a/open-vm-tools/services/vmtoolsd/cmdLine.c +++ b/open-vm-tools/services/vmtoolsd/cmdLine.c @@ -367,7 +367,8 @@ ToolsCore_ParseCommandLine(ToolsServiceState *state, ToolsCoreCmdLineError); if (!g_option_context_parse(context, &argc, &argv, &error)) { - g_printerr("%s: %s\n", N_("Command line parsing failed"), error->message); + g_printerr("%s: %s\n", N_("Command line parsing failed"), + error != NULL ? error->message : "No GError"); goto exit; } diff --git a/open-vm-tools/services/vmtoolsd/mainLoop.c b/open-vm-tools/services/vmtoolsd/mainLoop.c index b37607f90..61050c19d 100644 --- a/open-vm-tools/services/vmtoolsd/mainLoop.c +++ b/open-vm-tools/services/vmtoolsd/mainLoop.c @@ -785,33 +785,6 @@ ToolsCore_ReloadConfig(ToolsServiceState *state, #if defined(_WIN32) -/** - * Gets error message for the last error. - * - * @param[in] error Error code to be converted to string message. - * - * @return The error message, or NULL in case of failure. - */ - -static char * -ToolCoreGetLastErrorMsg(DWORD error) -{ - char *msg = Win32U_FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - error, - 0, // Default language - NULL); - if (msg == NULL) { - g_warning("Failed to get error message for %d, error=%d.\n", - error, GetLastError()); - return NULL; - } - - return msg; -} - - /** * Check the version for a file using GetFileVersionInfo method * @@ -906,287 +879,6 @@ exit: #endif -/** - * Gets an environment variable for the current process. - * - * @param[in] name Name of the env variable. - * - * @return The value of env variable, or NULL in case of error. - */ - -static gchar * -ToolsCoreEnvGetVar(const char *name) // IN -{ - gchar *value; - -#if defined(_WIN32) - DWORD valueSize; - /* - * Win32U_GetEnvironmentVariable requires buffer to be accurate size. - * So, we need to get the value size first. - * - * Windows bug: GetEnvironmentVariable() does not clear stale - * error when the return value is 0 because of env variable - * holding empty string value (just NUL-char). So, we need to - * clear it before we call the Win32 API. - */ - SetLastError(ERROR_SUCCESS); - valueSize = Win32U_GetEnvironmentVariable(name, NULL, 0); - if (valueSize == 0) { - goto error; - } - - value = g_malloc(valueSize); - SetLastError(ERROR_SUCCESS); - if (Win32U_GetEnvironmentVariable(name, value, valueSize) == 0) { - g_free(value); - goto error; - } - - return value; - -error: -{ - DWORD error = GetLastError(); - if (error == ERROR_SUCCESS) { - g_message("Env variable %s is empty.\n", name); - } else if (error == ERROR_ENVVAR_NOT_FOUND) { - g_message("Env variable %s not found.\n", name); - } else { - char *errorMsg = ToolCoreGetLastErrorMsg(error); - if (errorMsg != NULL) { - g_warning("Failed to get env variable size %s, error=%s.\n", - name, errorMsg); - free(errorMsg); - } else { - g_warning("Failed to get env variable size %s, error=%d.\n", - name, error); - } - } - return NULL; -} -#else - value = Posix_Getenv(name); - return value == NULL ? value : g_strdup(value); -#endif -} - - -/** - * Sets an environment variable for the current process. - * - * @param[in] name Name of the env variable. - * @param[in] value Value for the env variable. - * - * @return gboolean, TRUE on success or FALSE in case of error. - */ - -static gboolean -ToolsCoreEnvSetVar(const char *name, // IN - const char *value) // IN -{ -#if defined(_WIN32) - if (!Win32U_SetEnvironmentVariable(name, value)) { - char *errorMsg; - DWORD error = GetLastError(); - - errorMsg = ToolCoreGetLastErrorMsg(error); - if (errorMsg != NULL) { - g_warning("Failed to set env variable %s=%s, error=%s.\n", - name, value, errorMsg); - free(errorMsg); - } else { - g_warning("Failed to set env variable %s=%s, error=%d.\n", - name, value, error); - } - return FALSE; - } -#else - if (Posix_Setenv(name, value, TRUE) != 0) { - g_warning("Failed to set env variable %s=%s, error=%s.\n", - name, value, strerror(errno)); - return FALSE; - } -#endif - return TRUE; -} - - -/** - * Unsets an environment variable for the current process. - * - * @param[in] name Name of the env variable. - * - * @return gboolean, TRUE on success or FALSE in case of error. - */ - -static gboolean -ToolsCoreEnvUnsetVar(const char *name) // IN -{ -#if defined(_WIN32) - if (!Win32U_SetEnvironmentVariable(name, NULL)) { - char *errorMsg; - DWORD error = GetLastError(); - - errorMsg = ToolCoreGetLastErrorMsg(error); - if (errorMsg != NULL) { - g_warning("Failed to unset env variable %s, error=%s.\n", - name, errorMsg); - free(errorMsg); - } else { - g_warning("Failed to unset env variable %s, error=%d.\n", - name, error); - } - return FALSE; - } -#else - if (Posix_Unsetenv(name) != 0) { - g_warning("Failed to unset env variable %s, error=%s.\n", - name, strerror(errno)); - return FALSE; - } -#endif - return TRUE; -} - - -/** - * Setup environment variables for the current process from - * a given config group. - * - * @param[in] ctx Application context. - * @param[in] group Configuration group to be read. - * @param[in] doUnset Whether to unset the environment vars. - */ - -static void -ToolsCoreInitEnvGroup(ToolsAppCtx *ctx, // IN - const gchar *group, // IN - gboolean doUnset) // IN -{ - gsize i; - gsize length; - GError *err = NULL; - gchar **keys = g_key_file_get_keys(ctx->config, group, &length, &err); - if (err != NULL) { - if (err->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) { - g_warning("Failed to get keys for config group %s (err=%d).\n", - group, err->code); - } - g_clear_error(&err); - g_info("Skipping environment initialization for %s from %s config.\n", - ctx->name, group); - return; - } - - g_info("Found %"FMTSZ"d environment variable(s) in %s config.\n", - length, group); - - /* - * Following 2 formats are supported: - * 1. = - * 2. . = - * - * Variables specified in format #1 are applied to all services and - * variables specified in format #2 are applied to specified service only. - */ - for (i = 0; i < length; i++) { - const gchar *name = NULL; - const gchar *key = keys[i]; - const gchar *delim; - - /* - * Pick the keys that have service name prefix or no prefix. - */ - delim = strchr(key, '.'); - if (delim == NULL) { - name = key; - } else if (strncmp(key, ctx->name, delim - key) == 0) { - name = delim + 1; - } - - /* - * Ignore entries with empty env variable names. - */ - if (name != NULL && *name != '\0') { - gchar *oldValue = ToolsCoreEnvGetVar(name); - if (doUnset) { - /* - * We can't avoid duplicate removals, but removing a non-existing - * environment variable is a no-op anyway. - */ - if (ToolsCoreEnvUnsetVar(name)) { - g_message("Removed env var %s=[%s]\n", - name, oldValue == NULL ? "(null)" : oldValue); - } - } else { - gchar *value = VMTools_ConfigGetString(ctx->config, group, - key, NULL); - if (value != NULL) { - /* - * Get rid of trailing space. - */ - g_strchomp(value); - - /* - * Avoid updating environment var if it is already set to - * the same value. - * - * Also, g_key_file_get_keys() does not filter out duplicates - * but, VMTools_ConfigGetString returns only last entry - * for the key. So, by comparing old value, we avoid setting - * the environment multiple times when there are duplicates. - * - * NOTE: Need to use g_strcmp0 because oldValue can be NULL. - * As value can't be NULL but oldValue can be NULL, we might - * still do an unnecessary update in cases like setting a - * variable to empty/no value twice. However, it does not harm - * and is not worth avoiding it. - */ - if (g_strcmp0(oldValue, value) == 0) { - g_info("Env var %s already set to [%s], skipping.\n", - name, oldValue); - g_free(oldValue); - g_free(value); - continue; - } - g_debug("Changing env var %s from [%s] -> [%s]\n", - name, oldValue == NULL ? "(null)" : oldValue, value); - if (ToolsCoreEnvSetVar(name, value)) { - g_message("Updated env var %s from [%s] -> [%s]\n", - name, oldValue == NULL ? "(null)" : oldValue, - value); - } - g_free(value); - } - } - g_free(oldValue); - } - } - - g_info("Initialized environment for %s from %s config.\n", - ctx->name, group); - g_strfreev(keys); -} - - -/** - * Setup environment variables for the current process. - * - * @param[in] ctx Application context. - */ - -static void -ToolsCoreInitEnv(ToolsAppCtx *ctx) -{ - /* - * First apply unset environment configuration to start clean. - */ - ToolsCoreInitEnvGroup(ctx, CONFGROUPNAME_UNSET_ENVIRONMENT, TRUE); - ToolsCoreInitEnvGroup(ctx, CONFGROUPNAME_SET_ENVIRONMENT, FALSE); -} - - /** * Performs any initial setup steps for the service's main loop. * @@ -1230,7 +922,7 @@ ToolsCore_Setup(ToolsServiceState *state) g_object_set(state->ctx.serviceObj, TOOLS_CORE_PROP_CTX, &state->ctx, NULL); /* Initialize the environment from config. */ - ToolsCoreInitEnv(&state->ctx); + VMTools_SetupEnv(state->ctx.name, state->ctx.config, FALSE); ToolsCorePool_Init(&state->ctx); /* Initializes the debug library if needed. */ diff --git a/open-vm-tools/services/vmtoolsd/mainPosix.c b/open-vm-tools/services/vmtoolsd/mainPosix.c index be31206c3..72790ffe8 100644 --- a/open-vm-tools/services/vmtoolsd/mainPosix.c +++ b/open-vm-tools/services/vmtoolsd/mainPosix.c @@ -1,5 +1,6 @@ /********************************************************* - * Copyright (c) 2008-2020,2022-2023 VMware, Inc. All rights reserved. + * Copyright (c) 2008-2025 Broadcom. All Rights Reserved. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * * 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 @@ -253,8 +254,16 @@ main(int argc, i = atexit(VMTools_TeardownVmxGuestLog); ASSERT(i == 0); VMTools_UseVmxGuestLog(VMTOOLS_APP_NAME); - VMTools_ConfigLogging(G_LOG_DOMAIN, NULL, TRUE, FALSE); - VMTools_SetupVmxGuestLog(FALSE, NULL, NULL); + + /* + * ConfigLogging will be reset due to the config file being loaded. + * Set to vmtoolsd, temporarily, then clear. + * This is performed early to update env settings from the config file. + */ + gState.name = G_LOG_DOMAIN; + ToolsCore_ReloadConfig(&gState, TRUE); + VMTools_SetupEnv(gState.name, gState.ctx.config, TRUE); + gState.name = NULL; VMTools_BindTextDomain(VMW_TEXT_DOMAIN, NULL, NULL); diff --git a/open-vm-tools/toolbox/toolbox-cmd.c b/open-vm-tools/toolbox/toolbox-cmd.c index 57ff78dd9..e251f6fff 100644 --- a/open-vm-tools/toolbox/toolbox-cmd.c +++ b/open-vm-tools/toolbox/toolbox-cmd.c @@ -479,6 +479,8 @@ main(int argc, // IN: length of command line arguments TOOLBOXCMD_LOAD_GLOBALCONFIG(conf) VMTools_ConfigLogging("toolboxcmd", conf, FALSE, FALSE); + /* Initialize any global environment variables that might be required. */ + VMTools_SetupEnv("toolboxcmd", conf, TRUE); VMTools_BindTextDomain(VMW_TEXT_DOMAIN, NULL, NULL); if (!VmCheck_IsVirtualWorld()) { diff --git a/open-vm-tools/vgauth/cli/Makefile.am b/open-vm-tools/vgauth/cli/Makefile.am index 69bb10047..09d2481d8 100644 --- a/open-vm-tools/vgauth/cli/Makefile.am +++ b/open-vm-tools/vgauth/cli/Makefile.am @@ -1,5 +1,7 @@ ################################################################################ -### Copyright (C) 2014-2016, 2020 VMware, Inc. All rights reserved. +### Copyright (c) 2014-2025 Broadcom. All Rights Reserved. +### Broadcom Confidential. The term "Broadcom" refers to Broadcom Inc. +### and/or its subsidiaries. ### ### 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 @@ -32,6 +34,7 @@ vmware_vgauth_cmd_LDADD += @VMTOOLS_LIBS@ vmware_vgauth_cmd_LDADD += @GLIB2_LIBS@ vmware_vgauth_cmd_LDADD += @GTHREAD_LIBS@ vmware_vgauth_cmd_LDADD += @SSL_LIBS@ +vmware_vgauth_cmd_LDADD += @VMTOOLS_LIBS@ vmware_vgauth_cmd_LDADD += ../lib/libvgauth.la vmware_vgauth_cmd_LDADD += -lssl vmware_vgauth_cmd_LDADD += -lcrypto diff --git a/open-vm-tools/vgauth/cli/main.c b/open-vm-tools/vgauth/cli/main.c index e2b7ef061..411ffc9b8 100644 --- a/open-vm-tools/vgauth/cli/main.c +++ b/open-vm-tools/vgauth/cli/main.c @@ -1,5 +1,6 @@ /********************************************************* - * Copyright (c) 2011-2020, 2023 VMware, Inc. All rights reserved. + * Copyright (c) 2011-2025 Broadcom. All Rights Reserved. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * * 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 @@ -749,6 +750,13 @@ mainRun(int argc, * Set up the option parser */ g_set_prgname(appName); + + err = VGAuth_Init(appName, 0, NULL, &ctx); + if (VGAUTH_E_OK != err) { + g_printerr("%s\n", SU_(vgauth.init.failed, "Failed to init VGAuth")); + exit(-1); + } + noteMsg = g_strdup_printf(lNote, "removeAll"); context = g_option_context_new(paramStr); summaryMsg = g_strdup_printf( @@ -872,6 +880,8 @@ next: SU_(cmdline.parse, "Command line parsing failed"), gErr->message); g_error_free(gErr); + VGAuth_Shutdown(ctx); + g_free(appName); exit(-1); } #endif @@ -883,12 +893,6 @@ next: Usage(context, paramStr, cmdOptions); } - err = VGAuth_Init(appName, 0, NULL, &ctx); - if (VGAUTH_E_OK != err) { - g_printerr("%s\n", SU_(vgauth.init.failed, "Failed to init VGAuth")); - exit(-1); - } - /* * XXX * If username is unset, should it use the current user? diff --git a/open-vm-tools/vgauth/common/i18n.c b/open-vm-tools/vgauth/common/i18n.c index 59ee3858c..4f742e481 100644 --- a/open-vm-tools/vgauth/common/i18n.c +++ b/open-vm-tools/vgauth/common/i18n.c @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (c) 2010-2025 Broadcom. All Rights Reserved. + * Copyright (c) 2011-2025 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * * This program is free software; you can redistribute it and/or modify it @@ -571,8 +571,9 @@ MsgLoadCatalog(const char *path) g_debug("%s: loading message catalog '%s'\n", __FUNCTION__, localPath); RELEASE_FILENAME_LOCAL(localPath); - if (err != NULL) { - g_debug("Unable to open '%s': %s\n", path, err->message); + if (stream == NULL) { + g_debug("Unable to open '%s': %s\n", path, + err != NULL ? err->message : "No GError"); g_clear_error(&err); return NULL; } @@ -584,18 +585,19 @@ MsgLoadCatalog(const char *path) for (;;) { char *name = NULL; char *value = NULL; - gchar *line; + gchar *line = NULL; gsize len; gsize term; char *unused = NULL; + GIOStatus status; /* Read the next key / value pair. */ - g_io_channel_read_line(stream, &line, &len, &term, &err); + status = g_io_channel_read_line(stream, &line, &len, &term, &err); - if (err != NULL) { + if (status == G_IO_STATUS_ERROR) { g_warning("Unable to read a line from '%s': %s\n", - path, err->message); + path, err != NULL ? err->message : "No GError"); g_clear_error(&err); error = TRUE; g_free(line); @@ -627,13 +629,13 @@ MsgLoadCatalog(const char *path) } g_free(unused); g_free(line); + line = NULL; if (error) { /* * If the local DictLL_UnmarshalLine() returns NULL, name and value * will remain NULL pointers. No malloc'ed memory to free here. */ - /* coverity[leaked_storage] */ break; } diff --git a/open-vm-tools/vgauth/common/prefs.c b/open-vm-tools/vgauth/common/prefs.c index 686aace3b..4fa1012f0 100644 --- a/open-vm-tools/vgauth/common/prefs.c +++ b/open-vm-tools/vgauth/common/prefs.c @@ -1,5 +1,6 @@ /********************************************************* - * Copyright (C) 2011-2016,2020 VMware, Inc. All rights reserved. + * Copyright (c) 2011-2025 Broadcom. All Rights Reserved. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * * 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 @@ -28,7 +29,7 @@ #ifdef _WIN32 #include "winUtil.h" #endif - +#include "vmware/tools/utils.h" /* ****************************************************************************** @@ -234,13 +235,35 @@ Pref_GetBool(PrefHandle ph, } +/* + ****************************************************************************** + * Pref_InitEnv -- */ /** + * + * Intializes the environment variables from any preferences. + * + * @param[in] ph The handle to the preferences. + * @param[in] appfName The app name. + * @param[in] globalVars Update global environment variables. + * + ****************************************************************************** + */ + +void +Pref_InitEnv(PrefHandle ph, + const gchar *appName, + gboolean globalVars) +{ + GKeyFile *keyFile = ph->keyFile; + VMTools_SetupEnv(appName, keyFile, globalVars); +} + /* ****************************************************************************** * Pref_LogAllEntries -- */ /** * * Logs the full contents of the prefs. Useful for debugging. * - * @param[in] ph The PrefHandle to dunp. + * @param[in] ph The PrefHandle to dump. * ****************************************************************************** */ diff --git a/open-vm-tools/vgauth/common/prefs.h b/open-vm-tools/vgauth/common/prefs.h index 718053257..3ca8a2268 100644 --- a/open-vm-tools/vgauth/common/prefs.h +++ b/open-vm-tools/vgauth/common/prefs.h @@ -51,6 +51,9 @@ gboolean Pref_GetBool(PrefHandle ph, const gchar *prefName, const gchar *groupName, gboolean defaultVal); +void Pref_InitEnv(PrefHandle ph, + const gchar *appfName, + gboolean globalVars); void Pref_LogAllEntries(const PrefHandle ph); diff --git a/open-vm-tools/vgauth/lib/common.c b/open-vm-tools/vgauth/lib/common.c index 1db3a4f70..f76a8a1b7 100644 --- a/open-vm-tools/vgauth/lib/common.c +++ b/open-vm-tools/vgauth/lib/common.c @@ -308,6 +308,7 @@ VGAuth_Init(const char *applicationName, gchar *msgCatalog; gPrefs = Pref_Init(VGAUTH_PREF_CONFIG_FILENAME); + Pref_InitEnv(gPrefs, applicationName, TRUE); logSuccessAudits = Pref_GetBool(gPrefs, VGAUTH_PREF_AUDIT_SUCCESS, VGAUTH_PREF_GROUP_NAME_AUDIT, diff --git a/open-vm-tools/vgauth/service/Makefile.am b/open-vm-tools/vgauth/service/Makefile.am index 9f46a9a93..7e8949fb0 100644 --- a/open-vm-tools/vgauth/service/Makefile.am +++ b/open-vm-tools/vgauth/service/Makefile.am @@ -1,5 +1,7 @@ ################################################################################ -### Copyright (C) 2014-2020, 2022, 2023 VMware, Inc. All rights reserved. +### Copyright (c) 2014-2025 Broadcom. All Rights Reserved. +### Broadcom Confidential. The term "Broadcom" refers to Broadcom Inc. +### and/or its subsidiaries. ### ### 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 @@ -71,6 +73,7 @@ VGAuthService_LDADD += @GLIB2_LIBS@ VGAuthService_LDADD += @GTHREAD_LIBS@ VGAuthService_LDADD += @XMLSEC1_LIBS@ VGAuthService_LDADD += @SSL_LIBS@ +VGAuthService_LDADD += @VMTOOLS_LIBS@ VGAuthService_LDADD += -lssl VGAuthService_LDADD += -lcrypto diff --git a/open-vm-tools/vgauth/service/main.c b/open-vm-tools/vgauth/service/main.c index f157cf40c..adc247239 100644 --- a/open-vm-tools/vgauth/service/main.c +++ b/open-vm-tools/vgauth/service/main.c @@ -1,5 +1,6 @@ /********************************************************* - * Copyright (c) 2011-2021, 2023 VMware, Inc. All rights reserved. + * Copyright (c) 2011-2025 Broadcom. All Rights Reserved. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * * 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 @@ -538,6 +539,7 @@ main(int argc, #endif gPrefs = Pref_Init(VGAUTH_PREF_CONFIG_FILENAME); + Pref_InitEnv(gPrefs, VGAUTH_SERVICE_NAME, TRUE); /* * Determine where the service is running from, so resources can diff --git a/open-vm-tools/vgauthImport/vgauthImport.c b/open-vm-tools/vgauthImport/vgauthImport.c index 3b2d14629..41aaff57d 100644 --- a/open-vm-tools/vgauthImport/vgauthImport.c +++ b/open-vm-tools/vgauthImport/vgauthImport.c @@ -1,5 +1,6 @@ /********************************************************* - * Copyright (c) 2012,2018-2021, 2023 VMware, Inc. All rights reserved. + * Copyright (c) 2012-2025 Broadcom. All Rights Reserved. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * * 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 @@ -45,7 +46,8 @@ #define NOT_VMWARE_ERROR "Failed sending message to VMware.\n" -static Bool ProcessNamespace(char *namespace); +static Bool ProcessNamespace(char *namespace, + VGAuthContext *ctx); #define NSDB_PRIV_GET_VALUES_CMD "namespace-priv-get-values" #define NSDB_PRIV_SET_KEYS_CMD "namespace-priv-set-keys" @@ -154,8 +156,9 @@ vgauthLog(const char *logDomain, * Parses the data coming back from the namespace-get-values call, and * updates the VGAuth alias store with the information. * + * @param[in] ctx The VGAuthContext. * @param[in] result The raw data. UTF-8 bytestream with embedded NULs. - * @param[in] ersultLen The length of the data. + * @param[in] resultLen The length of the data. * * @return TRUE if successful, FALSE otherwise. * @@ -163,7 +166,8 @@ vgauthLog(const char *logDomain, */ static Bool -ProcessQueryReply(char *result, +ProcessQueryReply(VGAuthContext *ctx, + char *result, size_t resultLen) { char *p = result; @@ -173,7 +177,6 @@ ProcessQueryReply(char *result, Bool addMapped = FALSE; Bool status = TRUE; VGAuthError vgErr; - VGAuthContext *ctx = NULL; int i; int numMapped = 0; VGAuthMappedAlias *maList = NULL; @@ -231,17 +234,6 @@ ProcessQueryReply(char *result, */ g_strstrip(pemCert); - /* - * Now tell VGAuth. - */ - VGAuth_SetLogHandler(vgauthLog, NULL, 0, NULL); - vgErr = VGAuth_Init(VGAUTHIMPORT_APP_NAME, 0, NULL, &ctx); - if (VGAUTH_FAILED(vgErr)) { - fprintf(stderr, "Failed to init VGAuth context\n"); - status = FALSE; - goto done; - } - /* * Make sure we don't already have the entry. * @@ -303,9 +295,6 @@ ProcessQueryReply(char *result, } done: - if (NULL != ctx) { - VGAuth_Shutdown(ctx); - } for (i = 0; i < ARRAYSIZE(nameVals); i++) { free(nameVals[i].val); } @@ -452,6 +441,7 @@ done: * with a timestamp noting when the data was processed. * * @param[in] namespace The namespace name. + * @param[in] ctx The VGAuthContext. * * @return TRUE if successful, FALSE otherwise. * @@ -459,7 +449,8 @@ done: */ static Bool -ProcessNamespace(char *namespace) +ProcessNamespace(char *namespace, + VGAuthContext *ctx) { char *result = NULL; size_t resultLen = 0; @@ -479,7 +470,7 @@ ProcessNamespace(char *namespace) if (resultLen == 0) { fprintf(stderr, "Error: No keys found in namespace '%s'\n", namespace); } else { - status = ProcessQueryReply(result, resultLen); + status = ProcessQueryReply(ctx, result, resultLen); if (status ) { status = SetImportTime(namespace); @@ -522,6 +513,8 @@ main(int argc, char *argv[]) { NULL }, }; GOptionContext *optCtx; + VGAuthContext *ctx = NULL; + VGAuthError vgErr; /* * Using WinUtil_EnableSafePathSearching() and WinUtil_VerifyExePathW() from @@ -545,6 +538,17 @@ main(int argc, char *argv[]) gAppName = g_path_get_basename(argv[0]); g_set_prgname(gAppName); + + /* + * Initialize VGAuth and set up the environment. + */ + VGAuth_SetLogHandler(vgauthLog, NULL, 0, NULL); + vgErr = VGAuth_Init(VGAUTHIMPORT_APP_NAME, 0, NULL, &ctx); + if (VGAUTH_FAILED(vgErr)) { + fprintf(stderr, "Failed to init VGAuth context\n"); + goto out; + } + optCtx = g_option_context_new(usageMsg); g_option_context_set_summary(optCtx, summaryMsg); g_option_context_add_main_entries(optCtx, options, NULL); @@ -569,9 +573,12 @@ main(int argc, char *argv[]) goto out; } - success = ProcessNamespace(argv[1]) ? 0 : 1; + success = ProcessNamespace(argv[1], ctx) ? 0 : 1; out: + if (NULL != ctx) { + VGAuth_Shutdown(ctx); + } g_option_context_free(optCtx); g_free(gAppName);