github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/cloudconfig/powershell_helpers.go (about)

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