github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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, $maxRetryCount = 10, $retryInterval=2)
    23  {
    24  	$currErrorActionPreference = $ErrorActionPreference
    25  	$ErrorActionPreference = "Continue"
    26  
    27  	$retryCount = 0
    28  	while ($true)
    29  	{
    30  		try
    31  		{
    32  			& $command
    33  			break
    34  		}
    35  		catch [System.Exception]
    36  		{
    37  			$retryCount++
    38  			if ($retryCount -ge $maxRetryCount)
    39  			{
    40  				$ErrorActionPreference = $currErrorActionPreference
    41  				throw
    42  			}
    43  			else
    44  			{
    45  				Write-Error $_.Exception
    46  				Start-Sleep $retryInterval
    47  			}
    48  		}
    49  	}
    50  
    51  	$ErrorActionPreference = $currErrorActionPreference
    52  }
    53  
    54  function create-account ([string]$accountName, [string]$accountDescription, [string]$password) {
    55   $hostname = hostname
    56   $comp = [adsi]"WinNT://$hostname"
    57   $user = $comp.Create("User", $accountName)
    58   $user.SetPassword($password)
    59   $user.SetInfo()
    60   $user.description = $accountDescription
    61   $user.SetInfo()
    62   $User.UserFlags[0] = $User.UserFlags[0] -bor 0x10000
    63   $user.SetInfo()
    64  
    65   # This gets the Administrator group name that is localized on different windows versions. 
    66   # However the SID S-1-5-32-544 is the same on all versions.
    67   $adminGroup = (New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-544")).Translate([System.Security.Principal.NTAccount]).Value.Split("\")[1]
    68  
    69   $objOU = [ADSI]"WinNT://$hostname/$adminGroup,group"
    70   $objOU.add("WinNT://$hostname/$accountName")
    71  }
    72  
    73  $Source = @"
    74  using System;
    75  using System.Text;
    76  using System.Runtime.InteropServices;
    77  
    78  namespace PSCloudbase
    79  {
    80  	public sealed class Win32CryptApi
    81  	{
    82  		public static long CRYPT_SILENT = 0x00000040;
    83  		public static long CRYPT_VERIFYCONTEXT = 0xF0000000;
    84  		public static int PROV_RSA_FULL = 1;
    85  
    86  		[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
    87  		[return : MarshalAs(UnmanagedType.Bool)]
    88  		public static extern bool CryptAcquireContext(ref IntPtr hProv,
    89  													  StringBuilder pszContainer, // Don't use string, as Powershell replaces $null with an empty string
    90  													  StringBuilder pszProvider, // Don't use string, as Powershell replaces $null with an empty string
    91  													  uint dwProvType,
    92  													  uint dwFlags);
    93  
    94  		[DllImport("Advapi32.dll", EntryPoint = "CryptReleaseContext", CharSet = CharSet.Unicode, SetLastError = true)]
    95  		public static extern bool CryptReleaseContext(IntPtr hProv, Int32 dwFlags);
    96  
    97  		[DllImport("advapi32.dll", SetLastError=true)]
    98  		public static extern bool CryptGenRandom(IntPtr hProv, uint dwLen, byte[] pbBuffer);
    99  
   100  		[DllImport("Kernel32.dll")]
   101  		public static extern uint GetLastError();
   102  	}
   103  }
   104  "@
   105  
   106  Add-Type -TypeDefinition $Source -Language CSharp
   107  
   108  function Get-RandomPassword
   109  {
   110  	[CmdletBinding()]
   111  	param
   112  	(
   113  		[parameter(Mandatory=$true)]
   114  		[int]$Length
   115  	)
   116  	process
   117  	{
   118  		$hProvider = 0
   119  		try
   120  		{
   121  			if(![PSCloudbase.Win32CryptApi]::CryptAcquireContext([ref]$hProvider, $null, $null,
   122  																 [PSCloudbase.Win32CryptApi]::PROV_RSA_FULL,
   123  																 ([PSCloudbase.Win32CryptApi]::CRYPT_VERIFYCONTEXT -bor
   124  																  [PSCloudbase.Win32CryptApi]::CRYPT_SILENT)))
   125  			{
   126  				throw "CryptAcquireContext failed with error: 0x" + "{0:X0}" -f [PSCloudbase.Win32CryptApi]::GetLastError()
   127  			}
   128  
   129  			$buffer = New-Object byte[] $Length
   130  			if(![PSCloudbase.Win32CryptApi]::CryptGenRandom($hProvider, $Length, $buffer))
   131  			{
   132  				throw "CryptGenRandom failed with error: 0x" + "{0:X0}" -f [PSCloudbase.Win32CryptApi]::GetLastError()
   133  			}
   134  
   135  			$buffer | ForEach-Object { $password += "{0:X0}" -f $_ }
   136  			return $password
   137  		}
   138  		finally
   139  		{
   140  			if($hProvider)
   141  			{
   142  				$retVal = [PSCloudbase.Win32CryptApi]::CryptReleaseContext($hProvider, 0)
   143  			}
   144  		}
   145  	}
   146  }
   147  
   148  $SourcePolicy = @"
   149  /*
   150  Original sources available at: https://bitbucket.org/splatteredbits/carbon
   151  */
   152  
   153  using System;
   154  using System.Collections.Generic;
   155  using System.ComponentModel;
   156  using System.Runtime.InteropServices;
   157  using System.Security.Principal;
   158  using System.Text;
   159  
   160  namespace PSCarbon
   161  {
   162  	public sealed class Lsa
   163  	{
   164  		// ReSharper disable InconsistentNaming
   165  		[StructLayout(LayoutKind.Sequential)]
   166  		internal struct LSA_UNICODE_STRING
   167  		{
   168  			internal LSA_UNICODE_STRING(string inputString)
   169  			{
   170  				if (inputString == null)
   171  				{
   172  					Buffer = IntPtr.Zero;
   173  					Length = 0;
   174  					MaximumLength = 0;
   175  				}
   176  				else
   177  				{
   178  					Buffer = Marshal.StringToHGlobalAuto(inputString);
   179  					Length = (ushort)(inputString.Length * UnicodeEncoding.CharSize);
   180  					MaximumLength = (ushort)((inputString.Length + 1) * UnicodeEncoding.CharSize);
   181  				}
   182  			}
   183  
   184  			internal ushort Length;
   185  			internal ushort MaximumLength;
   186  			internal IntPtr Buffer;
   187  		}
   188  
   189  		[StructLayout(LayoutKind.Sequential)]
   190  		internal struct LSA_OBJECT_ATTRIBUTES
   191  		{
   192  			internal uint Length;
   193  			internal IntPtr RootDirectory;
   194  			internal LSA_UNICODE_STRING ObjectName;
   195  			internal uint Attributes;
   196  			internal IntPtr SecurityDescriptor;
   197  			internal IntPtr SecurityQualityOfService;
   198  		}
   199  
   200  		[StructLayout(LayoutKind.Sequential)]
   201  		public struct LUID
   202  		{
   203  			public uint LowPart;
   204  			public int HighPart;
   205  		}
   206  
   207  		// ReSharper disable UnusedMember.Local
   208  		private const uint POLICY_VIEW_LOCAL_INFORMATION = 0x00000001;
   209  		private const uint POLICY_VIEW_AUDIT_INFORMATION = 0x00000002;
   210  		private const uint POLICY_GET_PRIVATE_INFORMATION = 0x00000004;
   211  		private const uint POLICY_TRUST_ADMIN = 0x00000008;
   212  		private const uint POLICY_CREATE_ACCOUNT = 0x00000010;
   213  		private const uint POLICY_CREATE_SECRET = 0x00000014;
   214  		private const uint POLICY_CREATE_PRIVILEGE = 0x00000040;
   215  		private const uint POLICY_SET_DEFAULT_QUOTA_LIMITS = 0x00000080;
   216  		private const uint POLICY_SET_AUDIT_REQUIREMENTS = 0x00000100;
   217  		private const uint POLICY_AUDIT_LOG_ADMIN = 0x00000200;
   218  		private const uint POLICY_SERVER_ADMIN = 0x00000400;
   219  		private const uint POLICY_LOOKUP_NAMES = 0x00000800;
   220  		private const uint POLICY_NOTIFICATION = 0x00001000;
   221  		// ReSharper restore UnusedMember.Local
   222  
   223  		[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
   224  		public static extern bool LookupPrivilegeValue(
   225  			[MarshalAs(UnmanagedType.LPTStr)] string lpSystemName,
   226  			[MarshalAs(UnmanagedType.LPTStr)] string lpName,
   227  			out LUID lpLuid);
   228  
   229  		[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
   230  		private static extern uint LsaAddAccountRights(
   231  			IntPtr PolicyHandle,
   232  			IntPtr AccountSid,
   233  			LSA_UNICODE_STRING[] UserRights,
   234  			uint CountOfRights);
   235  
   236  		[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = false)]
   237  		private static extern uint LsaClose(IntPtr ObjectHandle);
   238  
   239  		[DllImport("advapi32.dll", SetLastError = true)]
   240  		private static extern uint LsaEnumerateAccountRights(IntPtr PolicyHandle,
   241  			IntPtr AccountSid,
   242  			out IntPtr UserRights,
   243  			out uint CountOfRights
   244  			);
   245  
   246  		[DllImport("advapi32.dll", SetLastError = true)]
   247  		private static extern uint LsaFreeMemory(IntPtr pBuffer);
   248  
   249  		[DllImport("advapi32.dll")]
   250  		private static extern int LsaNtStatusToWinError(long status);
   251  
   252  		[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
   253  		private static extern uint LsaOpenPolicy(ref LSA_UNICODE_STRING SystemName, ref LSA_OBJECT_ATTRIBUTES ObjectAttributes, uint DesiredAccess, out IntPtr PolicyHandle );
   254  
   255  		[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
   256  		static extern uint LsaRemoveAccountRights(
   257  			IntPtr PolicyHandle,
   258  			IntPtr AccountSid,
   259  			[MarshalAs(UnmanagedType.U1)]
   260  			bool AllRights,
   261  			LSA_UNICODE_STRING[] UserRights,
   262  			uint CountOfRights);
   263  		// ReSharper restore InconsistentNaming
   264  
   265  		private static IntPtr GetIdentitySid(string identity)
   266  		{
   267  			var sid =
   268  				new NTAccount(identity).Translate(typeof (SecurityIdentifier)) as SecurityIdentifier;
   269  			if (sid == null)
   270  			{
   271  				throw new ArgumentException(string.Format("Account {0} not found.", identity));
   272  			}
   273  			var sidBytes = new byte[sid.BinaryLength];
   274  			sid.GetBinaryForm(sidBytes, 0);
   275  			var sidPtr = Marshal.AllocHGlobal(sidBytes.Length);
   276  			Marshal.Copy(sidBytes, 0, sidPtr, sidBytes.Length);
   277  			return sidPtr;
   278  		}
   279  
   280  		private static IntPtr GetLsaPolicyHandle()
   281  		{
   282  			var computerName = Environment.MachineName;
   283  			IntPtr hPolicy;
   284  			var objectAttributes = new LSA_OBJECT_ATTRIBUTES
   285  			{
   286  				Length = 0,
   287  				RootDirectory = IntPtr.Zero,
   288  				Attributes = 0,
   289  				SecurityDescriptor = IntPtr.Zero,
   290  				SecurityQualityOfService = IntPtr.Zero
   291  			};
   292  
   293  			const uint ACCESS_MASK = POLICY_CREATE_SECRET | POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION;
   294  			var machineNameLsa = new LSA_UNICODE_STRING(computerName);
   295  			var result = LsaOpenPolicy(ref machineNameLsa, ref objectAttributes, ACCESS_MASK, out hPolicy);
   296  			HandleLsaResult(result);
   297  			return hPolicy;
   298  		}
   299  
   300  		public static string[] GetPrivileges(string identity)
   301  		{
   302  			var sidPtr = GetIdentitySid(identity);
   303  			var hPolicy = GetLsaPolicyHandle();
   304  			var rightsPtr = IntPtr.Zero;
   305  
   306  			try
   307  			{
   308  
   309  				var privileges = new List<string>();
   310  
   311  				uint rightsCount;
   312  				var result = LsaEnumerateAccountRights(hPolicy, sidPtr, out rightsPtr, out rightsCount);
   313  				var win32ErrorCode = LsaNtStatusToWinError(result);
   314  				// the user has no privileges
   315  				if( win32ErrorCode == STATUS_OBJECT_NAME_NOT_FOUND )
   316  				{
   317  					return new string[0];
   318  				}
   319  				HandleLsaResult(result);
   320  
   321  				var myLsaus = new LSA_UNICODE_STRING();
   322  				for (ulong i = 0; i < rightsCount; i++)
   323  				{
   324  					var itemAddr = new IntPtr(rightsPtr.ToInt64() + (long) (i*(ulong) Marshal.SizeOf(myLsaus)));
   325  					myLsaus = (LSA_UNICODE_STRING) Marshal.PtrToStructure(itemAddr, myLsaus.GetType());
   326  					var cvt = new char[myLsaus.Length/UnicodeEncoding.CharSize];
   327  					Marshal.Copy(myLsaus.Buffer, cvt, 0, myLsaus.Length/UnicodeEncoding.CharSize);
   328  					var thisRight = new string(cvt);
   329  					privileges.Add(thisRight);
   330  				}
   331  				return privileges.ToArray();
   332  			}
   333  			finally
   334  			{
   335  				Marshal.FreeHGlobal(sidPtr);
   336  				var result = LsaClose(hPolicy);
   337  				HandleLsaResult(result);
   338  				result = LsaFreeMemory(rightsPtr);
   339  				HandleLsaResult(result);
   340  			}
   341  		}
   342  
   343  		public static void GrantPrivileges(string identity, string[] privileges)
   344  		{
   345  			var sidPtr = GetIdentitySid(identity);
   346  			var hPolicy = GetLsaPolicyHandle();
   347  
   348  			try
   349  			{
   350  				var lsaPrivileges = StringsToLsaStrings(privileges);
   351  				var result = LsaAddAccountRights(hPolicy, sidPtr, lsaPrivileges, (uint)lsaPrivileges.Length);
   352  				HandleLsaResult(result);
   353  			}
   354  			finally
   355  			{
   356  				Marshal.FreeHGlobal(sidPtr);
   357  				var result = LsaClose(hPolicy);
   358  				HandleLsaResult(result);
   359  			}
   360  		}
   361  
   362  		const int STATUS_SUCCESS = 0x0;
   363  		const int STATUS_OBJECT_NAME_NOT_FOUND = 0x00000002;
   364  		const int STATUS_ACCESS_DENIED = 0x00000005;
   365  		const int STATUS_INVALID_HANDLE = 0x00000006;
   366  		const int STATUS_UNSUCCESSFUL = 0x0000001F;
   367  		const int STATUS_INVALID_PARAMETER = 0x00000057;
   368  		const int STATUS_NO_SUCH_PRIVILEGE = 0x00000521;
   369  		const int STATUS_INVALID_SERVER_STATE = 0x00000548;
   370  		const int STATUS_INTERNAL_DB_ERROR = 0x00000567;
   371  		const int STATUS_INSUFFICIENT_RESOURCES = 0x000005AA;
   372  
   373  		private static Dictionary<int, string> ErrorMessages = new Dictionary<int, string>
   374  									{
   375  										{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."},
   376  										{STATUS_ACCESS_DENIED, "Access denied. Caller does not have the appropriate access to complete the operation."},
   377  										{STATUS_INVALID_HANDLE, "Invalid handle. Indicates an object or RPC handle is not valid in the context used."},
   378  										{STATUS_UNSUCCESSFUL, "Unsuccessful. Generic failure, such as RPC connection failure."},
   379  										{STATUS_INVALID_PARAMETER, "Invalid parameter. One of the parameters is not valid."},
   380  										{STATUS_NO_SUCH_PRIVILEGE, "No such privilege. Indicates a specified privilege does not exist."},
   381  										{STATUS_INVALID_SERVER_STATE, "Invalid server state. Indicates the LSA server is currently disabled."},
   382  										{STATUS_INTERNAL_DB_ERROR, "Internal database error. The LSA database contains an internal inconsistency."},
   383  										{STATUS_INSUFFICIENT_RESOURCES, "Insufficient resources. There are not enough system resources (such as memory to allocate buffers) to complete the call."}
   384  									};
   385  
   386  		private static void HandleLsaResult(uint returnCode)
   387  		{
   388  			var win32ErrorCode = LsaNtStatusToWinError(returnCode);
   389  
   390  			if( win32ErrorCode == STATUS_SUCCESS)
   391  				return;
   392  
   393  			if( ErrorMessages.ContainsKey(win32ErrorCode) )
   394  			{
   395  				throw new Win32Exception(win32ErrorCode, ErrorMessages[win32ErrorCode]);
   396  			}
   397  
   398  			throw new Win32Exception(win32ErrorCode);
   399  		}
   400  
   401  		public static void RevokePrivileges(string identity, string[] privileges)
   402  		{
   403  			var sidPtr = GetIdentitySid(identity);
   404  			var hPolicy = GetLsaPolicyHandle();
   405  
   406  			try
   407  			{
   408  				var currentPrivileges = GetPrivileges(identity);
   409  				if (currentPrivileges.Length == 0)
   410  				{
   411  					return;
   412  				}
   413  				var lsaPrivileges = StringsToLsaStrings(privileges);
   414  				var result = LsaRemoveAccountRights(hPolicy, sidPtr, false, lsaPrivileges, (uint)lsaPrivileges.Length);
   415  				HandleLsaResult(result);
   416  			}
   417  			finally
   418  			{
   419  				Marshal.FreeHGlobal(sidPtr);
   420  				var result = LsaClose(hPolicy);
   421  				HandleLsaResult(result);
   422  			}
   423  
   424  		}
   425  
   426  		private static LSA_UNICODE_STRING[] StringsToLsaStrings(string[] privileges)
   427  		{
   428  			var lsaPrivileges = new LSA_UNICODE_STRING[privileges.Length];
   429  			for (var idx = 0; idx < privileges.Length; ++idx)
   430  			{
   431  				lsaPrivileges[idx] = new LSA_UNICODE_STRING(privileges[idx]);
   432  			}
   433  			return lsaPrivileges;
   434  		}
   435  	}
   436  }
   437  "@
   438  
   439  Add-Type -TypeDefinition $SourcePolicy -Language CSharp
   440  
   441  function SetAssignPrimaryTokenPrivilege($UserName)
   442  {
   443  	$privilege = "SeAssignPrimaryTokenPrivilege"
   444  	if (![PSCarbon.Lsa]::GetPrivileges($UserName).Contains($privilege))
   445  	{
   446  		[PSCarbon.Lsa]::GrantPrivileges($UserName, $privilege)
   447  	}
   448  }
   449  
   450  function SetUserLogonAsServiceRights($UserName)
   451  {
   452  	$privilege = "SeServiceLogonRight"
   453  	if (![PSCarbon.Lsa]::GetPrivileges($UserName).Contains($privilege))
   454  	{
   455  		[PSCarbon.Lsa]::GrantPrivileges($UserName, $privilege)
   456  	}
   457  }
   458  
   459  $Source = @"
   460  using System;
   461  using System.Collections.Generic;
   462  using System.Diagnostics;
   463  using System.IO;
   464  using System.Net;
   465  using System.Text;
   466  
   467  namespace Tarer
   468  {
   469  	public enum EntryType : byte
   470  	{
   471  		File = 0,
   472  		FileObsolete = 0x30,
   473  		HardLink = 0x31,
   474  		SymLink = 0x32,
   475  		CharDevice = 0x33,
   476  		BlockDevice = 0x34,
   477  		Directory = 0x35,
   478  		Fifo = 0x36,
   479  	}
   480  
   481  	public interface ITarHeader
   482  	{
   483  		string FileName { get; set; }
   484  		long SizeInBytes { get; set; }
   485  		DateTime LastModification { get; set; }
   486  		int HeaderSize { get; }
   487  		EntryType EntryType { get; set; }
   488  	}
   489  
   490  	public class Tar
   491  	{
   492  		private byte[] dataBuffer = new byte[512];
   493  		private UsTarHeader header;
   494  		private Stream inStream;
   495  		private long remainingBytesInFile;
   496  
   497  		public Tar(Stream tarredData) {
   498  			inStream = tarredData;
   499  			header = new UsTarHeader();
   500  		}
   501  
   502  		public ITarHeader FileInfo
   503  		{
   504  			get { return header; }
   505  		}
   506  
   507  		public void ReadToEnd(string destDirectory)
   508  		{
   509  			while (MoveNext())
   510  			{
   511  				string fileNameFromArchive = FileInfo.FileName;
   512  				string totalPath = destDirectory + Path.DirectorySeparatorChar + fileNameFromArchive;
   513  				if(UsTarHeader.IsPathSeparator(fileNameFromArchive[fileNameFromArchive.Length -1]) || FileInfo.EntryType == EntryType.Directory)
   514  				{
   515  					Directory.CreateDirectory(totalPath);
   516  					continue;
   517  				}
   518  				string fileName = Path.GetFileName(totalPath);
   519  				string directory = totalPath.Remove(totalPath.Length - fileName.Length);
   520  				Directory.CreateDirectory(directory);
   521  				using (FileStream file = File.Create(totalPath))
   522  				{
   523  					Read(file);
   524  				}
   525  			}
   526  		}
   527  
   528  		public void Read(Stream dataDestination)
   529  		{
   530  			int readBytes;
   531  			byte[] read;
   532  			while ((readBytes = Read(out read)) != -1)
   533  			{
   534  				dataDestination.Write(read, 0, readBytes);
   535  			}
   536  		}
   537  
   538  		protected int Read(out byte[] buffer)
   539  		{
   540  			if(remainingBytesInFile == 0)
   541  			{
   542  				buffer = null;
   543  				return -1;
   544  			}
   545  			int align512 = -1;
   546  			long toRead = remainingBytesInFile - 512;
   547  
   548  			if (toRead > 0)
   549  			{
   550  				toRead = 512;
   551  			}
   552  			else
   553  			{
   554  				align512 = 512 - (int)remainingBytesInFile;
   555  				toRead = remainingBytesInFile;
   556  			}
   557  
   558  			int bytesRead = 0;
   559  			long bytesRemainingToRead = toRead;
   560  			while (bytesRead < toRead && bytesRemainingToRead > 0)
   561  			{
   562  				bytesRead = inStream.Read(dataBuffer, (int)(toRead-bytesRemainingToRead), (int)bytesRemainingToRead);
   563  				bytesRemainingToRead -= bytesRead;
   564  				remainingBytesInFile -= bytesRead;
   565  			}
   566  
   567  			if(inStream.CanSeek && align512 > 0)
   568  			{
   569  				inStream.Seek(align512, SeekOrigin.Current);
   570  			}
   571  			else
   572  			{
   573  				while(align512 > 0)
   574  				{
   575  					inStream.ReadByte();
   576  					--align512;
   577  				}
   578  			}
   579  
   580  			buffer = dataBuffer;
   581  			return bytesRead;
   582  		}
   583  
   584  		private static bool IsEmpty(IEnumerable<byte> buffer)
   585  		{
   586  			foreach(byte b in buffer)
   587  			{
   588  				if (b != 0)
   589  				{
   590  					return false;
   591  				}
   592  			}
   593  			return true;
   594  		}
   595  
   596  		public bool MoveNext()
   597  		{
   598  			byte[] bytes = header.GetBytes();
   599  			int headerRead;
   600  			int bytesRemaining = header.HeaderSize;
   601  			while (bytesRemaining > 0)
   602  			{
   603  				headerRead = inStream.Read(bytes, header.HeaderSize - bytesRemaining, bytesRemaining);
   604  				bytesRemaining -= headerRead;
   605  				if (headerRead <= 0 && bytesRemaining > 0)
   606  				{
   607  					throw new Exception("Error reading tar header. Header size invalid");
   608  				}
   609  			}
   610  
   611  			if(IsEmpty(bytes))
   612  			{
   613  				bytesRemaining = header.HeaderSize;
   614  				while (bytesRemaining > 0)
   615  				{
   616  					headerRead = inStream.Read(bytes, header.HeaderSize - bytesRemaining, bytesRemaining);
   617  					bytesRemaining -= headerRead;
   618  					if (headerRead <= 0 && bytesRemaining > 0)
   619  					{
   620  						throw new Exception("Broken archive");
   621  					}
   622  				}
   623  				if (bytesRemaining == 0 && IsEmpty(bytes))
   624  				{
   625  					return false;
   626  				}
   627  				throw new Exception("Error occured: expected end of archive");
   628  			}
   629  
   630  			if (!header.UpdateHeaderFromBytes())
   631  			{
   632  				throw new Exception("Checksum check failed");
   633  			}
   634  
   635  			remainingBytesInFile = header.SizeInBytes;
   636  			return true;
   637  		}
   638  	}
   639  
   640  	internal class TarHeader : ITarHeader
   641  	{
   642  		private byte[] buffer = new byte[512];
   643  		private long headerChecksum;
   644  
   645  		private string fileName;
   646  		protected DateTime dateTime1970 = new DateTime(1970, 1, 1, 0, 0, 0);
   647  		public EntryType EntryType { get; set; }
   648  		private static byte[] spaces = Encoding.ASCII.GetBytes("        ");
   649  
   650  		public virtual string FileName
   651  		{
   652  			get { return fileName.Replace("\0",string.Empty); }
   653  			set { fileName = value; }
   654  		}
   655  
   656  		public long SizeInBytes { get; set; }
   657  
   658  		public string SizeString { get { return Convert.ToString(SizeInBytes, 8).PadLeft(11, '0'); } }
   659  
   660  		public DateTime LastModification { get; set; }
   661  
   662  		public virtual int HeaderSize { get { return 512; } }
   663  
   664  		public byte[] GetBytes()
   665  		{
   666  			return buffer;
   667  		}
   668  
   669  		public virtual bool UpdateHeaderFromBytes()
   670  		{
   671  			FileName = Encoding.UTF8.GetString(buffer, 0, 100);
   672  
   673  			EntryType = (EntryType)buffer[156];
   674  
   675  			if((buffer[124] & 0x80) == 0x80) // if size in binary
   676  			{
   677  				long sizeBigEndian = BitConverter.ToInt64(buffer,0x80);
   678  				SizeInBytes = IPAddress.NetworkToHostOrder(sizeBigEndian);
   679  			}
   680  			else
   681  			{
   682  				SizeInBytes = Convert.ToInt64(Encoding.ASCII.GetString(buffer, 124, 11).Trim(), 8);
   683  			}
   684  			long unixTimeStamp = Convert.ToInt64(Encoding.ASCII.GetString(buffer,136,11).Trim(),8);
   685  			LastModification = dateTime1970.AddSeconds(unixTimeStamp);
   686  
   687  			var storedChecksum = Convert.ToInt64(Encoding.ASCII.GetString(buffer,148,6).Trim(), 8);
   688  			RecalculateChecksum(buffer);
   689  			if (storedChecksum == headerChecksum)
   690  			{
   691  				return true;
   692  			}
   693  
   694  			RecalculateAltChecksum(buffer);
   695  			return storedChecksum == headerChecksum;
   696  		}
   697  
   698  		private void RecalculateAltChecksum(byte[] buf)
   699  		{
   700  			spaces.CopyTo(buf, 148);
   701  			headerChecksum = 0;
   702  			foreach(byte b in buf)
   703  			{
   704  				if((b & 0x80) == 0x80)
   705  				{
   706  					headerChecksum -= b ^ 0x80;
   707  				}
   708  				else
   709  				{
   710  					headerChecksum += b;
   711  				}
   712  			}
   713  		}
   714  
   715  		protected virtual void RecalculateChecksum(byte[] buf)
   716  		{
   717  			// Set default value for checksum. That is 8 spaces.
   718  			spaces.CopyTo(buf, 148);
   719  			// Calculate checksum
   720  			headerChecksum = 0;
   721  			foreach (byte b in buf)
   722  			{
   723  				headerChecksum += b;
   724  			}
   725  		}
   726  	}
   727  	internal class UsTarHeader : TarHeader
   728  	{
   729  		private const string magic = "ustar";
   730  		private const string version = "  ";
   731  
   732  		private string namePrefix = string.Empty;
   733  
   734  		public override string FileName
   735  		{
   736  			get { return namePrefix.Replace("\0", string.Empty) + base.FileName.Replace("\0", string.Empty); }
   737  			set
   738  			{
   739  				if (value.Length > 255)
   740  				{
   741  					throw new Exception("UsTar fileName can not be longer than 255 chars");
   742  				}
   743  				if (value.Length > 100)
   744  				{
   745  				int position = value.Length - 100;
   746  				while (!IsPathSeparator(value[position]))
   747  				{
   748  					++position;
   749  					if (position == value.Length)
   750  					{
   751  						break;
   752  					}
   753  				}
   754  				if (position == value.Length)
   755  				{
   756  					position = value.Length - 100;
   757  				}
   758  				namePrefix = value.Substring(0, position);
   759  				base.FileName = value.Substring(position, value.Length - position);
   760  				}
   761  				else
   762  				{
   763  					base.FileName = value;
   764  				}
   765  			}
   766  		}
   767  
   768  		public override bool UpdateHeaderFromBytes()
   769  		{
   770  			byte[] bytes = GetBytes();
   771  			namePrefix = Encoding.UTF8.GetString(bytes, 347, 157);
   772  			return base.UpdateHeaderFromBytes();
   773  		}
   774  
   775  		internal static bool IsPathSeparator(char ch)
   776  		{
   777  			return (ch == '\\' || ch == '/' || ch == '|');
   778  		}
   779  	}
   780  }
   781  "@
   782  
   783  Add-Type -TypeDefinition $Source -Language CSharp
   784  
   785  Function GUnZip-File{
   786  	Param(
   787  		$infile,
   788  		$outdir
   789  		)
   790  
   791  	$input = New-Object System.IO.FileStream $inFile, ([IO.FileMode]::Open), ([IO.FileAccess]::Read), ([IO.FileShare]::Read)
   792  	$tempFile = "$env:TEMP\jujud.tar"
   793  	$tempOut = New-Object System.IO.FileStream $tempFile, ([IO.FileMode]::Create), ([IO.FileAccess]::Write), ([IO.FileShare]::None)
   794  	$gzipStream = New-Object System.IO.Compression.GzipStream $input, ([IO.Compression.CompressionMode]::Decompress)
   795  
   796  	$buffer = New-Object byte[](1024)
   797  	while($true){
   798  		$read = $gzipstream.Read($buffer, 0, 1024)
   799  		if ($read -le 0){break}
   800  		$tempOut.Write($buffer, 0, $read)
   801  	}
   802  	$gzipStream.Close()
   803  	$tempOut.Close()
   804  	$input.Close()
   805  
   806  	$in = New-Object System.IO.FileStream $tempFile, ([IO.FileMode]::Open), ([IO.FileAccess]::Read), ([IO.FileShare]::Read)
   807  	$tar = New-Object Tarer.Tar($in)
   808  	$tar.ReadToEnd($outdir)
   809  	$in.Close()
   810  	rm $tempFile
   811  }
   812  
   813  Function Get-FileSHA256{
   814  	Param(
   815  		$FilePath
   816  	)
   817  	$hash = [Security.Cryptography.HashAlgorithm]::Create( "SHA256" )
   818  	$stream = ([IO.StreamReader]$FilePath).BaseStream
   819  	$res = -join ($hash.ComputeHash($stream) | ForEach { "{0:x2}" -f $_ })
   820  	$stream.Close()
   821  	return $res
   822  }
   823  
   824  $juju_passwd = Get-RandomPassword 20
   825  $juju_passwd += "^"
   826  create-account jujud "Juju Admin user" $juju_passwd
   827  $hostname = hostname
   828  $juju_user = "$hostname\jujud"
   829  
   830  SetUserLogonAsServiceRights $juju_user
   831  SetAssignPrimaryTokenPrivilege $juju_user
   832  
   833  $path = "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList"
   834  if(!(Test-Path $path)){
   835  	New-Item -Path $path -force
   836  }
   837  New-ItemProperty $path -Name "jujud" -Value 0 -PropertyType "DWord"
   838  
   839  $secpasswd = ConvertTo-SecureString $juju_passwd -AsPlainText -Force
   840  $jujuCreds = New-Object System.Management.Automation.PSCredential ($juju_user, $secpasswd)
   841  
   842  
   843  mkdir -Force "C:\Juju"
   844  mkdir C:\Juju\tmp
   845  mkdir "C:\Juju\bin"
   846  mkdir "C:\Juju\lib\juju\locks"
   847  $adminsGroup = (New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-544")).Translate([System.Security.Principal.NTAccount])
   848  $acl = Get-Acl -Path 'C:\Juju'
   849  $acl.SetAccessRuleProtection($true, $false)
   850  $adminPerm = "$adminsGroup", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow"
   851  $jujudPerm = "jujud", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow"
   852  $rule = New-Object System.Security.AccessControl.FileSystemAccessRule $adminPerm
   853  $acl.AddAccessRule($rule)
   854  $rule = New-Object System.Security.AccessControl.FileSystemAccessRule $jujudPerm
   855  $acl.AddAccessRule($rule)
   856  Set-Acl -Path 'C:\Juju' -AclObject $acl
   857  setx /m PATH "$env:PATH;C:\Juju\bin\"
   858  Set-Content "C:\Juju\lib\juju\nonce.txt" "'FAKE_NONCE'"
   859  $binDir="C:\Juju\lib\juju\tools\1.2.3-win8-amd64"
   860  mkdir 'C:\Juju\log\juju'
   861  mkdir $binDir
   862  $WebClient = New-Object System.Net.WebClient
   863  [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
   864  ExecRetry { $WebClient.DownloadFile('http://foo.com/tools/released/juju1.2.3-win8-amd64.tgz', "$binDir\tools.tar.gz") }
   865  $dToolsHash = Get-FileSHA256 -FilePath "$binDir\tools.tar.gz"
   866  $dToolsHash > "$binDir\juju1.2.3-win8-amd64.sha256"
   867  if ($dToolsHash.ToLower() -ne "1234"){ Throw "Tools checksum mismatch"}
   868  GUnZip-File -infile $binDir\tools.tar.gz -outdir $binDir
   869  rm "$binDir\tools.tar*"
   870  Set-Content $binDir\downloaded-tools.txt '{"version":"1.2.3-win8-amd64","url":"http://foo.com/tools/released/juju1.2.3-win8-amd64.tgz","sha256":"1234","size":10}'
   871  New-Item -Path 'HKLM:\SOFTWARE\juju-core'
   872  $acl = Get-Acl -Path 'HKLM:\SOFTWARE\juju-core'
   873  $acl.SetAccessRuleProtection($true, $false)
   874  $adminPerm = "$adminsGroup", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow"
   875  $jujudPerm = "jujud", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow"
   876  $rule = New-Object System.Security.AccessControl.RegistryAccessRule $adminPerm
   877  $acl.AddAccessRule($rule)
   878  $rule = New-Object System.Security.AccessControl.RegistryAccessRule $jujudPerm
   879  $acl.AddAccessRule($rule)
   880  Set-Acl -Path 'HKLM:\SOFTWARE\juju-core' -AclObject $acl
   881  New-ItemProperty -Path 'HKLM:\SOFTWARE\juju-core' -Name 'JUJU_DEV_FEATURE_FLAGS'
   882  Set-ItemProperty -Path 'HKLM:\SOFTWARE\juju-core' -Name 'JUJU_DEV_FEATURE_FLAGS' -Value ''
   883  mkdir 'C:\Juju\lib\juju\agents\machine-10'
   884  Set-Content 'C:/Juju/lib/juju/agents/machine-10/agent.conf' @"
   885  # format 1.18
   886  tag: machine-10
   887  datadir: C:/Juju/lib/juju
   888  logdir: C:/Juju/log/juju
   889  metricsspooldir: C:/Juju/lib/juju/metricspool
   890  nonce: FAKE_NONCE
   891  jobs:
   892  - JobHostUnits
   893  upgradedToVersion: 1.2.3
   894  cacert: |
   895    CA CERT
   896    SERVER CERT
   897    -----BEGIN CERTIFICATE-----
   898    MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwHjENMAsGA1UEChMEanVqdTEN
   899    MAsGA1UEAxMEcm9vdDAeFw0xMjExMDgxNjIyMzRaFw0xMzExMDgxNjI3MzRaMBwx
   900    DDAKBgNVBAoTA2htbTEMMAoGA1UEAxMDYW55MFowCwYJKoZIhvcNAQEBA0sAMEgC
   901    QQCACqz6JPwM7nbxAWub+APpnNB7myckWJ6nnsPKi9SipP1hyhfzkp8RGMJ5Uv7y
   902    8CSTtJ8kg/ibka1VV8LvP9tnAgMBAAGjUjBQMA4GA1UdDwEB/wQEAwIAsDAdBgNV
   903    HQ4EFgQU6G1ERaHCgfAv+yoDMFVpDbLOmIQwHwYDVR0jBBgwFoAUP/mfUdwOlHfk
   904    fR+gLQjslxf64w0wCwYJKoZIhvcNAQEFA0EAbn0MaxWVgGYBomeLYfDdb8vCq/5/
   905    G/2iCUQCXsVrBparMLFnor/iKOkJB5n3z3rtu70rFt+DpX6L8uBR3LB3+A==
   906    -----END CERTIFICATE-----
   907  stateaddresses:
   908  - state-addr.testing.invalid:12345
   909  environment: environment-deadbeef-0bad-400d-8000-4b1d0d06f00d
   910  apiaddresses:
   911  - state-addr.testing.invalid:54321
   912  oldpassword: arble
   913  values:
   914    AGENT_SERVICE_NAME: jujud-machine-10
   915    PROVIDER_TYPE: dummy
   916  
   917  "@
   918  cmd.exe /C mklink /D C:\Juju\lib\juju\tools\machine-10 1.2.3-win8-amd64
   919  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'
   920  sc.exe failure 'jujud-machine-10' reset=5 actions=restart/1000
   921  sc.exe failureflag 'jujud-machine-10' 1
   922  Start-Service 'jujud-machine-10'`