Browse Source

Windows compile fixes, check if running as administrator on startup for Windows.

Adam Ierymenko 11 years ago
parent
commit
2498ecbc84
3 changed files with 214 additions and 11 deletions
  1. 209 7
      main.cpp
  2. 4 4
      windows/ZeroTierOne/ZeroTierOne.vcxproj
  3. 1 0
      windows/ZeroTierOneService/Service.cs

+ 209 - 7
main.cpp

@@ -41,6 +41,7 @@
 #include <Windows.h>
 #include <tchar.h>
 #include <wchar.h>
+#include <lmcons.h>
 #else
 #include <unistd.h>
 #include <pwd.h>
@@ -317,8 +318,8 @@ static int main(int argc,char **argv)
 			fprintf(stderr,"%s is not readable"ZT_EOL_S,argv[3]);
 			return -1;
 		}
-		C25519::Signature signature = id.sign(inf.data(),inf.length());
-		printf("%s",Utils::hex(signature.data,signature.size()).c_str());
+		C25519::Signature signature = id.sign(inf.data(),(unsigned int)inf.length());
+		printf("%s",Utils::hex(signature.data,(unsigned int)signature.size()).c_str());
 	} else if (!strcmp(argv[1],"verify")) {
 		if (argc < 4) {
 			printHelp(stderr,argv[0]);
@@ -338,7 +339,7 @@ static int main(int argc,char **argv)
 		}
 
 		std::string signature(Utils::unhex(argv[4]));
-		if ((signature.length() > ZT_ADDRESS_LENGTH)&&(id.verify(inf.data(),inf.length(),signature.data(),signature.length()))) {
+		if ((signature.length() > ZT_ADDRESS_LENGTH)&&(id.verify(inf.data(),(unsigned int)inf.length(),signature.data(),(unsigned int)signature.length()))) {
 			printf("%s signature valid"ZT_EOL_S,argv[3]);
 		} else {
 			fprintf(stderr,"%s signature check FAILED"ZT_EOL_S,argv[3]);
@@ -380,7 +381,186 @@ static BOOL WINAPI _handlerRoutine(DWORD dwCtrlType)
 	}
 	return FALSE;
 }
-#endif
+
+static BOOL IsCurrentUserLocalAdministrator(void)
+{
+   BOOL   fReturn         = FALSE;
+   DWORD  dwStatus;
+   DWORD  dwAccessMask;
+   DWORD  dwAccessDesired;
+   DWORD  dwACLSize;
+   DWORD  dwStructureSize = sizeof(PRIVILEGE_SET);
+   PACL   pACL            = NULL;
+   PSID   psidAdmin       = NULL;
+
+   HANDLE hToken              = NULL;
+   HANDLE hImpersonationToken = NULL;
+
+   PRIVILEGE_SET   ps;
+   GENERIC_MAPPING GenericMapping;
+
+   PSECURITY_DESCRIPTOR     psdAdmin           = NULL;
+   SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;
+
+
+   /*
+      Determine if the current thread is running as a user that is a member 
+
+of
+      the local admins group.  To do this, create a security descriptor 
+
+that
+      has a DACL which has an ACE that allows only local aministrators 
+
+access.
+      Then, call AccessCheck with the current thread's token and the 
+
+security
+      descriptor.  It will say whether the user could access an object if 
+
+it
+      had that security descriptor.  Note: you do not need to actually 
+
+create
+      the object.  Just checking access against the security descriptor 
+
+alone
+      will be sufficient.
+   */
+   const DWORD ACCESS_READ  = 1;
+   const DWORD ACCESS_WRITE = 2;
+
+
+   __try
+   {
+
+      /*
+         AccessCheck() requires an impersonation token.  We first get a 
+
+primary
+         token and then create a duplicate impersonation token.  The
+         impersonation token is not actually assigned to the thread, but is
+         used in the call to AccessCheck.  Thus, this function itself never
+         impersonates, but does use the identity of the thread.  If the 
+
+thread
+         was impersonating already, this function uses that impersonation 
+
+context.
+      */
+      if (!OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE|TOKEN_QUERY, 
+
+TRUE, &hToken))
+      {
+         if (GetLastError() != ERROR_NO_TOKEN)
+            __leave;
+
+         if (!OpenProcessToken(GetCurrentProcess(), 
+
+TOKEN_DUPLICATE|TOKEN_QUERY, &hToken))
+            __leave;
+      }
+
+      if (!DuplicateToken (hToken, SecurityImpersonation, 
+
+&hImpersonationToken))
+          __leave;
+
+
+      /*
+        Create the binary representation of the well-known SID that
+        represents the local administrators group.  Then create the 
+
+security
+        descriptor and DACL with an ACE that allows only local admins 
+
+access.
+        After that, perform the access check.  This will determine whether
+        the current user is a local admin.
+      */
+      if (!AllocateAndInitializeSid(&SystemSidAuthority, 2,
+                                    SECURITY_BUILTIN_DOMAIN_RID,
+                                    DOMAIN_ALIAS_RID_ADMINS,
+                                    0, 0, 0, 0, 0, 0, &psidAdmin))
+         __leave;
+
+      psdAdmin = LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
+      if (psdAdmin == NULL)
+         __leave;
+
+      if (!InitializeSecurityDescriptor(psdAdmin, 
+
+SECURITY_DESCRIPTOR_REVISION))
+         __leave;
+
+      // Compute size needed for the ACL.
+      dwACLSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) +
+                  GetLengthSid(psidAdmin) - sizeof(DWORD);
+
+      pACL = (PACL)LocalAlloc(LPTR, dwACLSize);
+      if (pACL == NULL)
+         __leave;
+
+      if (!InitializeAcl(pACL, dwACLSize, ACL_REVISION2))
+         __leave;
+
+      dwAccessMask= ACCESS_READ | ACCESS_WRITE;
+
+      if (!AddAccessAllowedAce(pACL, ACL_REVISION2, dwAccessMask, 
+
+psidAdmin))
+         __leave;
+
+      if (!SetSecurityDescriptorDacl(psdAdmin, TRUE, pACL, FALSE))
+         __leave;
+
+      /*
+         AccessCheck validates a security descriptor somewhat; set the 
+
+group
+         and owner so that enough of the security descriptor is filled out 
+
+to
+         make AccessCheck happy.
+      */
+      SetSecurityDescriptorGroup(psdAdmin, psidAdmin, FALSE);
+      SetSecurityDescriptorOwner(psdAdmin, psidAdmin, FALSE);
+
+      if (!IsValidSecurityDescriptor(psdAdmin))
+         __leave;
+
+      dwAccessDesired = ACCESS_READ;
+
+      /*
+         Initialize GenericMapping structure even though you
+         do not use generic rights.
+      */
+      GenericMapping.GenericRead    = ACCESS_READ;
+      GenericMapping.GenericWrite   = ACCESS_WRITE;
+      GenericMapping.GenericExecute = 0;
+      GenericMapping.GenericAll     = ACCESS_READ | ACCESS_WRITE;
+
+      if (!AccessCheck(psdAdmin, hImpersonationToken, dwAccessDesired,
+                       &GenericMapping, &ps, &dwStructureSize, &dwStatus,
+                       &fReturn))
+      {
+         fReturn = FALSE;
+         __leave;
+      }
+   }
+   __finally
+   {
+      // Clean up.
+      if (pACL) LocalFree(pACL);
+      if (psdAdmin) LocalFree(psdAdmin);
+      if (psidAdmin) FreeSid(psidAdmin);
+      if (hImpersonationToken) CloseHandle (hImpersonationToken);
+      if (hToken) CloseHandle (hToken);
+   }
+
+   return fReturn;
+}
+#endif // __WINDOWS__
 
 #ifdef __WINDOWS__
 int _tmain(int argc, _TCHAR* argv[])
@@ -477,6 +657,13 @@ int main(int argc,char **argv)
 			fclose(pf);
 		}
 	}
+#else
+#ifdef __WINDOWS__
+	if (IsCurrentUserLocalAdministrator() != TRUE) {
+		fprintf(stderr,"%s: must be run as a local administrator."ZT_EOL_S,argv[0]);
+		return 1;
+	}
+#endif
 #endif
 
 	int exitCode = 0;
@@ -485,15 +672,30 @@ int main(int argc,char **argv)
 		node = new Node(homeDir,port,controlPort);
 		switch(node->run()) {
 			case Node::NODE_RESTART_FOR_UPGRADE: {
-#ifdef __UNIX_LIKE__
 				const char *upgPath = node->reasonForTermination();
+#ifdef __UNIX_LIKE__
+				// On Unix-type OSes we exec() right into the upgrade. This in turn will
+				// end with us being re-launched either via the upgrade itself or something
+				// like OSX's launchd.
 				if (upgPath) {
 					Utils::rm((std::string(homeDir)+"/zerotier-one.pid").c_str());
-					execl(upgPath,upgPath,(char *)0);
+					::execl(upgPath,upgPath,(char *)0);
 				}
 				exitCode = 2;
 				fprintf(stderr,"%s: abnormal termination: unable to execute update at %s\n",argv[0],(upgPath) ? upgPath : "(unknown path)");
-#endif
+#else // not __UNIX_LIKE
+#ifdef __WINDOWS__
+				// On Windows the service checks updates.d and invokes updates if they are
+				// found there. This only happens after exit code 4. The Windows service
+				// will listen to stdout as well to catch the filename.
+				if (upgPath) {
+					printf("[[[ UPDATE AVAILABLE: \"%s\" ]]]\r\n",upgPath);
+					exitCode = 4;
+				} else {
+					exitCode = 2;
+				}
+#endif // __WINDOWS__
+#endif // not __UNIX_LIKE__
 			}	break;
 			case Node::NODE_UNRECOVERABLE_ERROR: {
 				exitCode = 3;

+ 4 - 4
windows/ZeroTierOne/ZeroTierOne.vcxproj

@@ -151,22 +151,22 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <TargetExt>.exe</TargetExt>
     <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
-    <TargetName>zerotier-one-x86</TargetName>
+    <TargetName>zerotier-one_x86</TargetName>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <TargetExt>.exe</TargetExt>
     <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
-    <TargetName>zerotier-one-x86</TargetName>
+    <TargetName>zerotier-one_x86</TargetName>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
     <TargetExt>.exe</TargetExt>
     <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
-    <TargetName>zerotier-one-x64</TargetName>
+    <TargetName>zerotier-one_x64</TargetName>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
     <TargetExt>.exe</TargetExt>
     <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
-    <TargetName>zerotier-one-x64</TargetName>
+    <TargetName>zerotier-one_x64</TargetName>
   </PropertyGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>

+ 1 - 0
windows/ZeroTierOneService/Service.cs

@@ -45,6 +45,7 @@ namespace ZeroTierOneService
 
         private void ztService_Exited(object sender, System.EventArgs e)
         {
+            ztService = null;
         }
 
         private string ztHome;