2013-11-06 Shaddy Baddah * LogFile.cc (LogFile::flushAll): New function to flush log all logging to files without exiting (as LogFile::exit does). * LogFile.h: Declare new method closeAll. * main.cc (NoAdminOption): Add new CLI options -B/--no-admin. This option allows the user to suppress privilege elevation (in tandem with "asInvoker" requestedExecutionLevel changes to exe manifests). (WinMain): check if setup run with Administrator privilege and if the NoAdminOption has not been specified, attempt to elevate privilege to an Administrator via WINAPI ShellExecuteEx(). * setup.exe.manifest: Add requestedExecutionLevel of asInvoker to allow suppression of privilege elevation. * setup64.exe.manifest: Modify requestedExecutionLevel from requireAdministrator to asInvoker to allow suppression of privilege elevation. Continuity of privilege elevation attempt on startup is implemented by main.cc changes to WinMain(). * win32.cc (NTSecurity::isRunAsAdmin): New function to allow main.cc to check if setup.exe has been run with privilege elevated to Administrator level. * win32.h: Declare new method isRunAsAdmin. diff --git a/LogFile.cc b/LogFile.cc index 53c6ed7..ff8e260 100644 --- a/LogFile.cc +++ b/LogFile.cc @@ -149,6 +149,18 @@ LogFile::exit (int const exit_code) } void +LogFile::flushAll () +{ + log (LOG_TIMESTAMP) << "Writing messages to log files without exiting" << endLog; + + for (FileSet::iterator i = files.begin(); + i != files.end(); ++i) + { + log_save (i->level, i->key, i->append); + } +} + +void LogFile::log_save (int babble, const std::string& filename, bool append) { static int been_here = 0; diff --git a/LogFile.h b/LogFile.h index 912d2c5..3bed1d6 100644 --- a/LogFile.h +++ b/LogFile.h @@ -32,6 +32,7 @@ public: * but doesn't call generic C++ destructors */ virtual void exit (int const exit_code) __attribute__ ((noreturn)); + virtual void flushAll (); virtual ~LogFile(); // get a specific verbosity stream. virtual std::ostream &operator() (enum log_level level); diff --git a/main.cc b/main.cc index d4c6828..2be633e 100644 --- a/main.cc +++ b/main.cc @@ -35,6 +35,7 @@ static const char *cvsid = #define _WIN32_WINNT 0x0501 #include "win32.h" #include +#include #include "shlobj.h" #include @@ -93,6 +94,7 @@ HINSTANCE hinstance; static StringOption Arch ("", 'a', "arch", "architecture to install (x86_64 or x86)", false); static BoolOption UnattendedOption (false, 'q', "quiet-mode", "Unattended setup mode"); static BoolOption PackageManagerOption (false, 'M', "package-manager", "Semi-attended chooser-only mode"); +static BoolOption NoAdminOption (false, 'B', "no-admin", "Do not check for and enforce running as Administrator"); static BoolOption HelpOption (false, 'h', "help", "print help"); static BOOL WINAPI (*dyn_AttachConsole) (DWORD); static BOOL WINAPI (*dyn_GetLongPathName) (LPCTSTR, LPTSTR, DWORD); @@ -289,6 +291,57 @@ WinMain (HINSTANCE h, << "\nCommand Line Options:\n"); else { + OSVERSIONINFO version; + version.dwOSVersionInfoSize = sizeof version; + GetVersionEx (&version); + if ((version.dwMajorVersion >= 6) + && !NoAdminOption && !nt_sec.isRunAsAdmin ()) + { + log (LOG_PLAIN) << "Attempting to elevate to Administrator" << endLog; + char exe_path[MAX_PATH]; + if (!GetModuleFileName(NULL, exe_path, ARRAYSIZE(exe_path))) + { + log (LOG_TIMESTAMP) << "GetModuleFileName() failed: " << GetLastError () << endLog; + goto finish_up; + } + + SHELLEXECUTEINFO sei = { sizeof(sei) }; + sei.lpVerb = "runas"; + sei.lpFile = exe_path; + sei.nShow = SW_NORMAL; + + // Note, this is necessary to avoid an infinite loop. + // The understanding is that pre-Vista, the runas verb will not + // result in a privilege elevated process. Therefore we need to + // indicate to the forked process that it should be happy with + // whatever privileges it is run with. + std::string command_line_cs (command_line); + command_line_cs += " -"; + command_line_cs += NoAdminOption.shortOption(); + sei.lpParameters = command_line_cs.c_str (); + + // avoid the ambiguity of having both the parent and child + // process logging simultaneously, by ending it here. perhaps + // overkill, but safe. + theLog->flushAll (); + theLog->clearFiles (); + + if (!ShellExecuteEx(&sei)) + { + // Note: if user declined, we get an ERROR_CANCELLED. + // Merely to be compatible with existing Cygwin Setup + // behaviour. we do nothing with it but just exit. + // Future improvement is possible here. + + // TODO: because we have been prudent and closed off the + // logging, we can't actually log in this way. Though it + // would be helpful +// log (LOG_TIMESTAMP) << "ShellExecuteEx() failed: " << GetLastError () << endLog; + } + // once we are set on a course to privilege elevate, the parent + // process is unnecessary + goto finish_up; + } UserSettings Settings (local_dir); main_display (); @@ -296,6 +349,7 @@ WinMain (HINSTANCE h, Settings.save (); // Clean exit.. save user options. } +finish_up: if (rebootneeded) { theLog->exit (IDS_REBOOT_REQUIRED); diff --git a/setup.exe.manifest b/setup.exe.manifest index c195ebc..394f19f 100755 --- a/setup.exe.manifest +++ b/setup.exe.manifest @@ -19,6 +19,13 @@ /> + + + + + + + diff --git a/setup64.exe.manifest b/setup64.exe.manifest index 61c5241..f0bf282 100755 --- a/setup64.exe.manifest +++ b/setup64.exe.manifest @@ -22,7 +22,7 @@ - + diff --git a/win32.cc b/win32.cc index 31a923d..11e323d 100644 --- a/win32.cc +++ b/win32.cc @@ -400,6 +400,18 @@ NTSecurity::setDefaultSecurity () setAdminGroup (); } +bool +NTSecurity::isRunAsAdmin () +{ + BOOL is_run_as_admin = FALSE; + if (!CheckTokenMembership(NULL, administratorsSID.theSID (), &is_run_as_admin)) + { + NoteFailedAPI("CheckTokenMembership(administratorsSID)"); + } + return (is_run_as_admin == TRUE); +} + + VersionInfo::VersionInfo () { v.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); diff --git a/win32.h b/win32.h index 7838dcc..91ff184 100644 --- a/win32.h +++ b/win32.h @@ -132,6 +132,7 @@ public: void resetPrimaryGroup(); void setAdminGroup (); void setDefaultSecurity(); + bool isRunAsAdmin (); private: void NoteFailedAPI (const std::string &); bool wellKnownSIDsinitialized () const { return _wellKnownSIDsinitialized; }