Mercurial > trustbridge
view common/util.c @ 910:eaed02defe6a
More SSLConnection refactoring. Fixes curl downloader.
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Mon, 18 Aug 2014 18:51:33 +0200 |
parents | 698b6a9bd75e |
children | 427e2e18b8c8 |
line wrap: on
line source
/* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik * Software engineering by Intevation GmbH * * This file is Free Software under the GNU GPL (v>=2) * and comes with ABSOLUTELY NO WARRANTY! * See LICENSE.txt for details. */ #include "util.h" #include "logging.h" #include "strhelp.h" #ifndef _WIN32 #include <unistd.h> #include <sys/types.h> #include <pwd.h> #include <grp.h> #include <string.h> #else #include <windows.h> #endif #ifndef APPNAME #define APPNAME "TrustBridge" #endif #ifdef WIN32 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; char *dirsep = NULL; if (!GetModuleFileNameW (NULL, wPath, MAX_PATH - 1)) { PRINTLASTERROR ("Failed to obtain module file name. Path too long?"); return NULL; } /* wPath might not be 0 terminated */ wPath[MAX_PATH - 1] = '\0'; utf8path = wchar_to_utf8 (wPath, wcsnlen(wPath, MAX_PATH)); if (utf8path == NULL) { ERRORPRINTF ("Failed to convert module path to utf-8"); return NULL; } /* Cut away the executable name */ dirsep = strrchr(utf8path, '\\'); if (dirsep == NULL) { ERRORPRINTF ("Failed to find directory seperator."); return NULL; } *dirsep = '\0'; return utf8path; } static PSID copy_sid(PSID from) { if (!from) { return 0; } DWORD sidLength = GetLengthSid(from); PSID to = (PSID) xmalloc(sidLength); CopySid(sidLength, to, from); return to; } PSID get_process_owner(HANDLE hProcess) { HANDLE hToken = NULL; PSID sid; if (hProcess == NULL) { ERRORPRINTF ("invalid call to get_process_owner"); return NULL; } OpenProcessToken(hProcess, TOKEN_READ, &hToken); if (hToken) { DWORD size = 0; PTOKEN_USER userStruct; // check how much space is needed GetTokenInformation(hToken, TokenUser, NULL, 0, &size); if (ERROR_INSUFFICIENT_BUFFER == GetLastError()) { userStruct = (PTOKEN_USER) xmalloc (size); GetTokenInformation(hToken, TokenUser, userStruct, size, &size); sid = copy_sid(userStruct->User.Sid); CloseHandle(hToken); xfree (userStruct); return sid; } } 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 is_elevated() { bool ret = false; #ifndef _WIN32 ret = (geteuid() == 0); #else HANDLE hToken = NULL; if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken)) { DWORD elevation; DWORD cbSize = sizeof (DWORD); if (GetTokenInformation (hToken, TokenElevation, &elevation, sizeof (TokenElevation), &cbSize)) { ret = elevation; } } if (hToken) CloseHandle (hToken); #endif return ret; } bool is_admin() { #ifndef _WIN32 struct passwd *current_user = getpwuid (geteuid()); int ngroups = 0, ret = 0, i = 0; gid_t * groups = NULL; if (current_user == NULL) { ERRORPRINTF ("Failed to obtain user information."); return false; } ret = getgrouplist (current_user->pw_name, current_user->pw_gid, NULL, &ngroups); if (ret != -1 || ngroups <= 0) { ERRORPRINTF ("Unknown error in getgrouplist call"); return false; } groups = xmalloc (((unsigned int)ngroups) * sizeof (gid_t)); ret = getgrouplist (current_user->pw_name, current_user->pw_gid, groups, &ngroups); if (ret != ngroups) { ERRORPRINTF ("Group length mismatch."); xfree (groups); return false; } for (i = 0; i < ngroups; i++) { struct group *gr = getgrgid (groups[i]); if (gr == NULL) { ERRORPRINTF ("Error in group enumeration"); xfree (groups); return false; } if (strcmp("sudo", gr->gr_name) == 0) { DEBUGPRINTF ("User is in sudo group \n"); xfree (groups); return true; } } DEBUGPRINTF ("User is not in sudo group"); return false; #else bool retval = false; BOOL in_admin_group = FALSE; HANDLE hToken = NULL; HANDLE hTokenToCheck = NULL; DWORD cbSize = 0; TOKEN_ELEVATION_TYPE elevation; BYTE admin_id[SECURITY_MAX_SID_SIZE]; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE, &hToken)) { PRINTLASTERROR ("Failed to duplicate process token.\n"); return false; } if (!GetTokenInformation(hToken, TokenElevationType, &elevation, sizeof(elevation), &cbSize)) { PRINTLASTERROR ("Failed to get token information.\n"); goto done; } /* If limited check the the linked token instead */ if (TokenElevationTypeLimited == elevation) { if (!GetTokenInformation(hToken, TokenLinkedToken, &hTokenToCheck, sizeof(hTokenToCheck), &cbSize)) { PRINTLASTERROR ("Failed to get the linked token.\n"); goto done; } } if (!hTokenToCheck) /* The linked token is already of the correct type */ { if (!DuplicateToken(hToken, SecurityIdentification, &hTokenToCheck)) { PRINTLASTERROR ("Failed to duplicate token for identification.\n"); goto done; } } /* Do the sid dance for the adminSID */ cbSize = sizeof(admin_id); if (!CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, &admin_id, &cbSize)) { PRINTLASTERROR ("Failed to get admin sid.\n"); goto done; } /* The actual check */ if (!CheckTokenMembership(hTokenToCheck, &admin_id, &in_admin_group)) { PRINTLASTERROR ("Failed to check token membership.\n"); goto done; } if (in_admin_group) { /* Winbool to standard bool */ retval = true; } done: if (hToken) CloseHandle(hToken); if (hTokenToCheck) CloseHandle(hTokenToCheck); return retval; #endif }