aheinecke@404: /* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik aheinecke@404: * Software engineering by Intevation GmbH aheinecke@404: * aheinecke@404: * This file is Free Software under the GNU GPL (v>=2) aheinecke@404: * and comes with ABSOLUTELY NO WARRANTY! aheinecke@404: * See LICENSE.txt for details. aheinecke@404: */ aheinecke@321: #include "util.h" aheinecke@505: #include "logging.h" andre@644: #include "strhelp.h" aheinecke@505: wilde@323: #ifndef _WIN32 wilde@323: #include wilde@323: #include andre@644: #include andre@644: #include andre@644: #include wilde@323: #else andre@1010: #include aheinecke@321: #include andre@983: #include andre@983: #include andre@983: #include aheinecke@321: #endif aheinecke@321: andre@841: #ifndef APPNAME andre@841: #define APPNAME "TrustBridge" andre@841: #endif andre@841: andre@1348: #ifndef ORGANIZATION andre@1348: #define ORGANIZATION "BSI" andre@1348: #endif andre@1348: andre@671: #ifdef WIN32 andre@841: char* andre@841: read_registry_string (const HKEY root, const wchar_t *key, andre@841: const wchar_t *name) andre@841: { andre@841: HKEY key_handle = NULL; andre@841: DWORD size = 0, andre@841: type = 0, andre@841: ex_size = 0, andre@841: dwRet = 0; andre@841: LONG ret = 0; andre@841: char *retval = NULL; andre@841: wchar_t *buf = NULL, andre@905: *ex_buf = NULL; andre@841: if (root == NULL || key == NULL || name == NULL) andre@841: { andre@841: ERRORPRINTF ("Invalid call to read_registry_string"); andre@841: return NULL; andre@841: } andre@841: andre@841: ret = RegOpenKeyExW (root, key, 0, KEY_READ, &key_handle); andre@841: if (ret != ERROR_SUCCESS) andre@841: { andre@841: ERRORPRINTF ("Failed to open key."); andre@841: return NULL; andre@841: } andre@841: andre@841: /* Get the size */ andre@841: ret = RegQueryValueExW (key_handle, name, 0, NULL, NULL, &size); andre@841: if (ret != ERROR_MORE_DATA && !(ret == ERROR_SUCCESS && size != 0)) andre@841: { andre@841: ERRORPRINTF ("Failed to get required registry size."); andre@841: return retval; andre@841: } andre@841: andre@841: /* Size is size in bytes not in characters */ andre@841: buf = xmalloc (size + sizeof(wchar_t)); andre@841: andre@841: /* If the stored value is not zero terminated the returned value also andre@841: is not zero terminated. That's why we reserve more and ensure it's andre@841: initialized. */ andre@841: memset (buf, 0, size + sizeof(wchar_t)); andre@841: andre@841: ret = RegQueryValueExW (key_handle, name, 0, &type, (LPBYTE) buf, &size); andre@841: if (ret != ERROR_SUCCESS) andre@841: { andre@841: ERRORPRINTF ("Failed get registry value."); andre@841: return retval; andre@841: } andre@841: andre@841: if (type == REG_SZ || (type == REG_EXPAND_SZ && wcschr (buf, '%') == NULL)) andre@841: { andre@841: /* Nothing to expand, we are done */ andre@841: retval = wchar_to_utf8 (buf, wcslen (buf)); andre@841: goto done; andre@841: } andre@841: andre@841: if (type != REG_EXPAND_SZ) andre@841: { andre@841: ERRORPRINTF ("Unhandled registry type %i", type); andre@841: goto done; andre@841: } andre@841: andre@841: /* Expand the registry string */ andre@841: ex_size = ExpandEnvironmentStringsW (buf, NULL, 0); andre@841: andre@841: if (ex_size == 0) andre@841: { andre@841: PRINTLASTERROR ("Failed to determine expanded environment size."); andre@841: goto done; andre@841: } andre@841: andre@841: ex_buf = xmalloc ((ex_size + 1) * sizeof(wchar_t)); andre@841: andre@841: dwRet = ExpandEnvironmentStringsW (buf, ex_buf, ex_size); andre@841: andre@841: ex_buf[ex_size] = '\0'; /* Make sure it's a string */ andre@841: andre@841: if (dwRet == 0 || dwRet != ex_size) andre@841: { andre@841: PRINTLASTERROR ("Failed to expand environment variables."); andre@841: goto done; andre@841: } andre@841: andre@841: retval = wchar_to_utf8 (ex_buf, ex_size); andre@841: andre@841: done: andre@841: xfree (ex_buf); andre@841: xfree (buf); andre@841: andre@841: RegCloseKey (key_handle); andre@841: return retval; andre@841: } andre@841: andre@841: andre@841: /** @brief Compare two paths for equality based on the filename. andre@841: * andre@841: * Expand the paths by using GetFullPathName and do a string andre@841: * comparison on the result to check for equality. andre@841: * andre@841: * To be sure if it is really the same file it would be better andre@841: * to open the files and compare the serial number but this andre@841: * suffices for checks that only impact on the options presented andre@841: * to the user (try a system wide installation or not) andre@841: * andre@841: * If one file does not exist the function returns false. If andre@841: * The path is longer then MAX_PATH this function also returns andre@841: * false. andre@841: * andre@841: * @param [in] path1 first path to compare andre@841: * @paran [in] path2 first path to compare andre@841: * @returns true if the paths are the same. andre@841: */ andre@841: bool andre@841: paths_equal (const char *path1, const char *path2) andre@841: { andre@905: bool ret = false; andre@905: wchar_t buf1[MAX_PATH], andre@905: buf2[MAX_PATH]; andre@905: wchar_t *wpath1 = NULL, andre@905: *wpath2 = NULL; andre@905: DWORD retval = 0; andre@841: andre@905: if (!path1 || !path2) andre@905: { andre@905: return false; andre@905: } andre@841: andre@905: wpath1 = utf8_to_wchar(path1, strnlen(path1, MAX_PATH)); andre@905: wpath2 = utf8_to_wchar(path2, strnlen(path2, MAX_PATH)); andre@841: andre@905: if (wpath1 == NULL || wpath2 == NULL) andre@905: { andre@905: ERRORPRINTF ("Failed to convert paths to wchar."); andre@905: goto done; andre@905: } andre@905: andre@905: retval = GetFullPathNameW (wpath1, MAX_PATH, buf1, NULL); andre@905: if (retval >= MAX_PATH || retval != wcsnlen (buf1, MAX_PATH)) andre@905: { andre@905: ERRORPRINTF ("Path1 too long."); andre@905: goto done; andre@905: } andre@905: if (retval == 0) andre@905: { andre@905: PRINTLASTERROR ("Failed to get Full Path name."); andre@905: goto done; andre@905: } andre@905: andre@905: retval = GetFullPathNameW (wpath2, MAX_PATH, buf2, NULL); andre@905: if (retval >= MAX_PATH || retval != wcsnlen (buf2, MAX_PATH)) andre@905: { andre@905: ERRORPRINTF ("Path2 too long."); andre@905: goto done; andre@905: } andre@905: if (retval == 0) andre@905: { andre@905: PRINTLASTERROR ("Failed to get Full Path name."); andre@905: goto done; andre@905: } andre@905: andre@905: ret = wcscmp (buf1, buf2) == 0; andre@841: done: andre@905: xfree (wpath1); andre@905: xfree (wpath2); andre@841: andre@905: return ret; andre@841: } andre@841: andre@841: char * andre@841: get_install_dir() andre@675: { andre@675: wchar_t wPath[MAX_PATH]; andre@675: char *utf8path = NULL; andre@675: char *dirsep = NULL; andre@675: andre@904: if (!GetModuleFileNameW (NULL, wPath, MAX_PATH - 1)) andre@675: { andre@675: PRINTLASTERROR ("Failed to obtain module file name. Path too long?"); andre@675: return NULL; andre@675: } andre@675: andre@675: /* wPath might not be 0 terminated */ andre@675: wPath[MAX_PATH - 1] = '\0'; andre@675: andre@675: utf8path = wchar_to_utf8 (wPath, wcsnlen(wPath, MAX_PATH)); andre@675: andre@675: if (utf8path == NULL) andre@675: { andre@675: ERRORPRINTF ("Failed to convert module path to utf-8"); andre@675: return NULL; andre@675: } andre@675: andre@675: /* Cut away the executable name */ andre@675: dirsep = strrchr(utf8path, '\\'); andre@675: if (dirsep == NULL) andre@675: { andre@675: ERRORPRINTF ("Failed to find directory seperator."); andre@675: return NULL; andre@675: } andre@675: *dirsep = '\0'; andre@675: return utf8path; andre@675: } andre@675: andre@670: static PSID andre@670: copy_sid(PSID from) andre@670: { andre@670: if (!from) andre@670: { andre@670: return 0; andre@670: } andre@670: andre@670: DWORD sidLength = GetLengthSid(from); andre@670: PSID to = (PSID) xmalloc(sidLength); andre@670: CopySid(sidLength, to, from); andre@670: return to; andre@670: } andre@670: andre@670: PSID andre@670: get_process_owner(HANDLE hProcess) andre@670: { andre@905: HANDLE hToken = NULL; andre@905: PSID sid; andre@670: andre@905: if (hProcess == NULL) andre@905: { andre@905: ERRORPRINTF ("invalid call to get_process_owner"); andre@905: return NULL; andre@905: } andre@670: andre@905: OpenProcessToken(hProcess, TOKEN_READ, &hToken); andre@905: if (hToken) andre@905: { andre@905: DWORD size = 0; andre@905: PTOKEN_USER userStruct; andre@670: andre@905: // check how much space is needed andre@905: GetTokenInformation(hToken, TokenUser, NULL, 0, &size); andre@905: if (ERROR_INSUFFICIENT_BUFFER == GetLastError()) andre@905: { andre@905: userStruct = (PTOKEN_USER) xmalloc (size); andre@905: GetTokenInformation(hToken, TokenUser, userStruct, size, &size); andre@905: andre@905: sid = copy_sid(userStruct->User.Sid); andre@905: CloseHandle(hToken); andre@905: xfree (userStruct); andre@905: return sid; andre@905: } andre@905: } andre@905: return NULL; andre@670: } andre@841: andre@841: bool andre@841: is_system_install() andre@841: { andre@841: char *reg_inst_dir = NULL, andre@905: *real_prefix = NULL; andre@841: bool ret = false; andre@841: andre@841: reg_inst_dir = read_registry_string (HKEY_LOCAL_MACHINE, andre@1348: L"Software\\"ORGANIZATION"\\"APPNAME, L""); andre@841: andre@841: if (reg_inst_dir == NULL) andre@841: { andre@841: return false; andre@841: } andre@841: DEBUGPRINTF ("Registered installation directory: %s\n", reg_inst_dir); andre@841: andre@841: real_prefix = get_install_dir(); andre@841: andre@841: if (!real_prefix) andre@841: { andre@841: DEBUGPRINTF ("Failed to obtain installation prefix."); andre@841: xfree (reg_inst_dir); andre@841: return false; andre@841: } andre@841: andre@841: ret = paths_equal (real_prefix, reg_inst_dir); andre@841: andre@841: xfree (real_prefix); andre@841: xfree (reg_inst_dir); andre@841: DEBUGPRINTF ("Is system install? %s\n", ret ? "true" : "false"); andre@841: return ret; andre@841: } andre@841: #else /* WIN32 */ andre@841: andre@841: char * andre@1332: get_proc_install_dir(const char *proc) andre@841: { andre@841: char *retval = NULL, andre@1332: *procpath = NULL, andre@1332: *p = NULL, andre@1332: buf[MAX_PATH_LINUX]; andre@841: ssize_t ret; andre@841: size_t path_len = 0; andre@841: andre@1332: if (!proc) andre@1332: { andre@1332: return NULL; andre@1332: } andre@1332: andre@1332: xasprintf(&procpath, "/proc/%s/exe", proc); andre@1332: andre@1332: ret = readlink (procpath, buf, MAX_PATH_LINUX); andre@1332: xfree(procpath); andre@1332: procpath = NULL; andre@1332: andre@841: if (ret <= 0) andre@841: { andre@841: ERRORPRINTF ("readlink failed\n"); andre@841: return NULL; andre@841: } andre@841: andre@841: buf[ret] = '\0'; andre@841: andre@841: /* cut off the filename */ andre@841: p = strrchr (buf, '/'); andre@841: if (p == NULL) andre@841: { andre@841: ERRORPRINTF ("No filename found.\n"); andre@841: return NULL; andre@841: } andre@841: *(p + 1) = '\0'; andre@841: andre@841: path_len = strlen (buf); andre@841: retval = xmalloc (path_len + 1); andre@841: strncpy (retval, buf, path_len); andre@841: retval[path_len] = '\0'; andre@841: andre@841: return retval; andre@841: } andre@841: andre@1332: char * andre@1332: get_install_dir() andre@1332: { andre@1332: return get_proc_install_dir("self"); andre@1332: } andre@1332: andre@841: bool andre@841: is_system_install() andre@841: { andre@841: FILE *system_config; andre@841: int read_lines = 0; andre@841: char linebuf[MAX_PATH_LINUX + 7], andre@841: * inst_dir = NULL; andre@841: bool retval = false; andre@841: size_t inst_dir_len = 0; andre@841: andre@841: system_config = fopen ("/etc/"APPNAME"/"APPNAME"-inst.cfg", "r"); andre@841: if (system_config == NULL) andre@841: { andre@841: DEBUGPRINTF ("No system wide install configuration found.\n"); andre@841: return false; andre@841: } andre@841: inst_dir = get_install_dir (); andre@841: andre@841: if (inst_dir == NULL) andre@841: { andre@841: ERRORPRINTF ("Failed to find installation directory.\n"); andre@841: fclose(system_config); andre@841: return false; andre@841: } andre@841: andre@841: inst_dir_len = strnlen (inst_dir, MAX_PATH_LINUX); andre@841: andre@841: if (inst_dir_len == 0 || inst_dir_len >= MAX_PATH_LINUX) andre@841: { andre@841: ERRORPRINTF ("Installation directory invalid.\n"); andre@841: fclose(system_config); andre@841: return false; andre@841: } andre@841: andre@841: /* Read the first 10 lines and look for PREFIX. if it is not found andre@841: we return false. */ andre@841: while (read_lines < 10 && fgets (linebuf, MAX_PATH_LINUX + 7, andre@841: system_config) != NULL) andre@841: { andre@841: if (str_starts_with (linebuf, "PREFIX=")) andre@841: { andre@841: /* The last character is always a linebreak in a valid system_config andre@841: file so we can strip it. If this is not true the file is invalid. andre@841: linebuf is > 7 atm otherwise prefix= would not have been matched. */ andre@841: linebuf[strlen(linebuf) - 1] = '\0'; andre@841: retval = str_starts_with (inst_dir, linebuf + 7); andre@841: break; andre@841: } andre@841: read_lines++; andre@841: } andre@841: andre@841: fclose (system_config); andre@841: xfree (inst_dir); andre@841: DEBUGPRINTF ("Is system install? %s\n", retval ? "true" : "false"); andre@841: return retval; andre@841: } andre@671: #endif andre@670: andre@1031: #ifdef WIN32 aheinecke@321: bool andre@1029: has_high_integrity(HANDLE hToken) andre@1029: { andre@1029: PTOKEN_MANDATORY_LABEL integrity_label = NULL; andre@1029: DWORD integrity_level = 0, andre@1029: size = 0; andre@1029: andre@1029: if (hToken == NULL || hToken == INVALID_HANDLE_VALUE) andre@1029: { andre@1029: DEBUGPRINTF ("Invalid parameters."); andre@1029: return false; andre@1029: } andre@1029: andre@1029: /* Get the required size */ andre@1029: if (!GetTokenInformation(hToken, TokenIntegrityLevel, andre@1029: NULL, 0, &size) == ERROR_INSUFFICIENT_BUFFER) andre@1029: { andre@1029: PRINTLASTERROR ("Failed to get required size.\n"); andre@1029: return false; andre@1029: } andre@1029: integrity_label = (PTOKEN_MANDATORY_LABEL) LocalAlloc(0, size); andre@1029: if (integrity_label == NULL) andre@1029: { andre@1029: ERRORPRINTF ("Failed to allocate label. \n"); andre@1029: return false; andre@1029: } andre@1029: andre@1029: if (!GetTokenInformation(hToken, TokenIntegrityLevel, andre@1029: integrity_label, size, &size)) andre@1029: { andre@1029: PRINTLASTERROR ("Failed to get integrity level.\n"); andre@1029: LocalFree(integrity_label); andre@1029: return false; andre@1029: } andre@1029: andre@1029: /* Get the last integrity level */ andre@1029: integrity_level = *GetSidSubAuthority(integrity_label->Label.Sid, andre@1029: (DWORD)(UCHAR)(*GetSidSubAuthorityCount( andre@1029: integrity_label->Label.Sid) - 1)); andre@1029: andre@1029: LocalFree (integrity_label); andre@1029: andre@1029: return integrity_level >= SECURITY_MANDATORY_HIGH_RID; andre@1029: } andre@1031: #endif andre@1029: andre@1307: static bool g_is_elevated = false; andre@1307: static bool g_elevated_initalized = false; andre@1307: andre@1029: bool wilde@323: is_elevated() wilde@323: { wilde@323: bool ret = false; andre@1307: if (g_elevated_initalized) andre@1307: { andre@1307: return g_is_elevated; andre@1307: } wilde@323: #ifndef _WIN32 wilde@323: ret = (geteuid() == 0); wilde@323: #else wilde@323: HANDLE hToken = NULL; wilde@323: if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken)) wilde@323: { wilde@323: DWORD elevation; wilde@323: DWORD cbSize = sizeof (DWORD); wilde@323: if (GetTokenInformation (hToken, TokenElevation, &elevation, wilde@323: sizeof (TokenElevation), &cbSize)) wilde@323: { wilde@323: ret = elevation; wilde@323: } wilde@323: } andre@1029: /* Elevation will be true and ElevationType TokenElevationTypeFull even andre@1029: if the token is a user token created by SAFER so we additionally andre@1029: check the integrity level of the token which will only be high in andre@1029: the real elevated process and medium otherwise. */ andre@1029: andre@1029: ret = ret && has_high_integrity (hToken); andre@1029: wilde@323: if (hToken) wilde@323: CloseHandle (hToken); wilde@323: #endif andre@1307: g_is_elevated = ret; andre@1307: g_elevated_initalized = true; wilde@323: return ret; wilde@323: } aheinecke@505: andre@983: #ifdef _WIN32 andre@983: char * andre@983: get_program_files_folder () andre@983: { andre@983: wchar_t *folder_name = NULL; andre@983: char *retval = NULL; andre@983: if (SHGetKnownFolderPath (&FOLDERID_ProgramFiles, /* Get program data dir */ andre@983: KF_FLAG_NO_ALIAS, andre@983: INVALID_HANDLE_VALUE, /* Get it for the default user */ andre@983: &folder_name) != S_OK) andre@983: { andre@983: PRINTLASTERROR ("Failed to get program files folder."); andre@983: return NULL; andre@983: } andre@983: andre@983: retval = wchar_to_utf8 (folder_name, wcslen(folder_name)); andre@983: CoTaskMemFree (folder_name); andre@983: return retval; andre@983: } andre@983: andre@983: /* This is a bit ridicoulous but necessary as shlobj.h contains an inline andre@983: definition. So only one C file may include it and thus we have to put andre@983: all our shlobj calls into one file... */ andre@983: wchar_t * andre@983: get_program_data_folder () andre@983: { andre@983: wchar_t *folder_name = NULL; andre@1306: if (is_elevated()) andre@983: { andre@1306: if (SHGetKnownFolderPath (&FOLDERID_ProgramData, /* Get program data dir */ andre@1306: KF_FLAG_CREATE | /* Create if it does not exist */ andre@1306: KF_FLAG_INIT, /* Initialize it if created */ andre@1306: INVALID_HANDLE_VALUE, /* Get it for the default user */ andre@1306: &folder_name) != S_OK) andre@1306: { andre@1306: PRINTLASTERROR ("Failed to get folder path"); andre@1306: return NULL; andre@1306: } andre@1306: } andre@1306: else andre@1306: { andre@1306: if (SHGetKnownFolderPath (&FOLDERID_LocalAppData, /* Get program data dir */ andre@1306: KF_FLAG_CREATE | /* Create if it does not exist */ andre@1306: KF_FLAG_INIT, /* Initialize it if created */ andre@1306: NULL, /* Get it for the default user */ andre@1306: &folder_name) != S_OK) andre@1306: { andre@1306: PRINTLASTERROR ("Failed to get folder path"); andre@1306: return NULL; andre@1306: } andre@983: } andre@983: return folder_name; andre@983: } andre@983: #endif andre@983: andre@841: bool andre@841: is_admin() aheinecke@505: { aheinecke@505: #ifndef _WIN32 andre@644: struct passwd *current_user = getpwuid (geteuid()); andre@644: int ngroups = 0, andre@644: ret = 0, andre@644: i = 0; andre@644: gid_t * groups = NULL; andre@644: andre@644: if (current_user == NULL) andre@644: { andre@644: ERRORPRINTF ("Failed to obtain user information."); andre@644: return false; andre@644: } andre@644: andre@644: ret = getgrouplist (current_user->pw_name, current_user->pw_gid, NULL, andre@644: &ngroups); andre@644: andre@644: if (ret != -1 || ngroups <= 0) andre@644: { andre@644: ERRORPRINTF ("Unknown error in getgrouplist call"); andre@644: return false; andre@644: } andre@644: andre@644: groups = xmalloc (((unsigned int)ngroups) * sizeof (gid_t)); andre@644: andre@644: ret = getgrouplist (current_user->pw_name, current_user->pw_gid, groups, andre@644: &ngroups); andre@644: andre@644: if (ret != ngroups) andre@644: { andre@644: ERRORPRINTF ("Group length mismatch."); andre@644: xfree (groups); andre@644: return false; andre@644: } andre@644: andre@644: for (i = 0; i < ngroups; i++) andre@644: { andre@644: struct group *gr = getgrgid (groups[i]); andre@644: if (gr == NULL) andre@644: { andre@644: ERRORPRINTF ("Error in group enumeration"); andre@644: xfree (groups); andre@644: return false; andre@644: } andre@644: if (strcmp("sudo", gr->gr_name) == 0) andre@644: { andre@644: DEBUGPRINTF ("User is in sudo group \n"); andre@644: xfree (groups); andre@644: return true; andre@644: } andre@644: } andre@644: andre@644: DEBUGPRINTF ("User is not in sudo group"); andre@644: aheinecke@505: return false; aheinecke@505: #else aheinecke@505: bool retval = false; aheinecke@505: BOOL in_admin_group = FALSE; aheinecke@505: HANDLE hToken = NULL; aheinecke@505: HANDLE hTokenToCheck = NULL; aheinecke@505: DWORD cbSize = 0; aheinecke@505: TOKEN_ELEVATION_TYPE elevation; aheinecke@505: BYTE admin_id[SECURITY_MAX_SID_SIZE]; aheinecke@505: aheinecke@505: if (!OpenProcessToken(GetCurrentProcess(), aheinecke@505: TOKEN_QUERY | TOKEN_DUPLICATE, &hToken)) aheinecke@505: { aheinecke@505: PRINTLASTERROR ("Failed to duplicate process token.\n"); aheinecke@505: return false; aheinecke@505: } aheinecke@505: aheinecke@505: if (!GetTokenInformation(hToken, TokenElevationType, &elevation, aheinecke@505: sizeof(elevation), &cbSize)) aheinecke@505: { aheinecke@505: PRINTLASTERROR ("Failed to get token information.\n"); aheinecke@505: goto done; aheinecke@505: } aheinecke@505: aheinecke@505: /* If limited check the the linked token instead */ aheinecke@505: if (TokenElevationTypeLimited == elevation) aheinecke@505: { aheinecke@505: if (!GetTokenInformation(hToken, TokenLinkedToken, &hTokenToCheck, aheinecke@505: sizeof(hTokenToCheck), &cbSize)) aheinecke@505: { aheinecke@505: PRINTLASTERROR ("Failed to get the linked token.\n"); aheinecke@505: goto done; aheinecke@505: } aheinecke@505: } aheinecke@505: aheinecke@505: if (!hTokenToCheck) /* The linked token is already of the correct type */ aheinecke@505: { aheinecke@505: if (!DuplicateToken(hToken, SecurityIdentification, &hTokenToCheck)) aheinecke@505: { aheinecke@505: PRINTLASTERROR ("Failed to duplicate token for identification.\n"); aheinecke@505: goto done; aheinecke@505: } aheinecke@505: } aheinecke@505: aheinecke@505: /* Do the sid dance for the adminSID */ aheinecke@505: cbSize = sizeof(admin_id); aheinecke@505: if (!CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, &admin_id, aheinecke@505: &cbSize)) aheinecke@505: { aheinecke@505: PRINTLASTERROR ("Failed to get admin sid.\n"); aheinecke@505: goto done; aheinecke@505: } aheinecke@505: aheinecke@505: /* The actual check */ aheinecke@505: if (!CheckTokenMembership(hTokenToCheck, &admin_id, &in_admin_group)) aheinecke@505: { aheinecke@505: PRINTLASTERROR ("Failed to check token membership.\n"); aheinecke@505: goto done; aheinecke@505: } aheinecke@505: aheinecke@505: if (in_admin_group) aheinecke@505: { aheinecke@505: /* Winbool to standard bool */ aheinecke@505: retval = true; aheinecke@505: } aheinecke@505: aheinecke@505: done: aheinecke@505: if (hToken) CloseHandle(hToken); aheinecke@505: if (hTokenToCheck) CloseHandle(hTokenToCheck); aheinecke@505: aheinecke@505: return retval; aheinecke@505: #endif aheinecke@505: } aheinecke@505: andre@983: #ifdef WIN32 andre@983: bool andre@1208: create_restricted_directory (LPWSTR path, bool objects_should_inherit, PACL *rACL) andre@983: { andre@983: bool retval = false; andre@983: PSID everyone_SID = NULL, andre@983: admin_SID = NULL; andre@983: PACL access_control_list = NULL; andre@983: PSECURITY_DESCRIPTOR descriptor = NULL; andre@983: EXPLICIT_ACCESS explicit_access[2]; andre@983: SID_IDENTIFIER_AUTHORITY world_identifier = {SECURITY_WORLD_SID_AUTHORITY}, andre@983: admin_identifier = {SECURITY_NT_AUTHORITY}; andre@983: SECURITY_ATTRIBUTES security_attributes; andre@983: andre@983: ZeroMemory(&security_attributes, sizeof(security_attributes)); andre@983: ZeroMemory(&explicit_access, 2 * sizeof(EXPLICIT_ACCESS)); andre@983: andre@983: /* Create a well-known SID for the Everyone group. */ andre@983: if(!AllocateAndInitializeSid(&world_identifier, /* top-level identifier */ andre@983: 1, /* subauthorties count */ andre@983: SECURITY_WORLD_RID, /* Only one authority */ andre@983: 0, 0, 0, 0, 0, 0, 0, /* No other authorities*/ andre@983: &everyone_SID)) andre@983: { andre@983: PRINTLASTERROR ("Failed to allocate world sid.\n"); andre@983: return false; andre@983: } andre@983: andre@983: /* Initialize the first EXPLICIT_ACCESS structure for an ACE. andre@983: to allow everyone read access */ andre@983: explicit_access[0].grfAccessPermissions = GENERIC_READ; /* Give read access */ andre@983: explicit_access[0].grfAccessMode = SET_ACCESS; /* Overwrite other access for all users */ andre@1070: explicit_access[0].grfInheritance = objects_should_inherit ? andre@1070: SUB_CONTAINERS_AND_OBJECTS_INHERIT : /* make it stick */ andre@1070: NO_PROPAGATE_INHERIT_ACE; /* Don't inherit */ andre@983: explicit_access[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; andre@983: explicit_access[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; andre@983: explicit_access[0].Trustee.ptstrName = (LPTSTR) everyone_SID; andre@983: andre@983: /* Create the SID for the BUILTIN\Administrators group. */ andre@983: if(!AllocateAndInitializeSid(&admin_identifier, andre@983: 2, andre@983: SECURITY_BUILTIN_DOMAIN_RID, /*BUILTIN\ */ andre@983: DOMAIN_ALIAS_RID_ADMINS, /*\Administrators */ andre@983: 0, 0, 0, 0, 0, 0, /* No other */ andre@983: &admin_SID)) andre@983: { andre@983: PRINTLASTERROR ("Failed to allocate admin sid."); andre@983: goto done; andre@983: } andre@983: andre@983: /* explicit_access[1] grants admins full rights for this object and inherits andre@983: it to the children */ andre@983: explicit_access[1].grfAccessPermissions = GENERIC_ALL; andre@983: explicit_access[1].grfAccessMode = SET_ACCESS; andre@1070: explicit_access[1].grfInheritance = objects_should_inherit ? andre@1070: SUB_CONTAINERS_AND_OBJECTS_INHERIT : /* make it stick */ andre@1070: NO_PROPAGATE_INHERIT_ACE; /* Don't inherit */ andre@983: explicit_access[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; andre@983: explicit_access[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP; andre@983: explicit_access[1].Trustee.ptstrName = (LPTSTR) admin_SID; andre@983: andre@983: /* Set up the ACL structure. */ andre@983: if (ERROR_SUCCESS != SetEntriesInAcl(2, explicit_access, NULL, &access_control_list)) andre@983: { andre@983: PRINTLASTERROR ("Failed to set up Acl."); andre@983: goto done; andre@983: } andre@983: andre@983: /* Initialize a security descriptor */ andre@983: descriptor = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, andre@983: SECURITY_DESCRIPTOR_MIN_LENGTH); andre@983: if (descriptor == NULL) andre@983: { andre@983: PRINTLASTERROR("Failed to allocate descriptor."); andre@983: goto done; andre@983: } andre@983: andre@983: if (!InitializeSecurityDescriptor(descriptor, andre@983: SECURITY_DESCRIPTOR_REVISION)) andre@983: { andre@983: PRINTLASTERROR("Failed to initialize descriptor."); andre@983: goto done; andre@983: } andre@983: andre@983: /* Now we add the ACL to the the descriptor */ andre@983: if (!SetSecurityDescriptorDacl(descriptor, andre@983: TRUE, /* bDaclPresent flag */ andre@983: access_control_list, andre@983: FALSE)) /* not a default DACL */ andre@983: { andre@983: PRINTLASTERROR("Failed to set security descriptor."); andre@983: goto done; andre@983: } andre@983: andre@983: /* Finally set up the security attributes structure */ andre@983: security_attributes.nLength = sizeof (SECURITY_ATTRIBUTES); andre@983: security_attributes.lpSecurityDescriptor = descriptor; andre@983: security_attributes.bInheritHandle = FALSE; andre@983: andre@983: /* Use the security attributes to create the directory */ andre@983: if (!CreateDirectoryW(path, &security_attributes)) andre@983: { andre@983: DWORD err = GetLastError(); andre@983: if (err == ERROR_ALREADY_EXISTS) andre@983: { andre@1208: if (!objects_should_inherit) andre@1208: { andre@1208: /* This means it is a parent directory of something and andre@1208: we should not touch the DACL. */ andre@1208: retval = true; andre@1208: goto done; andre@1208: } andre@1208: andre@1208: /* Set our ACL on the directory */ andre@1208: err = SetNamedSecurityInfoW (path, andre@1208: SE_FILE_OBJECT, andre@1208: DACL_SECURITY_INFORMATION | andre@1208: OWNER_SECURITY_INFORMATION | andre@1208: GROUP_SECURITY_INFORMATION, andre@1208: admin_SID, /* owner */ andre@1208: admin_SID, /* group */ andre@1208: access_control_list, /* the dacl */ andre@1208: NULL); andre@1208: if (err != ERROR_SUCCESS) andre@1208: { andre@1208: ERRORPRINTF ("Failed to set security info on folder. Err: %lu", err); andre@1208: goto done; andre@1208: } andre@1208: } andre@1208: else andre@1208: { andre@1208: ERRORPRINTF ("Failed to create directory. Err: %lu", err); andre@983: goto done; andre@983: } andre@983: } andre@983: retval = true; andre@983: andre@983: done: andre@983: andre@1208: if (retval != true) andre@1208: { andre@1208: ERRORPRINTF ("Failed to create directory for NSS installer instructions."); andre@1208: syslog_error_printf ("Failed to create directory for NSS installer instructions."); andre@1208: } andre@1208: else if (rACL) andre@1208: { andre@1208: *rACL = access_control_list; andre@1208: } andre@1208: andre@1208: andre@983: if (everyone_SID) andre@983: FreeSid(everyone_SID); andre@983: if (admin_SID) andre@983: FreeSid(admin_SID); andre@1208: if (!rACL && access_control_list) andre@983: LocalFree(access_control_list); andre@983: if (descriptor) andre@983: LocalFree(descriptor); andre@983: andre@983: return retval; andre@983: } andre@983: #endif andre@1010: andre@1010: #ifdef WIN32 andre@1010: /** @brief get a restricted access token andre@1010: * andre@1010: * This function uses the Software Restriction API to obtain the andre@1010: * access token for a process run als normal user. andre@1010: * andre@1010: * @returns A restricted handle or NULL on error. andre@1010: */ andre@1010: HANDLE andre@1010: get_restricted_token() andre@1010: { andre@1010: SAFER_LEVEL_HANDLE user_level = NULL; andre@1010: HANDLE retval = NULL; andre@1118: SID_IDENTIFIER_AUTHORITY medium_identifier = {SECURITY_MANDATORY_LABEL_AUTHORITY}; andre@1118: PSID medium_sid = NULL; andre@1118: TOKEN_MANDATORY_LABEL integrity_label; andre@1118: andre@1118: memset (&integrity_label, 0, sizeof (integrity_label)); andre@1118: andre@1010: if (!SaferCreateLevel(SAFER_SCOPEID_USER, andre@1010: SAFER_LEVELID_NORMALUSER, andre@1010: SAFER_LEVEL_OPEN, &user_level, NULL)) andre@1010: { andre@1010: PRINTLASTERROR ("Failed to create user level.\n"); andre@1010: return NULL; andre@1010: } andre@1010: andre@1010: if (!SaferComputeTokenFromLevel(user_level, NULL, &retval, 0, NULL)) andre@1010: { andre@1010: SaferCloseLevel(user_level); andre@1010: return NULL; andre@1010: } andre@1010: andre@1118: SaferCloseLevel(user_level); andre@1010: andre@1118: /* Set the SID to medium it will still be high otherwise. Even if andre@1118: there is no high access allowed. */ andre@1118: if (!AllocateAndInitializeSid(&medium_identifier, andre@1118: 1, andre@1118: SECURITY_MANDATORY_MEDIUM_RID, andre@1118: 0, andre@1118: 0, andre@1118: 0, andre@1118: 0, andre@1118: 0, andre@1118: 0, andre@1118: 0, andre@1118: &medium_sid)) andre@1010: { andre@1118: PRINTLASTERROR ("Failed to initialize sid.\n"); andre@1010: return NULL; andre@1010: } andre@1010: andre@1118: integrity_label.Label.Attributes = SE_GROUP_INTEGRITY; andre@1118: integrity_label.Label.Sid = medium_sid; andre@1118: andre@1118: if (!SetTokenInformation(retval, andre@1118: TokenIntegrityLevel, andre@1118: &integrity_label, andre@1118: sizeof(TOKEN_MANDATORY_LABEL))) andre@1010: { andre@1118: PRINTLASTERROR ("Failed to set token integrity.\n"); andre@1010: return NULL; andre@1010: } andre@1010: andre@1010: return retval; andre@1010: } andre@1010: #endif