Hi,
On Nov 07 11:23, Christopher Faylor wrote:
Hi,
As discussed, please find the patch to allow setup to be run as a
non-admin user include clear text.
--
Regards,
Shaddy
Only a very minor comment:
+
+ // Note, this is necessary to avoid an infinite loop.
+ // The understanding is that pre-Vista, the runas verb will not
This indentation looks wrong.
Corrected with this updated patch.
--
Regards,
Shaddy
2013-11-06 Shaddy Baddah <lithium-cygwin at shaddybaddah dot name>
* 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..127b279 100644
--- a/main.cc
+++ b/main.cc
@@ -35,6 +35,7 @@ static const char *cvsid =
#define _WIN32_WINNT 0x0501
#include "win32.h"
#include <commctrl.h>
+#include <shellapi.h>
#include "shlobj.h"
#include <stdio.h>
@@ -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 @@
/>
</dependentAssembly>
</dependency>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!--The ID below indicates application support for Windows Vista -->
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 @@
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
- <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
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; }