-doxygen
[oweals/gnunet.git] / src / util / os_installation.c
index ef3eda2ff21d877ac354229e8e6dd43418c37523..30e729dc63b36360daec7297501bd85aa664346e 100644 (file)
@@ -4,7 +4,7 @@
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 2, or (at your
+     by the Free Software Foundation; either version 3, or (at your
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
 #include <unistr.h> /* for u16_to_u8 */
 
 #include "platform.h"
 #include <unistr.h> /* for u16_to_u8 */
 
 #include "platform.h"
-#include "gnunet_common.h"
-#include "gnunet_configuration_lib.h"
-#include "gnunet_disk_lib.h"
-#include "gnunet_os_lib.h"
-#include "gnunet_strings_lib.h"
+#include "gnunet_util_lib.h"
 #if DARWIN
 #include <mach-o/ldsyms.h>
 #include <mach-o/dyld.h>
 #if DARWIN
 #include <mach-o/ldsyms.h>
 #include <mach-o/dyld.h>
+#elif WINDOWS
+#include <windows.h>
 #endif
 
 #endif
 
+
 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
 
 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
 
 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
 
 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
 
+
 #if LINUX
 /**
  * Try to determine path by reading /proc/PID/exe
 #if LINUX
 /**
  * Try to determine path by reading /proc/PID/exe
@@ -116,12 +116,13 @@ get_path_from_proc_exe ()
 }
 #endif
 
 }
 #endif
 
-#if WINDOWS
 
 
+#if WINDOWS
 static HINSTANCE dll_instance;
 
 
 static HINSTANCE dll_instance;
 
 
-/* GNUNET_util_cl_init() in common_logging.c is preferred.
+/**
+ * GNUNET_util_cl_init() in common_logging.c is preferred.
  * This function is only for thread-local storage (not used in GNUnet)
  * and hInstance saving.
  */
  * This function is only for thread-local storage (not used in GNUnet)
  * and hInstance saving.
  */
@@ -143,6 +144,7 @@ DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
   return TRUE;
 }
 
   return TRUE;
 }
 
+
 /**
  * Try to determine path with win32-specific function
  *
 /**
  * Try to determine path with win32-specific function
  *
@@ -226,6 +228,7 @@ get_path_from_module_filename ()
 }
 #endif
 
 }
 #endif
 
+
 #if DARWIN
 /**
  * Signature of the '_NSGetExecutablePath" function.
 #if DARWIN
 /**
  * Signature of the '_NSGetExecutablePath" function.
@@ -291,7 +294,7 @@ get_path_from_dyld_image ()
   c = _dyld_image_count ();
   for (i = 0; i < c; i++)
   {
   c = _dyld_image_count ();
   for (i = 0; i < c; i++)
   {
-    if (_dyld_get_image_header (i) != &_mh_dylib_header)
+    if (((const void *) _dyld_get_image_header (i)) != (const void *)&_mh_dylib_header)
       continue;
     path = _dyld_get_image_name (i);
     if ( (NULL == path) || (0 == strlen (path)) )
       continue;
     path = _dyld_get_image_name (i);
     if ( (NULL == path) || (0 == strlen (path)) )
@@ -395,6 +398,9 @@ os_get_gnunet_path ()
 #if LINUX
   if (NULL != (ret = get_path_from_proc_maps ()))
     return ret;
 #if LINUX
   if (NULL != (ret = get_path_from_proc_maps ()))
     return ret;
+  /* try path *first*, before /proc/exe, as /proc/exe can be wrong */
+  if (NULL != (ret = get_path_from_PATH ("gnunet-arm")))
+    return ret;
   if (NULL != (ret = get_path_from_proc_exe ()))
     return ret;
 #endif
   if (NULL != (ret = get_path_from_proc_exe ()))
     return ret;
 #endif
@@ -412,8 +418,7 @@ os_get_gnunet_path ()
     return ret;
   /* other attempts here */
   LOG (GNUNET_ERROR_TYPE_ERROR,
     return ret;
   /* other attempts here */
   LOG (GNUNET_ERROR_TYPE_ERROR,
-       _
-       ("Could not determine installation path for %s.  Set `%s' environment variable.\n"),
+       _("Could not determine installation path for %s.  Set `%s' environment variable.\n"),
        "GNUnet", "GNUNET_PREFIX");
   return NULL;
 }
        "GNUnet", "GNUNET_PREFIX");
   return NULL;
 }
@@ -428,7 +433,7 @@ os_get_gnunet_path ()
 static char *
 os_get_exec_path ()
 {
 static char *
 os_get_exec_path ()
 {
-  char *ret;
+  char *ret = NULL;
 
 #if LINUX
   if (NULL != (ret = get_path_from_proc_exe ()))
 
 #if LINUX
   if (NULL != (ret = get_path_from_proc_exe ()))
@@ -443,13 +448,13 @@ os_get_exec_path ()
     return ret;
 #endif
   /* other attempts here */
     return ret;
 #endif
   /* other attempts here */
-  return NULL;
+  return ret;
 }
 
 
 /**
  * @brief get the path to a specific GNUnet installation directory or,
 }
 
 
 /**
  * @brief get the path to a specific GNUnet installation directory or,
- * with GNUNET_IPK_SELF_PREFIX, the current running apps installation directory
+ * with #GNUNET_OS_IPK_SELF_PREFIX, the current running apps installation directory
  * @author Milan
  * @return a pointer to the dir path (to be freed by the caller)
  */
  * @author Milan
  * @return a pointer to the dir path (to be freed by the caller)
  */
@@ -486,26 +491,27 @@ GNUNET_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind)
     execpath[--n] = '\0';
 
   isbasedir = 1;
     execpath[--n] = '\0';
 
   isbasedir = 1;
-  if ((n > 5) &&
-      ((0 == strcasecmp (&execpath[n - 5], "lib32")) ||
-       (0 == strcasecmp (&execpath[n - 5], "lib64"))))
+  if ((n > 6) &&
+      ((0 == strcasecmp (&execpath[n - 6], "/lib32")) ||
+       (0 == strcasecmp (&execpath[n - 6], "/lib64"))))
   {
   {
-    if (GNUNET_OS_IPK_LIBDIR != dirkind)
+    if ( (GNUNET_OS_IPK_LIBDIR != dirkind) &&
+        (GNUNET_OS_IPK_LIBEXECDIR != dirkind) )
     {
       /* strip '/lib32' or '/lib64' */
     {
       /* strip '/lib32' or '/lib64' */
-      execpath[n - 5] = '\0';
-      n -= 5;
+      execpath[n - 6] = '\0';
+      n -= 6;
     }
     else
       isbasedir = 0;
   }
     }
     else
       isbasedir = 0;
   }
-  else if ((n > 3) &&
-           ((0 == strcasecmp (&execpath[n - 3], "bin")) ||
-            (0 == strcasecmp (&execpath[n - 3], "lib"))))
+  else if ((n > 4) &&
+           ((0 == strcasecmp (&execpath[n - 4], "/bin")) ||
+            (0 == strcasecmp (&execpath[n - 4], "/lib"))))
   {
     /* strip '/bin' or '/lib' */
   {
     /* strip '/bin' or '/lib' */
-    execpath[n - 3] = '\0';
-    n -= 3;
+    execpath[n - 4] = '\0';
+    n -= 4;
   }
   /* in case this was a directory named foo-bin, remove "foo-" */
   while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR))
   }
   /* in case this was a directory named foo-bin, remove "foo-" */
   while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR))
@@ -521,8 +527,30 @@ GNUNET_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind)
     break;
   case GNUNET_OS_IPK_LIBDIR:
     if (isbasedir)
     break;
   case GNUNET_OS_IPK_LIBDIR:
     if (isbasedir)
+    {
       dirname =
           DIR_SEPARATOR_STR "lib" DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR;
       dirname =
           DIR_SEPARATOR_STR "lib" DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR;
+      tmp = GNUNET_malloc (strlen (execpath) + strlen (dirname) + 1);
+      sprintf (tmp, "%s%s", execpath, dirname);
+      if ( (GNUNET_YES !=
+           GNUNET_DISK_directory_test (tmp, GNUNET_YES)) &&
+          (4 == sizeof (void *)) )
+      {
+       GNUNET_free (tmp);
+       dirname =
+         DIR_SEPARATOR_STR "lib32" DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR;
+       tmp = GNUNET_malloc (strlen (execpath) + strlen (dirname) + 1);
+       sprintf (tmp, "%s%s", execpath, dirname);
+      }
+      if ( (GNUNET_YES !=
+           GNUNET_DISK_directory_test (tmp, GNUNET_YES)) &&
+          (8 == sizeof (void *)) )
+      {
+       dirname =
+         DIR_SEPARATOR_STR "lib64" DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR;
+      }
+      GNUNET_free (tmp);
+    }
     else
       dirname = DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR;
     break;
     else
       dirname = DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR;
     break;
@@ -544,9 +572,38 @@ GNUNET_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind)
         "gnunet" DIR_SEPARATOR_STR;
     break;
   case GNUNET_OS_IPK_LIBEXECDIR:
         "gnunet" DIR_SEPARATOR_STR;
     break;
   case GNUNET_OS_IPK_LIBEXECDIR:
-    dirname =
+    if (isbasedir)
+    {
+      dirname =
         DIR_SEPARATOR_STR "lib" DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR \
         "libexec" DIR_SEPARATOR_STR;
         DIR_SEPARATOR_STR "lib" DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR \
         "libexec" DIR_SEPARATOR_STR;
+      tmp = GNUNET_malloc (strlen (execpath) + strlen (dirname) + 1);
+      sprintf (tmp, "%s%s", execpath, dirname);
+      if ( (GNUNET_YES !=
+           GNUNET_DISK_directory_test (tmp, GNUNET_YES)) &&
+          (4 == sizeof (void *)) )
+      {
+       GNUNET_free (tmp);
+       dirname =
+         DIR_SEPARATOR_STR "lib32" DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR \
+         "libexec" DIR_SEPARATOR_STR;
+       tmp = GNUNET_malloc (strlen (execpath) + strlen (dirname) + 1);
+       sprintf (tmp, "%s%s", execpath, dirname);
+      }
+      if ( (GNUNET_YES !=
+           GNUNET_DISK_directory_test (tmp, GNUNET_YES)) &&
+          (8 == sizeof (void *)) )
+      {
+       dirname =
+         DIR_SEPARATOR_STR "lib64" DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR \
+         "libexec" DIR_SEPARATOR_STR;
+      }
+      GNUNET_free (tmp);
+    }
+    else
+      dirname =
+        DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR \
+        "libexec" DIR_SEPARATOR_STR;
     break;
   default:
     GNUNET_free (execpath);
     break;
   default:
     GNUNET_free (execpath);
@@ -570,17 +627,24 @@ GNUNET_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind)
 char *
 GNUNET_OS_get_libexec_binary_path (const char *progname)
 {
 char *
 GNUNET_OS_get_libexec_binary_path (const char *progname)
 {
+  static char *cache;
   char *libexecdir;
   char *binary;
 
   char *libexecdir;
   char *binary;
 
-  libexecdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBEXECDIR);
+  if ( (DIR_SEPARATOR == progname[0]) ||
+       (GNUNET_YES == GNUNET_STRINGS_path_is_absolute (progname, GNUNET_NO, NULL, NULL)) )
+    return GNUNET_strdup (progname);
+  if (NULL != cache)
+    libexecdir = cache;
+  else
+    libexecdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBEXECDIR);
   if (NULL == libexecdir)
     return GNUNET_strdup (progname);
   GNUNET_asprintf (&binary,
                   "%s%s",
                   libexecdir,
                   progname);
   if (NULL == libexecdir)
     return GNUNET_strdup (progname);
   GNUNET_asprintf (&binary,
                   "%s%s",
                   libexecdir,
                   progname);
-  GNUNET_free (libexecdir);
+  cache = libexecdir;
   return binary;
 }
 
   return binary;
 }
 
@@ -593,18 +657,22 @@ GNUNET_OS_get_libexec_binary_path (const char *progname)
  *
  * @param binary the name of the file to check.
  *        W32: must not have an .exe suffix.
  *
  * @param binary the name of the file to check.
  *        W32: must not have an .exe suffix.
- * @return GNUNET_YES if the file is SUID,
- *         GNUNET_NO if not SUID (but binary exists)
- *         GNUNET_SYSERR on error (no such binary or not executable)
+ * @param check_suid input true if the binary should be checked for SUID (*nix)
+ *        W32: checks if the program has sufficient privileges by executing this
+ *             binary with the -d flag. -d omits a programs main loop and only
+ *             executes all privileged operations in an binary.
+ * @param params parameters used for w32 privilege checking (can be NULL for != w32 )
+ * @return #GNUNET_YES if the file is SUID (*nix) or can be executed with current privileges (W32),
+ *         #GNUNET_NO if not SUID (but binary exists),
+ *         #GNUNET_SYSERR on error (no such binary or not executable)
  */
 int
  */
 int
-GNUNET_OS_check_helper_binary (const char *binary)
+GNUNET_OS_check_helper_binary (const char *binary, int check_suid, const char *params)
 {
   struct stat statbuf;
   char *p;
   char *pf;
 #ifdef MINGW
 {
   struct stat statbuf;
   char *p;
   char *pf;
 #ifdef MINGW
-  SOCKET rawsock;
   char *binaryexe;
 
   GNUNET_asprintf (&binaryexe, "%s.exe", binary);
   char *binaryexe;
 
   GNUNET_asprintf (&binaryexe, "%s.exe", binary);
@@ -641,7 +709,8 @@ GNUNET_OS_check_helper_binary (const char *binary)
 #endif
   if (NULL == p)
   {
 #endif
   if (NULL == p)
   {
-    LOG (GNUNET_ERROR_TYPE_INFO, _("Could not find binary `%s' in PATH!\n"),
+    LOG (GNUNET_ERROR_TYPE_INFO,
+         _("Could not find binary `%s' in PATH!\n"),
          binary);
     return GNUNET_SYSERR;
   }
          binary);
     return GNUNET_SYSERR;
   }
@@ -665,40 +734,66 @@ GNUNET_OS_check_helper_binary (const char *binary)
     GNUNET_free (p);
     return GNUNET_SYSERR;
   }
     GNUNET_free (p);
     return GNUNET_SYSERR;
   }
+  if (check_suid){
 #ifndef MINGW
 #ifndef MINGW
-  if ((0 != (statbuf.st_mode & S_ISUID)) && (0 == statbuf.st_uid))
-  {
-    GNUNET_free (p);
-    return GNUNET_YES;
-  }
-  /* binary exists, but not SUID */
-  GNUNET_free (p);
-  return GNUNET_NO;
-#else
-  GNUNET_free (p);
-  {
-    static int once; /* remember result from previous runs... */
-
-    if (0 == once)
+    if ((0 != (statbuf.st_mode & S_ISUID)) && (0 == statbuf.st_uid))
     {
     {
-      rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
-      if (INVALID_SOCKET == rawsock)
-       {
-         DWORD err = GetLastError ();
-         
-         LOG (GNUNET_ERROR_TYPE_DEBUG,
-              "socket (AF_INET, SOCK_RAW, IPPROTO_ICMP) failed! GLE = %d\n", err);
-         once = -1;
-         return GNUNET_NO;           /* not running as administrator */
-       }
-      once = 1;
-      closesocket (rawsock);
+      GNUNET_free (p);
+      return GNUNET_YES;
     }
     }
-    if (-1 == once)
-      return GNUNET_NO;
-    return GNUNET_YES;
-  }
+    /* binary exists, but not SUID */
+#else
+    STARTUPINFO start;
+    char parameters[512];
+    PROCESS_INFORMATION proc;
+    DWORD exit_value;
+
+    GNUNET_snprintf (parameters,
+                    sizeof (parameters),
+                    "-d %s", params);
+    memset (&start, 0, sizeof (start));
+    start.cb = sizeof (start);
+    memset (&proc, 0, sizeof (proc));
+
+
+    // Start the child process.
+    if ( ! (CreateProcess( p,   // current windows (2k3 and up can handle / instead of \ in paths))
+        parameters,           // execute dryrun/priviliege checking mode
+        NULL,           // Process handle not inheritable
+        NULL,           // Thread handle not inheritable
+        FALSE,          // Set handle inheritance to FALSE
+        CREATE_DEFAULT_ERROR_MODE, // No creation flags
+        NULL,           // Use parent's environment block
+        NULL,           // Use parent's starting directory
+        &start,            // Pointer to STARTUPINFO structure
+        &proc )           // Pointer to PROCESS_INFORMATION structure
+                               ))
+      {
+        LOG (GNUNET_ERROR_TYPE_ERROR,
+             _("CreateProcess failed for binary %s (%d).\n"),
+             p, GetLastError());
+        return GNUNET_SYSERR;
+    }
+
+    // Wait until child process exits.
+    WaitForSingleObject( proc.hProcess, INFINITE );
+
+    if ( ! GetExitCodeProcess (proc.hProcess, &exit_value)){
+        LOG (GNUNET_ERROR_TYPE_ERROR,
+             _("GetExitCodeProcess failed for binary %s (%d).\n"),
+             p, GetLastError() );
+        return GNUNET_SYSERR;
+      }
+    // Close process and thread handles.
+    CloseHandle( proc.hProcess );
+    CloseHandle( proc.hThread );
+
+    if (!exit_value)
+      return GNUNET_YES;
 #endif
 #endif
+    }
+  GNUNET_free (p);
+  return GNUNET_NO;
 }
 
 
 }