# HG changeset patch # User Andre Heinecke # Date 1406650377 -7200 # Node ID 216a65d7fc4b8c44bf78cd208234998a16a7cd4f # Parent c9a31544aaab78d7374a3e6179757322480d2e33 (issue66) Implement is_system_install and use it This has completly different implementations for linux and Windows. The commit also moves some code into util.c for better reuse. diff -r c9a31544aaab -r 216a65d7fc4b cinst/nssstore_linux.c --- a/cinst/nssstore_linux.c Tue Jul 29 13:15:32 2014 +0200 +++ b/cinst/nssstore_linux.c Tue Jul 29 18:12:57 2014 +0200 @@ -25,52 +25,10 @@ #include "nssstore.h" #include "logging.h" #include "strhelp.h" +#include "util.h" #define NSS_PROCESS_NAME "mozilla" -/**@brief get the current path of the executable - * - * Looks up the current executables directory. The caller - * has to free the return value. - * - * The returned value includes the last / - * - * @returns the absolute directory of the currently executed executable or NULL - */ -char * -get_exe_dir() -{ - char *retval = NULL, - *p = NULL, - buf[PATH_MAX]; - ssize_t ret; - size_t path_len = 0; - - ret = readlink ("/proc/self/exe", buf, PATH_MAX); - if (ret <= 0) - { - ERRORPRINTF ("readlink failed\n"); - return NULL; - } - - buf[ret] = '\0'; - - /* cut off the filename */ - p = strrchr (buf, '/'); - if (p == NULL) - { - ERRORPRINTF ("No filename found.\n"); - return NULL; - } - *(p + 1) = '\0'; - - path_len = strlen (buf); - retval = xmalloc (path_len + 1); - strncpy (retval, buf, path_len); - retval[path_len] = '\0'; - - return retval; -} /**@brief Start the process to install / remove * @@ -123,7 +81,7 @@ } /* Set up the file name of the installer process */ - inst_dir = get_exe_dir(); + inst_dir = get_install_dir(); if (inst_dir == NULL) { ERRORPRINTF ("Failed to find installation directory.\n"); diff -r c9a31544aaab -r 216a65d7fc4b cinst/nssstore_win.c --- a/cinst/nssstore_win.c Tue Jul 29 13:15:32 2014 +0200 +++ b/cinst/nssstore_win.c Tue Jul 29 18:12:57 2014 +0200 @@ -171,114 +171,6 @@ } return true; } - -/**@brief Read (and expand if necessary) a registry string. - * - * Reads a registry string and calls ExpandEnvironmentString - * if necessary on it. Returns a newly allocated string array - * with the expanded registry value converted to UTF-8 - * - * Caller has to free return value with free. - * - * @param [in] root the root key (e.g. HKEY_LOCAL_MACHINE) - * @param [in] key the key - * @param [in] name the name of the value to read. - * - * @returns the expanded, null terminated utf-8 string of the value. - * or NULL on error. - */ -static char* -read_registry_string (const HKEY root, const wchar_t *key, - const wchar_t *name) -{ - HKEY key_handle = NULL; - DWORD size = 0, - type = 0, - ex_size = 0, - dwRet = 0; - LONG ret = 0; - char *retval = NULL; - wchar_t *buf = NULL, - *ex_buf = NULL; - if (root == NULL || key == NULL || name == NULL) - { - ERRORPRINTF ("Invalid call to read_registry_string"); - return NULL; - } - - ret = RegOpenKeyExW (root, key, 0, KEY_READ, &key_handle); - if (ret != ERROR_SUCCESS) - { - ERRORPRINTF ("Failed to open key."); - return NULL; - } - - /* Get the size */ - ret = RegQueryValueExW (key_handle, name, 0, NULL, NULL, &size); - if (ret != ERROR_MORE_DATA && !(ret == ERROR_SUCCESS && size != 0)) - { - ERRORPRINTF ("Failed to get required registry size."); - return retval; - } - - /* Size is size in bytes not in characters */ - buf = xmalloc (size + sizeof(wchar_t)); - - /* If the stored value is not zero terminated the returned value also - is not zero terminated. That's why we reserve more and ensure it's - initialized. */ - memset (buf, 0, size + sizeof(wchar_t)); - - ret = RegQueryValueExW (key_handle, name, 0, &type, (LPBYTE) buf, &size); - if (ret != ERROR_SUCCESS) - { - ERRORPRINTF ("Failed get registry value."); - return retval; - } - - if (type == REG_SZ || (type == REG_EXPAND_SZ && wcschr (buf, '%') == NULL)) - { - /* Nothing to expand, we are done */ - retval = wchar_to_utf8 (buf, wcslen (buf)); - goto done; - } - - if (type != REG_EXPAND_SZ) - { - ERRORPRINTF ("Unhandled registry type %i", type); - goto done; - } - - /* Expand the registry string */ - ex_size = ExpandEnvironmentStringsW (buf, NULL, 0); - - if (ex_size == 0) - { - PRINTLASTERROR ("Failed to determine expanded environment size."); - goto done; - } - - ex_buf = xmalloc ((ex_size + 1) * sizeof(wchar_t)); - - dwRet = ExpandEnvironmentStringsW (buf, ex_buf, ex_size); - - ex_buf[ex_size] = '\0'; /* Make sure it's a string */ - - if (dwRet == 0 || dwRet != ex_size) - { - PRINTLASTERROR ("Failed to expand environment variables."); - goto done; - } - - retval = wchar_to_utf8 (ex_buf, ex_size); - -done: - xfree (ex_buf); - xfree (buf); - - RegCloseKey (key_handle); - return retval; -} /**@brief Get the path to all users default registry hive * * Enumerates the keys in #PROFILE_LIST and retuns a diff -r c9a31544aaab -r 216a65d7fc4b common/util.c --- a/common/util.c Tue Jul 29 13:15:32 2014 +0200 +++ b/common/util.c Tue Jul 29 18:12:57 2014 +0200 @@ -19,8 +19,181 @@ #include #endif +#ifndef APPNAME +#define APPNAME "TrustBridge" +#endif + #ifdef WIN32 -char * get_install_dir() +char* +read_registry_string (const HKEY root, const wchar_t *key, + const wchar_t *name) +{ + HKEY key_handle = NULL; + DWORD size = 0, + type = 0, + ex_size = 0, + dwRet = 0; + LONG ret = 0; + char *retval = NULL; + wchar_t *buf = NULL, + *ex_buf = NULL; + if (root == NULL || key == NULL || name == NULL) + { + ERRORPRINTF ("Invalid call to read_registry_string"); + return NULL; + } + + ret = RegOpenKeyExW (root, key, 0, KEY_READ, &key_handle); + if (ret != ERROR_SUCCESS) + { + ERRORPRINTF ("Failed to open key."); + return NULL; + } + + /* Get the size */ + ret = RegQueryValueExW (key_handle, name, 0, NULL, NULL, &size); + if (ret != ERROR_MORE_DATA && !(ret == ERROR_SUCCESS && size != 0)) + { + ERRORPRINTF ("Failed to get required registry size."); + return retval; + } + + /* Size is size in bytes not in characters */ + buf = xmalloc (size + sizeof(wchar_t)); + + /* If the stored value is not zero terminated the returned value also + is not zero terminated. That's why we reserve more and ensure it's + initialized. */ + memset (buf, 0, size + sizeof(wchar_t)); + + ret = RegQueryValueExW (key_handle, name, 0, &type, (LPBYTE) buf, &size); + if (ret != ERROR_SUCCESS) + { + ERRORPRINTF ("Failed get registry value."); + return retval; + } + + if (type == REG_SZ || (type == REG_EXPAND_SZ && wcschr (buf, '%') == NULL)) + { + /* Nothing to expand, we are done */ + retval = wchar_to_utf8 (buf, wcslen (buf)); + goto done; + } + + if (type != REG_EXPAND_SZ) + { + ERRORPRINTF ("Unhandled registry type %i", type); + goto done; + } + + /* Expand the registry string */ + ex_size = ExpandEnvironmentStringsW (buf, NULL, 0); + + if (ex_size == 0) + { + PRINTLASTERROR ("Failed to determine expanded environment size."); + goto done; + } + + ex_buf = xmalloc ((ex_size + 1) * sizeof(wchar_t)); + + dwRet = ExpandEnvironmentStringsW (buf, ex_buf, ex_size); + + ex_buf[ex_size] = '\0'; /* Make sure it's a string */ + + if (dwRet == 0 || dwRet != ex_size) + { + PRINTLASTERROR ("Failed to expand environment variables."); + goto done; + } + + retval = wchar_to_utf8 (ex_buf, ex_size); + +done: + xfree (ex_buf); + xfree (buf); + + RegCloseKey (key_handle); + return retval; +} + + +/** @brief Compare two paths for equality based on the filename. + * + * Expand the paths by using GetFullPathName and do a string + * comparison on the result to check for equality. + * + * To be sure if it is really the same file it would be better + * to open the files and compare the serial number but this + * suffices for checks that only impact on the options presented + * to the user (try a system wide installation or not) + * + * If one file does not exist the function returns false. If + * The path is longer then MAX_PATH this function also returns + * false. + * + * @param [in] path1 first path to compare + * @paran [in] path2 first path to compare + * @returns true if the paths are the same. + */ +bool +paths_equal (const char *path1, const char *path2) +{ + bool ret = false; + wchar_t buf1[MAX_PATH], + buf2[MAX_PATH]; + wchar_t *wpath1 = NULL, + *wpath2 = NULL; + DWORD retval = 0; + + if (!path1 || !path2) + { + return false; + } + + wpath1 = utf8_to_wchar(path1, strnlen(path1, MAX_PATH)); + wpath2 = utf8_to_wchar(path2, strnlen(path2, MAX_PATH)); + + if (wpath1 == NULL || wpath2 == NULL) + { + ERRORPRINTF ("Failed to convert paths to wchar."); + goto done; + } + + retval = GetFullPathNameW (wpath1, MAX_PATH, buf1, NULL); + if (retval >= MAX_PATH || retval != wcsnlen (buf1, MAX_PATH)) + { + ERRORPRINTF ("Path1 too long."); + goto done; + } + if (retval == 0) + { + PRINTLASTERROR ("Failed to get Full Path name."); + goto done; + } + + retval = GetFullPathNameW (wpath2, MAX_PATH, buf2, NULL); + if (retval >= MAX_PATH || retval != wcsnlen (buf2, MAX_PATH)) + { + ERRORPRINTF ("Path2 too long."); + goto done; + } + if (retval == 0) + { + PRINTLASTERROR ("Failed to get Full Path name."); + goto done; + } + + ret = wcscmp (buf1, buf2) == 0; +done: + xfree (wpath1); + xfree (wpath2); + + return ret; +} + +char * +get_install_dir() { wchar_t wPath[MAX_PATH]; char *utf8path = NULL; @@ -68,7 +241,6 @@ return to; } - PSID get_process_owner(HANDLE hProcess) { @@ -102,6 +274,132 @@ } return NULL; } + +bool +is_system_install() +{ + char *reg_inst_dir = NULL, + *real_prefix = NULL; + bool ret = false; + + reg_inst_dir = read_registry_string (HKEY_LOCAL_MACHINE, + L"Software\\"APPNAME, L""); + + if (reg_inst_dir == NULL) + { + return false; + } + DEBUGPRINTF ("Registered installation directory: %s\n", reg_inst_dir); + + real_prefix = get_install_dir(); + + if (!real_prefix) + { + DEBUGPRINTF ("Failed to obtain installation prefix."); + xfree (reg_inst_dir); + return false; + } + + ret = paths_equal (real_prefix, reg_inst_dir); + + xfree (real_prefix); + xfree (reg_inst_dir); + DEBUGPRINTF ("Is system install? %s\n", ret ? "true" : "false"); + return ret; +} +#else /* WIN32 */ + +char * +get_install_dir() +{ + char *retval = NULL, + *p = NULL, + buf[MAX_PATH_LINUX]; + ssize_t ret; + size_t path_len = 0; + + ret = readlink ("/proc/self/exe", buf, MAX_PATH_LINUX); + if (ret <= 0) + { + ERRORPRINTF ("readlink failed\n"); + return NULL; + } + + buf[ret] = '\0'; + + /* cut off the filename */ + p = strrchr (buf, '/'); + if (p == NULL) + { + ERRORPRINTF ("No filename found.\n"); + return NULL; + } + *(p + 1) = '\0'; + + path_len = strlen (buf); + retval = xmalloc (path_len + 1); + strncpy (retval, buf, path_len); + retval[path_len] = '\0'; + + return retval; +} + +bool +is_system_install() +{ + FILE *system_config; + int read_lines = 0; + char linebuf[MAX_PATH_LINUX + 7], + * inst_dir = NULL; + bool retval = false; + size_t inst_dir_len = 0; + + system_config = fopen ("/etc/"APPNAME"/"APPNAME"-inst.cfg", "r"); + if (system_config == NULL) + { + DEBUGPRINTF ("No system wide install configuration found.\n"); + return false; + } + inst_dir = get_install_dir (); + + if (inst_dir == NULL) + { + ERRORPRINTF ("Failed to find installation directory.\n"); + fclose(system_config); + return false; + } + + inst_dir_len = strnlen (inst_dir, MAX_PATH_LINUX); + + if (inst_dir_len == 0 || inst_dir_len >= MAX_PATH_LINUX) + { + ERRORPRINTF ("Installation directory invalid.\n"); + fclose(system_config); + return false; + } + + /* Read the first 10 lines and look for PREFIX. if it is not found + we return false. */ + while (read_lines < 10 && fgets (linebuf, MAX_PATH_LINUX + 7, + system_config) != NULL) + { + if (str_starts_with (linebuf, "PREFIX=")) + { + /* The last character is always a linebreak in a valid system_config + file so we can strip it. If this is not true the file is invalid. + linebuf is > 7 atm otherwise prefix= would not have been matched. */ + linebuf[strlen(linebuf) - 1] = '\0'; + retval = str_starts_with (inst_dir, linebuf + 7); + break; + } + read_lines++; + } + + fclose (system_config); + xfree (inst_dir); + DEBUGPRINTF ("Is system install? %s\n", retval ? "true" : "false"); + return retval; +} #endif bool @@ -128,7 +426,8 @@ return ret; } -bool is_admin() +bool +is_admin() { #ifndef _WIN32 struct passwd *current_user = getpwuid (geteuid()); diff -r c9a31544aaab -r 216a65d7fc4b common/util.h --- a/common/util.h Tue Jul 29 13:15:32 2014 +0200 +++ b/common/util.h Tue Jul 29 18:12:57 2014 +0200 @@ -21,6 +21,11 @@ extern "C" { #endif +#ifndef WIN32 +/**@def Some value to use as equivalent as MAX_PATH on windows */ +#define MAX_PATH_LINUX 4000 +#endif + /**@brief Check if the current process is running with elevated privileges. * * Elevates the current process token to check if it is marked as elevated. @@ -29,6 +34,20 @@ * @returns true if the current process is elevated.*/ bool is_elevated(); +/**@brief Check if the Software is installed system wide + * + * On Windows this checks if a registry key under HKLM exists for + * trustbridge and that the installation path mentioned there matches + * the current module path. + * + * On linux this looks for the installation configuration in /etc + * and checks if the current process is inside the installation prefix. + * + * The checked path is limited to MAX_PATH on Windows and @MAX_PATH_LINUX on + * Linux. + */ +bool is_system_install(); + /**@brief Check if the user is in the administrators group. * * The function checks if the account that startet this process @@ -38,6 +57,18 @@ */ bool is_admin(); +/**@brief Get the directory in which the current process resides in + * + * Look up the directory in which the current process is placed. + * If the path is longer then MAX_PATH NULL is returned. + * + * Returns a utf-8 encoded string that has to be freed by the caller + * on linux the path is returned as is including the last /. + * + * @returns The directory of the current process + */ +char * get_install_dir(); + #ifdef WIN32 /**@brief Get a copy of the processes owner sid * @@ -52,16 +83,23 @@ */ PSID get_process_owner(HANDLE hProcess); -/**@brief Get the directory in which the current process resides in - * - * Look up the directory in which the current process is placed. - * If the path is longer then MAX_PATH NULL is returned. +/**@brief Read (and expand if necessary) a registry string. * - * Returns a utf-8 encoded string that has to be freed by the caller + * Reads a registry string and calls ExpandEnvironmentString + * if necessary on it. Returns a newly allocated string array + * with the expanded registry value converted to UTF-8 * - * @returns The directory of the current process + * Caller has to free return value with free. + * + * @param [in] root the root key (e.g. HKEY_LOCAL_MACHINE) + * @param [in] key the key + * @param [in] name the name of the value to read. + * + * @returns the expanded, null terminated utf-8 string of the value. + * or NULL on error. */ -char * get_install_dir(); +char * read_registry_string (const HKEY root, const wchar_t *key, + const wchar_t *name); #endif #ifdef __cplusplus diff -r c9a31544aaab -r 216a65d7fc4b ui/installwrapper.cpp --- a/ui/installwrapper.cpp Tue Jul 29 13:15:32 2014 +0200 +++ b/ui/installwrapper.cpp Tue Jul 29 18:12:57 2014 +0200 @@ -96,7 +96,7 @@ shExecInfo.cbSize = sizeof(SHELLEXECUTEINFOW); shExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; - if (!is_admin()) { + if (!is_admin() || !is_system_install()) { shExecInfo.lpVerb = L"open"; } else { shExecInfo.lpVerb = L"runas"; @@ -156,7 +156,7 @@ parameters << "list=" + mCertListFile << "choices=" + choicesFile.fileName(); bool sudo_started = false; - bool use_sudo = is_admin(); + bool use_sudo = is_admin() && is_system_install(); if (use_sudo) { QStringList sudoPrograms; sudoPrograms << "gksudo" << "kdesudo" << "sudo"; diff -r c9a31544aaab -r 216a65d7fc4b ui/mainwindow.cpp --- a/ui/mainwindow.cpp Tue Jul 29 13:15:32 2014 +0200 +++ b/ui/mainwindow.cpp Tue Jul 29 18:12:57 2014 +0200 @@ -277,7 +277,7 @@ // shExecInfo.fMask = SEE_MASK_NOASYNC; shExecInfo.nShow = SW_SHOWDEFAULT; - if (!is_admin()) { + if (!is_system_install() || !is_admin()) { shExecInfo.lpVerb = L"open"; } else { shExecInfo.lpVerb = L"runas"; @@ -299,7 +299,7 @@ QStringList parameters; parameters << "--prefix" << installDir.path(); bool sudo_started = false; - bool use_sudo = is_admin(); + bool use_sudo = is_admin() && is_system_install(); if (use_sudo) { QStringList sudoPrograms; sudoPrograms << "gksudo" << "kdesudo" << "sudo"; @@ -566,7 +566,7 @@ mInstallButton = new QPushButton(" " + tr("Install certificates again")); mInstallButton->setFixedHeight(30); #ifdef Q_OS_WIN - if (is_admin()) { + if (is_admin() && is_system_install()) { QIcon uacShield = QApplication::style()->standardIcon(QStyle::SP_VistaShield); mInstallButton->setIcon(uacShield); }