diff --git a/Docs/src/history.but b/Docs/src/history.but index a37485b0..513fa150 100644 --- a/Docs/src/history.but +++ b/Docs/src/history.but @@ -10,10 +10,10 @@ Released on ? ?th, 201? \b RequestExecutionLevel now defaults to \c{admin} -\b FileReadUTF16LE now skips the optional BOM at the start of a file - \b LoadLibrary security hardening to prevent dll hijacking +\b FileReadUTF16LE now skips the optional BOM at the start of a file + \S2{} Minor Changes \b Fixed System plugin GUID type output bug on Win98 diff --git a/Source/build.cpp b/Source/build.cpp index cc465fc4..6fd1add1 100644 --- a/Source/build.cpp +++ b/Source/build.cpp @@ -3592,8 +3592,8 @@ again: // Delete $0 [simple, nothing that could clash with special temp permissions] ret=add_entry_direct(EW_DELETEFILE, zero_offset, DEL_SIMPLE); if (ret != PS_OK) return ret; - // CraeteDirectory $0 - a dir instead of that temp file - ret=add_entry_direct(EW_CREATEDIR, zero_offset); + // CreateDirectory $0 - a dir instead of that temp file + ret=add_entry_direct(EW_CREATEDIR, zero_offset, 0, 1); if (ret != PS_OK) return ret; // IfErrors Initialize_____Plugins_error - detect errors ret=add_entry_direct(EW_IFFLAG, ns_label.add(_T("Initialize_____Plugins_error"),0), 0, FLAG_OFFSET(exec_error)); diff --git a/Source/exehead/Main.c b/Source/exehead/Main.c index 8e658dd4..5f3dfee9 100644 --- a/Source/exehead/Main.c +++ b/Source/exehead/Main.c @@ -74,7 +74,7 @@ TCHAR *ValidateTempDir() if (!validpathspec(state_temp_dir)) return NULL; addtrailingslash(state_temp_dir); - CreateDirectory(state_temp_dir, NULL); + CreateNormalDirectory(state_temp_dir); // state_language is used as a temp var here return my_GetTempFileName(state_language, state_temp_dir); } @@ -273,16 +273,18 @@ EXTERN_C void NSISWinMainNOCRT() } else { - int x; + int x, admin = UserIsAdminGrpMember(); - mystrcat(state_temp_dir,_T("~nsu.tmp")); + mystrcat(state_temp_dir,_T("~nsu")); + if (admin) mystrcat(state_temp_dir,_T("A")); // Don't lock down the directory used by non-admins + mystrcat(state_temp_dir,_T(".tmp")); // check if already running from uninstaller temp dir // this prevents recursive uninstaller calls if (!lstrcmpi(state_temp_dir,state_exe_directory)) goto end; - CreateDirectory(state_temp_dir,NULL); + admin ? CreateRestrictedDirectory(state_temp_dir) : CreateNormalDirectory(state_temp_dir); SetCurrentDirectory(state_temp_dir); if (!(*state_install_directory)) diff --git a/Source/exehead/exec.c b/Source/exehead/exec.c index 854f7dc7..887b42d1 100644 --- a/Source/exehead/exec.c +++ b/Source/exehead/exec.c @@ -331,20 +331,23 @@ static int NSISCALL ExecuteEntry(entry *entry_) TCHAR *buf1=GetStringFromParm(-0x10); log_printf3(_T("CreateDirectory: \"%s\" (%d)"),buf1,parm1); { - TCHAR *p = skip_root(buf1); - TCHAR c = _T('c'); + TCHAR *p = skip_root(buf1), c = _T('c'); if (p) { while (c) { + DWORD ec; p = findchar(p, _T('\\')); - c = *p; - *p = 0; - if (!CreateDirectory(buf1, NULL)) + c = *p, *p = 0; + if (!c && parm2 && UserIsAdminGrpMember()) // Lock down the final directory? + ec = CreateRestrictedDirectory(buf1); + else + ec = CreateNormalDirectory(buf1); + if (ec) { - if (GetLastError() != ERROR_ALREADY_EXISTS) + if (ec != ERROR_ALREADY_EXISTS) { - log_printf3(_T("CreateDirectory: can't create \"%s\" (err=%d)"),buf1,GetLastError()); + log_printf3(_T("CreateDirectory: can't create \"%s\" (err=%d)"),buf1,ec); exec_error++; } else if ((GetFileAttributes(buf1) & FILE_ATTRIBUTE_DIRECTORY) == 0) diff --git a/Source/exehead/fileform.h b/Source/exehead/fileform.h index bed2dad7..5c885abf 100644 --- a/Source/exehead/fileform.h +++ b/Source/exehead/fileform.h @@ -61,7 +61,7 @@ enum EW_BRINGTOFRONT, // BringToFront: 0 EW_CHDETAILSVIEW, // SetDetailsView: 2 [listaction,buttonaction] EW_SETFILEATTRIBUTES, // SetFileAttributes: 2 [filename, attributes] - EW_CREATEDIR, // Create directory: 2, [path, ?update$INSTDIR] + EW_CREATEDIR, // Create directory: 2, [path, ?update$INSTDIR, ?restrictAcl] EW_IFFILEEXISTS, // IfFileExists: 3, [file name, jump amount if exists, jump amount if not exists] EW_SETFLAG, // Sets a flag: 2 [id, data] EW_IFFLAG, // If a flag: 4 [on, off, id, new value mask] diff --git a/Source/exehead/util.c b/Source/exehead/util.c index f5c2bfda..c97d105a 100644 --- a/Source/exehead/util.c +++ b/Source/exehead/util.c @@ -56,6 +56,38 @@ const NSIS_STRING*const g_usrvarsstart = (const NSIS_STRING*const) g_usrvarssect # endif #endif +const UINT32 g_restrictedacl[] = { + 0x00340002, 0x00000002, // ACL (ACL_REVISION2, 2 ACEs) + 0x00180300, // ACCESS_ALLOWED_ACE:ACE_HEADER (ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE|OBJECT_INHERIT_ACE) + 0x10000000, // ACCESS_ALLOWED_ACE:ACCESS_MASK: GENERIC_ALL + 0x00000201, 0x05000000, 0x00000020, 0x00000220, // ACCESS_ALLOWED_ACE:SID (BUILTIN\Administrators) NOTE: GetAdminGrpSid() relies on this being the first SID in the ACL + 0x00140300, // ACCESS_ALLOWED_ACE:ACE_HEADER (ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE|OBJECT_INHERIT_ACE) + 0x00130041, // ACCESS_ALLOWED_ACE:ACCESS_MASK: DELETE|READ_CONTROL|SYNCHRONIZE|FILE_DELETE_CHILD|FILE_LIST_DIRECTORY + 0x00000101, 0x01000000, 0x00000000 // ACCESS_ALLOWED_ACE:SID (WORLD\Everyone) +}; + +DWORD NSISCALL CreateRestrictedDirectory(LPCTSTR path) +{ + const SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION|PROTECTED_DACL_SECURITY_INFORMATION; + PSID admingrpsid = GetAdminGrpSid(); + SECURITY_DESCRIPTOR sd = { 1, 0, SE_DACL_PRESENT, admingrpsid, admingrpsid, NULL, GetAdminGrpAcl() }; + SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), &sd, FALSE }; + DWORD ec = CreateDirectory(path, &sa) ? ERROR_SUCCESS : GetLastError(); + if (ERROR_ALREADY_EXISTS == ec) + ec = SetFileSecurity(path, si, &sd) ? ERROR_SUCCESS : GetLastError(); + return ec; +} +DWORD NSISCALL CreateNormalDirectory(LPCTSTR path) +{ + return CreateDirectory(path, NULL) ? ERROR_SUCCESS : GetLastError(); +} + +BOOL NSISCALL UserIsAdminGrpMember() +{ + FARPROC iuaa = myGetProcAddress(MGA_IsUserAnAdmin); + return iuaa && ((BOOL(WINAPI*)())iuaa)(); +} + HANDLE NSISCALL myCreateProcess(TCHAR *cmd) { PROCESS_INFORMATION ProcInfo; @@ -1070,6 +1102,7 @@ struct MGA_FUNC MGA_FUNCS[] = { {"ADVAPI32", "RegDeleteKeyExW"}, #endif {"ADVAPI32", "InitiateShutdownW"}, + {"SHELL32", (CHAR*) 680}, // IsUserAnAdmin {"SHLWAPI", "SHAutoComplete"}, {"SHFOLDER", "SHGetFolderPathW"}, {"VERSION", "GetFileVersionInfoSizeW"}, @@ -1082,6 +1115,7 @@ struct MGA_FUNC MGA_FUNCS[] = { {"KERNEL32", "GetUserDefaultUILanguage"}, {"ADVAPI32", "RegDeleteKeyExA"}, {"ADVAPI32", "InitiateShutdownA"}, + {"SHELL32", (CHAR*) 680}, // IsUserAnAdmin {"SHLWAPI", "SHAutoComplete"}, {"SHFOLDER", "SHGetFolderPathA"}, {"VERSION", "GetFileVersionInfoSizeA"}, diff --git a/Source/exehead/util.h b/Source/exehead/util.h index e3906723..3de510a4 100644 --- a/Source/exehead/util.h +++ b/Source/exehead/util.h @@ -86,6 +86,13 @@ extern TCHAR g_log_file[1024]; #define LogData2Hex(x1,x2,x3,x4) #endif +extern const UINT32 g_restrictedacl[]; +#define GetAdminGrpAcl() ( (PACL) g_restrictedacl ) +#define GetAdminGrpSid() ( (PSID) &g_restrictedacl[4] ) +BOOL NSISCALL UserIsAdminGrpMember(); // Does not check integrity level, returns true if the process has a non-deny administrators group ACE in the token +DWORD NSISCALL CreateRestrictedDirectory(LPCTSTR path); +DWORD NSISCALL CreateNormalDirectory(LPCTSTR path); + HANDLE NSISCALL myCreateProcess(TCHAR *cmd); int NSISCALL my_MessageBox(const TCHAR *text, UINT type); @@ -126,6 +133,7 @@ enum myGetProcAddressFunctions { MGA_RegDeleteKeyEx, #endif MGA_InitiateShutdown, + MGA_IsUserAnAdmin, MGA_SHAutoComplete, // x64 can link to shlwapi directly but as long as MGA_SHGetFolderPath is used we can stick with myGetProcAddress MGA_SHGetFolderPath, // TODO: This can probably call something else directly on x64 #ifdef NSIS_SUPPORT_GETDLLVERSION