This is the mail archive of the cygwin-apps mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] setup: allow running as non-admin


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; }

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]