github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cloudconfig/windows_userdata_test.go (about)

     1  // Copyright 2014, 2015 Canonical Ltd.
     2  // Copyright 2014, 2015 Cloudbase Solutions
     3  // Copyright 2012 Aaron Jensen
     4  //
     5  // Licensed under the AGPLv3, see LICENCE file for details.
     6  //
     7  // This file borrowed some code from https://bitbucket.org/splatteredbits/carbon
     8  // (see Source/Security/Privilege.cs). This external source is licensed under
     9  // Apache-2.0 license which is compatible with AGPLv3 license. Because it's
    10  // compatible we can and have licensed this derived work under AGPLv3. The original
    11  // Apache-2.0 license for the external source can be found inside Apache-License.txt.
    12  // Copyright statement of the external source: Copyright 2012 Aaron Jensen
    13  
    14  package cloudconfig_test
    15  
    16  var WindowsUserdata = `#ps1_sysnative
    17  
    18  
    19  
    20  $ErrorActionPreference = "Stop"
    21  
    22  function ExecRetry($command, $retryInterval = 15)
    23  {
    24  	$currErrorActionPreference = $ErrorActionPreference
    25  	$ErrorActionPreference = "Continue"
    26  
    27  	while ($true)
    28  	{
    29  		try
    30  		{
    31  			& $command
    32  			break
    33  		}
    34  		catch [System.Exception]
    35  		{
    36  			Write-Error $_.Exception
    37  			Start-Sleep $retryInterval
    38  		}
    39  	}
    40  
    41  	$ErrorActionPreference = $currErrorActionPreference
    42  }
    43  
    44  # TryExecAll attempts all of the commands in the supplied array until
    45  # one can be executed without throwing an exception. If none of the
    46  # commands succeeds, an exception will be raised.
    47  function TryExecAll($commands)
    48  {
    49  	$currErrorActionPreference = $ErrorActionPreference
    50  	$ErrorActionPreference = "Continue"
    51  
    52  	foreach ($command in $commands)
    53  	{
    54  		try
    55  		{
    56  			& $command
    57  			$ErrorActionPreference = $currErrorActionPreference
    58  			return
    59  		}
    60  		catch [System.Exception]
    61  		{
    62  			Write-Error $_.Exception
    63  		}
    64  	}
    65  
    66  	$ErrorActionPreference = $currErrorActionPreference
    67  	throw "All commands failed"
    68  }
    69  
    70  Function GUnZip-File{
    71      Param(
    72          $infile,
    73          $outdir
    74          )
    75  
    76      $input = New-Object System.IO.FileStream $inFile, ([IO.FileMode]::Open), ([IO.FileAccess]::Read), ([IO.FileShare]::Read)
    77      $tempFile = "$env:TEMP\jujud.tar"
    78      $tempOut = New-Object System.IO.FileStream $tempFile, ([IO.FileMode]::Create), ([IO.FileAccess]::Write), ([IO.FileShare]::None)
    79      $gzipStream = New-Object System.IO.Compression.GzipStream $input, ([IO.Compression.CompressionMode]::Decompress)
    80  
    81      $buffer = New-Object byte[](1024)
    82      while($true) {
    83          $read = $gzipstream.Read($buffer, 0, 1024)
    84          if ($read -le 0){break}
    85          $tempOut.Write($buffer, 0, $read)
    86      }
    87      $gzipStream.Close()
    88      $tempOut.Close()
    89      $input.Close()
    90  
    91      $in = New-Object System.IO.FileStream $tempFile, ([IO.FileMode]::Open), ([IO.FileAccess]::Read), ([IO.FileShare]::Read)
    92      Untar-File $in $outdir
    93      $in.Close()
    94      rm $tempFile
    95  }
    96  
    97  $HEADERSIZE = 512
    98  
    99  Function Untar-File {
   100      Param(
   101          $inStream,
   102          $outdir
   103          )
   104      $DirectoryEntryType = 0x35
   105      $headerBytes = New-Object byte[]($HEADERSIZE)
   106  
   107      # $headerBytes is written inside, function returns whether we've reached the end
   108      while(GetHeaderBytes $inStream $headerBytes) {
   109          $fileName, $entryType, $sizeInBytes = GetFileInfoFromHeader $headerBytes
   110  
   111          $totalPath = Join-Path $outDir $fileName
   112          if ($entryType -eq $DirectoryEntryType) {
   113              [System.IO.Directory]::CreateDirectory($totalPath)
   114              continue;
   115          }
   116  
   117          $fName = [System.IO.Path]::GetFileName($totalPath)
   118          $dirName = [System.IO.Path]::GetDirectoryName($totalPath)
   119          [System.IO.Directory]::CreateDirectory($dirName)
   120          $file = [System.IO.File]::Create($totalPath)
   121          WriteTarEntryToFile $inStream $file $sizeInBytes
   122          $file.Close()
   123      }
   124  }
   125  
   126  Function WriteTarEntryToFile {
   127      Param(
   128          $inStream,
   129          $outFile,
   130          $sizeInBytes
   131          )
   132      $moveToAlign512 = 0
   133      $toRead = 0
   134      $buf = New-Object byte[](512)
   135  
   136      $remainingBytesInFile = $sizeInBytes
   137      while ($remainingBytesInFile -ne 0) {
   138          if ($remainingBytesInFile - 512 -lt 0) {
   139              $moveToAlign512 = 512 - $remainingBytesInFile
   140              $toRead = $remainingBytesInFile
   141          } else {
   142              $toRead = 512
   143          }
   144  
   145          $bytesRead = 0
   146          $bytesRemainingToRead = $toRead
   147          while ($bytesRead -lt $toRead -and $bytesRemainingToRead -gt 0) {
   148              $bytesRead = $inStream.Read($buf, $toRead - $bytesRemainingToRead, $bytesRemainingToRead)
   149              $bytesRemainingToRead = $bytesRemainingToRead - $bytesRead
   150              $remainingBytesInFile = $remainingBytesInFile - $bytesRead
   151              $outFile.Write($buf, 0, $bytesRead)
   152          }
   153  
   154          if ($moveToAlign512 -ne 0) {
   155              $inStream.Seek($moveToAlign512, [System.IO.SeekOrigin]::Current)
   156          }
   157      }
   158  }
   159  
   160  Function GetHeaderBytes {
   161      Param($inStream, $headerBytes)
   162  
   163      $headerRead = 0
   164      $bytesRemaining = $HEADERSIZE
   165      while ($bytesRemaining -gt 0) {
   166          $headerRead = $inStream.Read($headerBytes, $HEADERSIZE - $bytesRemaining, $bytesRemaining)
   167          $bytesRemaining -= $headerRead
   168          if ($headerRead -le 0 -and $bytesRemaining -gt 0) {
   169              throw "Error reading tar header. Header size invalid"
   170          }
   171      }
   172  
   173      # Proper end of archive is 2 empty headers
   174      if (IsEmptyByteArray $headerBytes) {
   175          $bytesRemaining = $HEADERSIZE
   176          while ($bytesRemaining -gt 0) {
   177              $headerRead = $inStream.Read($headerBytes, $HEADERSIZE - $bytesRemaining, $bytesRemaining)
   178              $bytesRemaining -= $headerRead
   179              if ($headerRead -le 0 -and $bytesRemaining -gt 0) {
   180                  throw "Broken end archive"
   181              }
   182          }
   183          if ($bytesRemaining -eq 0 -and (IsEmptyByteArray($headerBytes))) {
   184              return $false
   185          }
   186          throw "Error occurred: expected end of archive"
   187      }
   188  
   189      return $true
   190  }
   191  
   192  Function GetFileInfoFromHeader {
   193      Param($headerBytes)
   194  
   195      $FileName = [System.Text.Encoding]::UTF8.GetString($headerBytes, 0, 100);
   196      $EntryType = $headerBytes[156];
   197      $SizeInBytes = [Convert]::ToInt64([System.Text.Encoding]::ASCII.GetString($headerBytes, 124, 11).Trim(), 8);
   198      Return $FileName.replace("` + "`" + `0", [String].Empty), $EntryType, $SizeInBytes
   199  }
   200  
   201  Function IsEmptyByteArray {
   202      Param ($bytes)
   203      foreach($b in $bytes) {
   204          if ($b -ne 0) {
   205              return $false
   206          }
   207      }
   208      return $true
   209  }
   210  
   211  Function Get-FileSHA256{
   212  	Param(
   213  		$FilePath
   214  	)
   215  	try {
   216  		$hash = [Security.Cryptography.HashAlgorithm]::Create( "SHA256" )
   217  		$stream = ([IO.StreamReader]$FilePath).BaseStream
   218  		$res = -join ($hash.ComputeHash($stream) | ForEach { "{0:x2}" -f $_ })
   219  		$stream.Close()
   220  		return $res
   221  	} catch [System.Management.Automation.RuntimeException] {
   222  		return (Get-FileHash -Path $FilePath).Hash
   223  	}
   224  }
   225  
   226  Function Invoke-FastWebRequest {
   227  	Param(
   228  		$URI,
   229  		$OutFile
   230  	)
   231  
   232  	if(!([System.Management.Automation.PSTypeName]'System.Net.Http.HttpClient').Type)
   233  	{
   234  		$assembly = [System.Reflection.Assembly]::LoadWithPartialName("System.Net.Http")
   235  	}
   236  
   237  	$client = new-object System.Net.Http.HttpClient
   238  
   239  	$task = $client.GetStreamAsync($URI)
   240  	$response = $task.Result
   241  	$outStream = New-Object IO.FileStream $OutFile, Create, Write, None
   242  
   243  	try {
   244  		$totRead = 0
   245  		$buffer = New-Object Byte[] 1MB
   246  		while (($read = $response.Read($buffer, 0, $buffer.Length)) -gt 0) {
   247  		$totRead += $read
   248  		$outStream.Write($buffer, 0, $read);
   249  		}
   250  	}
   251  	finally {
   252  		$outStream.Close()
   253  	}
   254  }
   255  
   256  
   257  
   258  function create-account ([string]$accountName, [string]$accountDescription, [string]$password) {
   259  	$hostname = hostname
   260  	$comp = [adsi]"WinNT://$hostname"
   261  	$user = $comp.Create("User", $accountName)
   262  	$user.SetPassword($password)
   263  	$user.SetInfo()
   264  	$user.description = $accountDescription
   265  	$user.SetInfo()
   266  	$User.UserFlags[0] = $User.UserFlags[0] -bor 0x10000
   267  	$user.SetInfo()
   268  
   269  	# This gets the Administrator group name that is localized on different windows versions. 
   270  	# However the SID S-1-5-32-544 is the same on all versions.
   271  	$adminGroup = (New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-544")).Translate([System.Security.Principal.NTAccount]).Value.Split("\")[1]
   272  
   273  	$objOU = [ADSI]"WinNT://$hostname/$adminGroup,group"
   274  	$objOU.add("WinNT://$hostname/$accountName")
   275  }
   276  
   277  $Source = @"
   278  using System;
   279  using System.Text;
   280  using System.Runtime.InteropServices;
   281  
   282  namespace PSCloudbase
   283  {
   284  	public sealed class Win32CryptApi
   285  	{
   286  		public static long CRYPT_SILENT = 0x00000040;
   287  		public static long CRYPT_VERIFYCONTEXT = 0xF0000000;
   288  		public static int PROV_RSA_FULL = 1;
   289  
   290  		[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
   291  		[return : MarshalAs(UnmanagedType.Bool)]
   292  		public static extern bool CryptAcquireContext(ref IntPtr hProv,
   293  													  StringBuilder pszContainer, // Don't use string, as Powershell replaces $null with an empty string
   294  													  StringBuilder pszProvider, // Don't use string, as Powershell replaces $null with an empty string
   295  													  uint dwProvType,
   296  													  uint dwFlags);
   297  
   298  		[DllImport("Advapi32.dll", EntryPoint = "CryptReleaseContext", CharSet = CharSet.Unicode, SetLastError = true)]
   299  		public static extern bool CryptReleaseContext(IntPtr hProv, Int32 dwFlags);
   300  
   301  		[DllImport("advapi32.dll", SetLastError=true)]
   302  		public static extern bool CryptGenRandom(IntPtr hProv, uint dwLen, byte[] pbBuffer);
   303  
   304  		[DllImport("Kernel32.dll")]
   305  		public static extern uint GetLastError();
   306  	}
   307  }
   308  "@
   309  
   310  Add-Type -TypeDefinition $Source -Language CSharp
   311  
   312  function Get-RandomPassword
   313  {
   314  	[CmdletBinding()]
   315  	param
   316  	(
   317  		[parameter(Mandatory=$true)]
   318  		[int]$Length
   319  	)
   320  	process
   321  	{
   322  		$hProvider = 0
   323  		try
   324  		{
   325  			if(![PSCloudbase.Win32CryptApi]::CryptAcquireContext([ref]$hProvider, $null, $null,
   326  																 [PSCloudbase.Win32CryptApi]::PROV_RSA_FULL,
   327  																 ([PSCloudbase.Win32CryptApi]::CRYPT_VERIFYCONTEXT -bor
   328  																  [PSCloudbase.Win32CryptApi]::CRYPT_SILENT)))
   329  			{
   330  				throw "CryptAcquireContext failed with error: 0x" + "{0:X0}" -f [PSCloudbase.Win32CryptApi]::GetLastError()
   331  			}
   332  
   333  			$buffer = New-Object byte[] $Length
   334  			if(![PSCloudbase.Win32CryptApi]::CryptGenRandom($hProvider, $Length, $buffer))
   335  			{
   336  				throw "CryptGenRandom failed with error: 0x" + "{0:X0}" -f [PSCloudbase.Win32CryptApi]::GetLastError()
   337  			}
   338  
   339  			$buffer | ForEach-Object { $password += "{0:X0}" -f $_ }
   340  			return $password
   341  		}
   342  		finally
   343  		{
   344  			if($hProvider)
   345  			{
   346  				$retVal = [PSCloudbase.Win32CryptApi]::CryptReleaseContext($hProvider, 0)
   347  			}
   348  		}
   349  	}
   350  }
   351  $SourcePolicy = @"
   352  /*
   353  Original sources available at: https://bitbucket.org/splatteredbits/carbon
   354  */
   355  
   356  using System;
   357  using System.Collections.Generic;
   358  using System.ComponentModel;
   359  using System.Runtime.InteropServices;
   360  using System.Security.Principal;
   361  using System.Text;
   362  
   363  namespace PSCarbon
   364  {
   365  	public sealed class Lsa
   366  	{
   367  		// ReSharper disable InconsistentNaming
   368  		[StructLayout(LayoutKind.Sequential)]
   369  		internal struct LSA_UNICODE_STRING
   370  		{
   371  			internal LSA_UNICODE_STRING(string inputString)
   372  			{
   373  				if (inputString == null)
   374  				{
   375  					Buffer = IntPtr.Zero;
   376  					Length = 0;
   377  					MaximumLength = 0;
   378  				}
   379  				else
   380  				{
   381  					Buffer = Marshal.StringToHGlobalAuto(inputString);
   382  					Length = (ushort)(inputString.Length * UnicodeEncoding.CharSize);
   383  					MaximumLength = (ushort)((inputString.Length + 1) * UnicodeEncoding.CharSize);
   384  				}
   385  			}
   386  
   387  			internal ushort Length;
   388  			internal ushort MaximumLength;
   389  			internal IntPtr Buffer;
   390  		}
   391  
   392  		[StructLayout(LayoutKind.Sequential)]
   393  		internal struct LSA_OBJECT_ATTRIBUTES
   394  		{
   395  			internal uint Length;
   396  			internal IntPtr RootDirectory;
   397  			internal LSA_UNICODE_STRING ObjectName;
   398  			internal uint Attributes;
   399  			internal IntPtr SecurityDescriptor;
   400  			internal IntPtr SecurityQualityOfService;
   401  		}
   402  
   403  		[StructLayout(LayoutKind.Sequential)]
   404  		public struct LUID
   405  		{
   406  			public uint LowPart;
   407  			public int HighPart;
   408  		}
   409  
   410  		// ReSharper disable UnusedMember.Local
   411  		private const uint POLICY_VIEW_LOCAL_INFORMATION = 0x00000001;
   412  		private const uint POLICY_VIEW_AUDIT_INFORMATION = 0x00000002;
   413  		private const uint POLICY_GET_PRIVATE_INFORMATION = 0x00000004;
   414  		private const uint POLICY_TRUST_ADMIN = 0x00000008;
   415  		private const uint POLICY_CREATE_ACCOUNT = 0x00000010;
   416  		private const uint POLICY_CREATE_SECRET = 0x00000014;
   417  		private const uint POLICY_CREATE_PRIVILEGE = 0x00000040;
   418  		private const uint POLICY_SET_DEFAULT_QUOTA_LIMITS = 0x00000080;
   419  		private const uint POLICY_SET_AUDIT_REQUIREMENTS = 0x00000100;
   420  		private const uint POLICY_AUDIT_LOG_ADMIN = 0x00000200;
   421  		private const uint POLICY_SERVER_ADMIN = 0x00000400;
   422  		private const uint POLICY_LOOKUP_NAMES = 0x00000800;
   423  		private const uint POLICY_NOTIFICATION = 0x00001000;
   424  		// ReSharper restore UnusedMember.Local
   425  
   426  		[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
   427  		public static extern bool LookupPrivilegeValue(
   428  			[MarshalAs(UnmanagedType.LPTStr)] string lpSystemName,
   429  			[MarshalAs(UnmanagedType.LPTStr)] string lpName,
   430  			out LUID lpLuid);
   431  
   432  		[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
   433  		private static extern uint LsaAddAccountRights(
   434  			IntPtr PolicyHandle,
   435  			IntPtr AccountSid,
   436  			LSA_UNICODE_STRING[] UserRights,
   437  			uint CountOfRights);
   438  
   439  		[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = false)]
   440  		private static extern uint LsaClose(IntPtr ObjectHandle);
   441  
   442  		[DllImport("advapi32.dll", SetLastError = true)]
   443  		private static extern uint LsaEnumerateAccountRights(IntPtr PolicyHandle,
   444  			IntPtr AccountSid,
   445  			out IntPtr UserRights,
   446  			out uint CountOfRights
   447  			);
   448  
   449  		[DllImport("advapi32.dll", SetLastError = true)]
   450  		private static extern uint LsaFreeMemory(IntPtr pBuffer);
   451  
   452  		[DllImport("advapi32.dll")]
   453  		private static extern int LsaNtStatusToWinError(long status);
   454  
   455  		[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
   456  		private static extern uint LsaOpenPolicy(ref LSA_UNICODE_STRING SystemName, ref LSA_OBJECT_ATTRIBUTES ObjectAttributes, uint DesiredAccess, out IntPtr PolicyHandle );
   457  
   458  		[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
   459  		static extern uint LsaRemoveAccountRights(
   460  			IntPtr PolicyHandle,
   461  			IntPtr AccountSid,
   462  			[MarshalAs(UnmanagedType.U1)]
   463  			bool AllRights,
   464  			LSA_UNICODE_STRING[] UserRights,
   465  			uint CountOfRights);
   466  		// ReSharper restore InconsistentNaming
   467  
   468  		private static IntPtr GetIdentitySid(string identity)
   469  		{
   470  			SecurityIdentifier sid =
   471  				new NTAccount(identity).Translate(typeof (SecurityIdentifier)) as SecurityIdentifier;
   472  			if (sid == null)
   473  			{
   474  				throw new ArgumentException(string.Format("Account {0} not found.", identity));
   475  			}
   476  			byte[] sidBytes = new byte[sid.BinaryLength];
   477  			sid.GetBinaryForm(sidBytes, 0);
   478  			System.IntPtr sidPtr = Marshal.AllocHGlobal(sidBytes.Length);
   479  			Marshal.Copy(sidBytes, 0, sidPtr, sidBytes.Length);
   480  			return sidPtr;
   481  		}
   482  
   483  		private static IntPtr GetLsaPolicyHandle()
   484  		{
   485  			string computerName = Environment.MachineName;
   486  			IntPtr hPolicy;
   487  			LSA_OBJECT_ATTRIBUTES objectAttributes = new LSA_OBJECT_ATTRIBUTES();
   488  			objectAttributes.Length = 0;
   489  			objectAttributes.RootDirectory = IntPtr.Zero;
   490  			objectAttributes.Attributes = 0;
   491  			objectAttributes.SecurityDescriptor = IntPtr.Zero;
   492  			objectAttributes.SecurityQualityOfService = IntPtr.Zero;
   493  
   494  			const uint ACCESS_MASK = POLICY_CREATE_SECRET | POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION;
   495  			LSA_UNICODE_STRING machineNameLsa = new LSA_UNICODE_STRING(computerName);
   496  			uint result = LsaOpenPolicy(ref machineNameLsa, ref objectAttributes, ACCESS_MASK, out hPolicy);
   497  			HandleLsaResult(result);
   498  			return hPolicy;
   499  		}
   500  
   501  		public static string[] GetPrivileges(string identity)
   502  		{
   503  			IntPtr sidPtr = GetIdentitySid(identity);
   504  			IntPtr hPolicy = GetLsaPolicyHandle();
   505  			IntPtr rightsPtr = IntPtr.Zero;
   506  
   507  			try
   508  			{
   509  
   510  				List<string> privileges = new List<string>();
   511  
   512  				uint rightsCount;
   513  				uint result = LsaEnumerateAccountRights(hPolicy, sidPtr, out rightsPtr, out rightsCount);
   514  				int win32ErrorCode = LsaNtStatusToWinError(result);
   515  				// the user has no privileges
   516  				if( win32ErrorCode == STATUS_OBJECT_NAME_NOT_FOUND )
   517  				{
   518  					return new string[0];
   519  				}
   520  				HandleLsaResult(result);
   521  
   522  				LSA_UNICODE_STRING myLsaus = new LSA_UNICODE_STRING();
   523  				for (ulong i = 0; i < rightsCount; i++)
   524  				{
   525  					IntPtr itemAddr = new IntPtr(rightsPtr.ToInt64() + (long) (i*(ulong) Marshal.SizeOf(myLsaus)));
   526  					myLsaus = (LSA_UNICODE_STRING) Marshal.PtrToStructure(itemAddr, myLsaus.GetType());
   527  					char[] cvt = new char[myLsaus.Length/UnicodeEncoding.CharSize];
   528  					Marshal.Copy(myLsaus.Buffer, cvt, 0, myLsaus.Length/UnicodeEncoding.CharSize);
   529  					string thisRight = new string(cvt);
   530  					privileges.Add(thisRight);
   531  				}
   532  				return privileges.ToArray();
   533  			}
   534  			finally
   535  			{
   536  				Marshal.FreeHGlobal(sidPtr);
   537  				uint result = LsaClose(hPolicy);
   538  				HandleLsaResult(result);
   539  				result = LsaFreeMemory(rightsPtr);
   540  				HandleLsaResult(result);
   541  			}
   542  		}
   543  
   544  		public static void GrantPrivileges(string identity, string[] privileges)
   545  		{
   546  			IntPtr sidPtr = GetIdentitySid(identity);
   547  			IntPtr hPolicy = GetLsaPolicyHandle();
   548  
   549  			try
   550  			{
   551  				LSA_UNICODE_STRING[] lsaPrivileges = StringsToLsaStrings(privileges);
   552  				uint result = LsaAddAccountRights(hPolicy, sidPtr, lsaPrivileges, (uint)lsaPrivileges.Length);
   553  				HandleLsaResult(result);
   554  			}
   555  			finally
   556  			{
   557  				Marshal.FreeHGlobal(sidPtr);
   558  				uint result = LsaClose(hPolicy);
   559  				HandleLsaResult(result);
   560  			}
   561  		}
   562  
   563  		const int STATUS_SUCCESS = 0x0;
   564  		const int STATUS_OBJECT_NAME_NOT_FOUND = 0x00000002;
   565  		const int STATUS_ACCESS_DENIED = 0x00000005;
   566  		const int STATUS_INVALID_HANDLE = 0x00000006;
   567  		const int STATUS_UNSUCCESSFUL = 0x0000001F;
   568  		const int STATUS_INVALID_PARAMETER = 0x00000057;
   569  		const int STATUS_NO_SUCH_PRIVILEGE = 0x00000521;
   570  		const int STATUS_INVALID_SERVER_STATE = 0x00000548;
   571  		const int STATUS_INTERNAL_DB_ERROR = 0x00000567;
   572  		const int STATUS_INSUFFICIENT_RESOURCES = 0x000005AA;
   573  
   574  		private static Dictionary<int, string> ErrorMessages = new Dictionary<int, string>();
   575  		public Lsa () {
   576  			ErrorMessages.Add(STATUS_ACCESS_DENIED, "Access denied. Caller does not have the appropriate access to complete the operation.");
   577  			ErrorMessages.Add(STATUS_INVALID_HANDLE, "Invalid handle. Indicates an object or RPC handle is not valid in the context used.");
   578  			ErrorMessages.Add(STATUS_UNSUCCESSFUL, "Unsuccessful. Generic failure, such as RPC connection failure.");
   579  			ErrorMessages.Add(STATUS_INVALID_PARAMETER, "Invalid parameter. One of the parameters is not valid.");
   580  			ErrorMessages.Add(STATUS_NO_SUCH_PRIVILEGE, "No such privilege. Indicates a specified privilege does not exist.");
   581  			ErrorMessages.Add(STATUS_INVALID_SERVER_STATE, "Invalid server state. Indicates the LSA server is currently disabled.");
   582  			ErrorMessages.Add(STATUS_INTERNAL_DB_ERROR, "Internal database error. The LSA database contains an internal inconsistency.");
   583  			ErrorMessages.Add(STATUS_INSUFFICIENT_RESOURCES, "Insufficient resources. There are not enough system resources (such as memory to allocate buffers) to complete the call.");
   584  			ErrorMessages.Add(STATUS_OBJECT_NAME_NOT_FOUND, "Object name not found. An object in the LSA policy database was not found. The object may have been specified either by SID or by name, depending on its type.");
   585  		}
   586  
   587  		private static void HandleLsaResult(uint returnCode)
   588  		{
   589  			int win32ErrorCode = LsaNtStatusToWinError(returnCode);
   590  
   591  			if( win32ErrorCode == STATUS_SUCCESS)
   592  				return;
   593  
   594  			if( ErrorMessages.ContainsKey(win32ErrorCode) )
   595  			{
   596  				throw new Win32Exception(win32ErrorCode, ErrorMessages[win32ErrorCode]);
   597  			}
   598  
   599  			throw new Win32Exception(win32ErrorCode);
   600  		}
   601  
   602  		public static void RevokePrivileges(string identity, string[] privileges)
   603  		{
   604  			IntPtr sidPtr = GetIdentitySid(identity);
   605  			IntPtr hPolicy = GetLsaPolicyHandle();
   606  
   607  			try
   608  			{
   609  				string[] currentPrivileges = GetPrivileges(identity);
   610  				if (currentPrivileges.Length == 0)
   611  				{
   612  					return;
   613  				}
   614  				LSA_UNICODE_STRING[] lsaPrivileges = StringsToLsaStrings(privileges);
   615  				uint result = LsaRemoveAccountRights(hPolicy, sidPtr, false, lsaPrivileges, (uint)lsaPrivileges.Length);
   616  				HandleLsaResult(result);
   617  			}
   618  			finally
   619  			{
   620  				Marshal.FreeHGlobal(sidPtr);
   621  				uint result = LsaClose(hPolicy);
   622  				HandleLsaResult(result);
   623  			}
   624  
   625  		}
   626  
   627  		private static LSA_UNICODE_STRING[] StringsToLsaStrings(string[] privileges)
   628  		{
   629  			LSA_UNICODE_STRING[] lsaPrivileges = new LSA_UNICODE_STRING[privileges.Length];
   630  			for (int idx = 0; idx < privileges.Length; ++idx)
   631  			{
   632  				lsaPrivileges[idx] = new LSA_UNICODE_STRING(privileges[idx]);
   633  			}
   634  			return lsaPrivileges;
   635  		}
   636  	}
   637  }
   638  "@
   639  Add-Type -TypeDefinition $SourcePolicy -Language CSharp
   640  
   641  function SetAssignPrimaryTokenPrivilege($UserName)
   642  {
   643  	$privilege = "SeAssignPrimaryTokenPrivilege"
   644  	if (!([PSCarbon.Lsa]::GetPrivileges($UserName) -contains $privilege))
   645  	{
   646  		[PSCarbon.Lsa]::GrantPrivileges($UserName, $privilege)
   647  	}
   648  }
   649  
   650  function SetUserLogonAsServiceRights($UserName)
   651  {
   652  	$privilege = "SeServiceLogonRight"
   653  	if (!([PSCarbon.Lsa]::GetPrivileges($UserName) -Contains $privilege))
   654  	{
   655  		[PSCarbon.Lsa]::GrantPrivileges($UserName, $privilege)
   656  	}
   657  }
   658  function Test-RegistryValue {
   659  param ([parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()]$Path,
   660   [parameter(Mandatory=$true)]
   661   [ValidateNotNullOrEmpty()]$Value)
   662  	try {
   663  	Get-ItemProperty -Path $Path | Select-Object -ExpandProperty $Value -ErrorAction Stop | Out-Null
   664  	return $true
   665  	}catch {return $false}
   666  }
   667  $juju_passwd = Get-RandomPassword 20
   668  $juju_passwd += "^"
   669  if (& net users | select-string "jujud") {
   670  	net user "jujud" /DELETE
   671  } 
   672  create-account jujud "Juju Admin user" $juju_passwd
   673  $hostname = hostname
   674  $juju_user = "$hostname\jujud"
   675  SetUserLogonAsServiceRights $juju_user
   676  SetAssignPrimaryTokenPrivilege $juju_user
   677  $path = "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList"
   678  if (Test-RegistryValue -Path $path -Value "jujud") {
   679  	Remove-ItemProperty -Path $path -Name "jujud"
   680  }
   681  if(!(Test-Path $path)){
   682  	New-Item -Path $path -force
   683  }
   684  if(Test-Path "C:\Juju") {
   685  	rm -Recurse -Force "C:\Juju"
   686  }
   687  if(Test-Path "HKLM:\SOFTWARE\juju-core") {
   688  	Remove-Item -Path "HKLM:\SOFTWARE\juju-core" -Recurse -Force
   689  }
   690  New-ItemProperty $path -Name "jujud" -Value 0 -PropertyType "DWord"
   691  $secpasswd = ConvertTo-SecureString $juju_passwd -AsPlainText -Force
   692  $jujuCreds = New-Object System.Management.Automation.PSCredential ($juju_user, $secpasswd)
   693  
   694  mkdir -Force "C:\Juju"
   695  mkdir C:\Juju\tmp
   696  mkdir "C:\Juju\bin"
   697  mkdir "C:\Juju\lib\juju\locks"
   698  setx /m PATH "$env:PATH;C:\Juju\bin\"
   699  $adminsGroup = (New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-544")).Translate([System.Security.Principal.NTAccount])
   700  icacls "C:\Juju" /inheritance:r /grant "${adminsGroup}:(OI)(CI)(F)" /t
   701  icacls "C:\Juju" /inheritance:r /grant "jujud:(OI)(CI)(F)" /t
   702  Set-Content "C:\Juju\lib\juju\nonce.txt" "'FAKE_NONCE'"
   703  $binDir="C:\Juju\lib\juju\tools\1.2.3-win8-amd64"
   704  mkdir 'C:\Juju\log\juju'
   705  mkdir $binDir
   706  $WebClient = New-Object System.Net.WebClient
   707  [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
   708  [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
   709  ExecRetry { TryExecAll @({ $WebClient.DownloadFile('https://state-addr.testing.invalid:54321/deadbeef-0bad-400d-8000-4b1d0d06f00d/tools/1.2.3-win8-amd64', "$binDir\tools.tar.gz"); }) }
   710  $dToolsHash = Get-FileSHA256 -FilePath "$binDir\tools.tar.gz"
   711  $dToolsHash > "$binDir\juju1.2.3-win8-amd64.sha256"
   712  if ($dToolsHash.ToLower() -ne "1234"){ Throw "Tools checksum mismatch"}
   713  GUnZip-File -infile $binDir\tools.tar.gz -outdir $binDir
   714  rm "$binDir\tools.tar*"
   715  Set-Content $binDir\downloaded-tools.txt '{"version":"1.2.3-win8-amd64","url":"https://state-addr.testing.invalid:54321/deadbeef-0bad-400d-8000-4b1d0d06f00d/tools/1.2.3-win8-amd64","sha256":"1234","size":10}'
   716  New-Item -Path 'HKLM:\SOFTWARE\juju-core'
   717  $acl = Get-Acl -Path 'HKLM:\SOFTWARE\juju-core'
   718  $acl.SetAccessRuleProtection($true, $false)
   719  $adminPerm = "$adminsGroup", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow"
   720  $rule = New-Object System.Security.AccessControl.RegistryAccessRule $adminPerm
   721  $acl.AddAccessRule($rule)
   722  $jujudPerm = "jujud", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow"
   723  $rule = New-Object System.Security.AccessControl.RegistryAccessRule $jujudPerm
   724  $acl.AddAccessRule($rule)
   725  Set-Acl -Path 'HKLM:\SOFTWARE\juju-core' -AclObject $acl
   726  New-ItemProperty -Path 'HKLM:\SOFTWARE\juju-core' -Name 'JUJU_DEV_FEATURE_FLAGS'
   727  Set-ItemProperty -Path 'HKLM:\SOFTWARE\juju-core' -Name 'JUJU_DEV_FEATURE_FLAGS' -Value ''
   728  mkdir 'C:\Juju\lib\juju\agents\machine-10'
   729  Set-Content 'C:/Juju/lib/juju/agents/machine-10/agent.conf' @"
   730  # format 2.0
   731  tag: machine-10
   732  datadir: C:/Juju/lib/juju
   733  logdir: C:/Juju/log/juju
   734  metricsspooldir: C:/Juju/lib/juju/metricspool
   735  nonce: FAKE_NONCE
   736  jobs:
   737  - JobHostUnits
   738  upgradedToVersion: 1.2.3
   739  cacert: |
   740    CA CERT
   741    SERVER CERT
   742    -----BEGIN CERTIFICATE-----
   743    MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwHjENMAsGA1UEChMEanVqdTEN
   744    MAsGA1UEAxMEcm9vdDAeFw0xMjExMDgxNjIyMzRaFw0xMzExMDgxNjI3MzRaMBwx
   745    DDAKBgNVBAoTA2htbTEMMAoGA1UEAxMDYW55MFowCwYJKoZIhvcNAQEBA0sAMEgC
   746    QQCACqz6JPwM7nbxAWub+APpnNB7myckWJ6nnsPKi9SipP1hyhfzkp8RGMJ5Uv7y
   747    8CSTtJ8kg/ibka1VV8LvP9tnAgMBAAGjUjBQMA4GA1UdDwEB/wQEAwIAsDAdBgNV
   748    HQ4EFgQU6G1ERaHCgfAv+yoDMFVpDbLOmIQwHwYDVR0jBBgwFoAUP/mfUdwOlHfk
   749    fR+gLQjslxf64w0wCwYJKoZIhvcNAQEFA0EAbn0MaxWVgGYBomeLYfDdb8vCq/5/
   750    G/2iCUQCXsVrBparMLFnor/iKOkJB5n3z3rtu70rFt+DpX6L8uBR3LB3+A==
   751    -----END CERTIFICATE-----
   752  controller: controller-deadbeef-1bad-500d-9000-4b1d0d06f00d
   753  model: model-deadbeef-0bad-400d-8000-4b1d0d06f00d
   754  apiaddresses:
   755  - state-addr.testing.invalid:54321
   756  oldpassword: bletch
   757  values:
   758    AGENT_SERVICE_NAME: jujud-machine-10
   759    PROVIDER_TYPE: dummy
   760  mongoversion: "0.0"
   761  
   762  "@
   763  cmd.exe /C mklink /D C:\Juju\lib\juju\tools\machine-10 1.2.3-win8-amd64
   764  if ($jujuCreds) {
   765    New-Service -Credential $jujuCreds -Name 'jujud-machine-10' -DependsOn Winmgmt -DisplayName 'juju agent for machine-10' '"C:\Juju\lib\juju\tools\machine-10\jujud.exe" machine --data-dir "C:\Juju\lib\juju" --machine-id 10 --debug'
   766  } else {
   767    New-Service -Name 'jujud-machine-10' -DependsOn Winmgmt -DisplayName 'juju agent for machine-10' '"C:\Juju\lib\juju\tools\machine-10\jujud.exe" machine --data-dir "C:\Juju\lib\juju" --machine-id 10 --debug'
   768  }
   769  sc.exe failure 'jujud-machine-10' reset=5 actions=restart/1000
   770  sc.exe failureflag 'jujud-machine-10' 1
   771  Start-Service 'jujud-machine-10'`