changeset 841:216a65d7fc4b

(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.
author Andre Heinecke <andre.heinecke@intevation.de>
date Tue, 29 Jul 2014 18:12:57 +0200 (2014-07-29)
parents c9a31544aaab
children bd33c6585cd0
files cinst/nssstore_linux.c cinst/nssstore_win.c common/util.c common/util.h ui/installwrapper.cpp ui/mainwindow.cpp
diffstat 6 files changed, 354 insertions(+), 167 deletions(-) [+]
line wrap: on
line diff
--- 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");
--- 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
--- 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 <windows.h>
 #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());
--- 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
--- 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";
--- 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);
     }

http://wald.intevation.org/projects/trustbridge/