Sunday, July 15, 2012

Supporting Wildcard Pattern Matching in Script CMDLETS

One of the cool features about PowerShell is its support for POSIX style patterns in many of the pre-installed cmdlets. For example the following command will get all items under the C root that have the second, third and forth characters “rog”:

Outputs:

    Directory: C:\


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d-r--          6/7/2012  11:30 PM            Program Files
d-r--         6/26/2012   1:50 PM            Program Files (x86)

The following table shows a reference of the supported POSIX style matching patterns:

Wildcard character

Description

Example

Matches

Does not match

*

Matches zero or more characters, starting at the specified position

a*

A, ag, Apple

 

?

Matches any character at the specified position

?n

An, in, on

ran

[ ]

Matches a range of characters

[a-l]ook

book, cook, look

took

[ ]

Matches the specified characters

[bc]ook

book, cook

look

So how would one go about adding support for these types of patterns in a script CMDLET? In order to do this you’ll need to work with two .NET types:

  1. System.Management.Automation.WildcardPattern
  2. System.Management.Automation.WildcardOptions

First define your options (Compile, CultureInvariant, IgnoreCase or None). Then use New-Object to create a WildcardPattern instance using the pattern you want to match against as the first argument and the WildcardOptions object as the second argument.

Here is a an example function:

Tuesday, February 21, 2012

Get System DEP Setting with PowerShell

The data execution policy can be obtained using bcdedit /enum however that command has a lot of output. I needed to get the system DEP policy setting in PowerShell so I found a Win32 function called GetSystemDEPPolicy. So in order to call this from PowerShell we'll need to use p/invoke again. Here's how:

function Get-SystemDEPPolicy {
Add-Type -Namespace Win32 -Name DEP -MemberDefinition @"
[DllImport("kernel32.dll", CharSet=CharSet.Auto, ExactSpelling=true)]
public static extern int GetSystemDEPPolicy();

public static int GetCurrentSystemDEPPolicy() {
return GetSystemDEPPolicy();
}
"@
$policyCode = [Win32.DEP]::GetCurrentSystemDEPPolicy()
switch ($policyCode) {
0 {Write-Output 'AlwaysOff'}
1 {Write-Output 'AlwaysOn'}
2 {Write-Output 'OptIn'}
3 {Write-Output 'OptOut'}
}
}

Update: I should of looked at WMI first! There is a property named DataExecutionPrevention_SupportPolicy in the Win32_OperatingSystem class that also stores this information and it's easier to retrieve...


Get-WmiObject -Class Win32_OperatingSystem -Property DataExecutionPrevention_SupportPolicy

Monday, February 20, 2012

Unblocking Files with PowerShell

When you download a file from the internet you may of seen this:

2012-02-19 01h38_17

If the file is an executable and you try to run it, you will be prompted if you want to run the file… Yea, yea… I just freaking downloaded it, I want to run it already! If you download a ZIP file (such as the Sysinternals Suite) you'll have to unblock every program in it…

So what's really going on is that something called an alternate data stream get's created for the file when you save it to disk. This is a special feature of NTFS that allows a file name to reference more than one data stream on disk. The primary data stream is just the data that the filename is referencing. So Test.zip points to a data stream and that's where the ZIP file data is stored. Alternate data streams are stored and accessed with the name of the file plus ':Data_Stream_Name' (a colon and the name of the alternate stream). These additional streams are hidden from Windows Explorer and the command prompt directory listing. Only special methods allow you to access them. When a file is downloaded from the internet an alternate access stream is created with the name 'Zone.Identifier'. The contents look like this:

ZoneId 3 means it came from the internet. This is what Windows uses to determine whether or not it should block the file. You can edit the file by opening it with Notepad from a command prompt like this:

However you cannot delete the alternate access stream from disk using this approach, only delete or change it's contents. There is a function called DeleteFile in Win32 API that will allow you to delete an alternate access stream however we can't call this from PowerShell directly. We will need to p/invoke using the Add-Type cmdlet.

I've wrapped this into a portable function that allows pipeline input. So you can pipe an entire set of files to this like so:

As I write this, PowerShell 3 is current in CTP2 and it does include a cmdlet for this. However if you are using PowerShell v2 this is the way to go.

Sunday, February 12, 2012

Fixing Unresolvable NTFS ACL Accounts

An interesting question came up on SuperUser the other day. The OP wanted to know how to change the account of a NTFS ACL but still retain all of the ACL settings (privileges, inheritance etc.…). This could be needed if the original account was deleted, or if the ACL was on a drive that was moved to another machine etc.…

When the ACL entry has an unresolvable account it will appear like this:

image

The first thing that came to mind was to edit the SDDL string. The SDDL is a not so easy to read string representation of the ACL. It looks like this:

Easy to read huh?

You can learn all about the format on MSDN, however the format is not important for our purpose here. All we need to do is replace the SID with the SID of the account we want.

So first lets use the Win32_Account WMI class to get a list of user account SIDs:

This will get all account types from the local machine and the domain if the machine is on a domain which could make this command take a really long time.

To make it go faster we’ll filter the query a little bit. There are a couple properties of this class we can use to reduce the run time.

  • Name
  • SidType

    (1) User
    (2) Group
    (3) Domain
    (4) Alias
    (5) WellKnownGroup
    (6) DeletedAccount
    (7) Invalid
    (8) Unknown
    (9) Computer

  • LocalAccount

So to get the SID of a local user account named NewAccount we’ll use:

To find the SID of the broken ACL entry we’ll use the Get-ACL cmdlet. The ACE’s are stored in the Access property returned by the Get-Acl object. To retrieve the SID of the entry which account is unknown we’ll use the Translate method to find that one that can’t translate to an NTAccount object. We also aren’t interested in ACE’s that are inherited so we’ll filter them out.

The invalid SIDs are stored in the array $invalidSids.

Now we can do a simple string search and replace in the SDDL string.

The last step is to set the ACL with the updated SDDL. To do this we’ll use the SetSecurityDescriptorSddlForm method.

And that’s it. The ACL is now updated with the account but all of the previous ACE settings are retained as we wanted.

image

Saturday, January 7, 2012

Changing Tomcat’s CA Trust Keystore File

By default Tomcat makes use of the cacerts file provided with the Java runtime environment located under: C:\Program Files\Java\jre6\lib\security.

Since the JRE installer likes to remove this file when it is updated or uninstalled I needed to find a way to re-configure Tomcat so it uses an different keystore.

System Setup

  1. Start without Java or Tomcat installed.
  2. Install JRE (this is the x64 version): http://download.oracle.com/otn-pub/java/jdk/6u30-b12/jre-6u30-windows-x64.exe
  3. Create JAVA_HOME system wide variable: setx.exe JAVA_HOME "C:\Program Files\Java\jre6" /M
  4. Install Tomcat: http://archive.apache.org/dist/tomcat/tomcat-6/v6.0.29/bin/apache-tomcat-6.0.29.exe
  5. Select all defaults for the Tomcat Normal installation.
  6. Get Portecle to create new and view contents of JKS keystores: http://cdnetworks-us-2.dl.sourceforge.net/project/portecle/portecle/1.7/portecle-1.7.zip

Testing

  1. First we’ll create a test Java webapp that makes an HTTPS connection.
  2. We’ll create a test JSP page (source code at the bottom) using the URLConnection class in Java to connect to https://www.paypal.com/ and fetch its page content.
  3. If the connection is successful the page content is displayed below the “We are connected to HTTPS URL?” message.
  4. If the connection is not successful the exception error message will be displayed.
  5. The test JSP shows we can connect to https://www.paypal.com/ successfully and retrieve it’s content:
image

Why Does This Work?

In order for the URLConnection object to successfully make a connection the certificate from Paypal’s certificate signer must be pre-installed in the JRE cacerts JKS keystore. Let’s make sure…

Paypal’s certificate is signed by a VeriSign certificate.

The SHA1 thumbprint of the VeriSign cert is: 4E:B6:D5:78:49:9B:1C:CF:5F:58:1E:AD:56:BE:3D:9B:67:44:A5:E5

image

We have this VeriSign certificate pre-installed in the JRE cacerts JKS keystore:

image

More Testing…

First lets verify the test JSP works by removing all certificates from the JRE cacerts keystore and re-trying the test webapp.

We’ll use some PowerShell to remove all certificates from the JRE cacerts JKS keystore.

Resultant empty JRE cacerts keystore:

image

For good measure lets restart Tomcat to make sure our changes take effect with a PowerShell one liner: restart-service tomcat*
After restarting Tomcat the HTTPS connection is now broken!

image

As a sanity check we’ll install only the VeriSign certificate in the JRE cacerts keystore all by itself:

image

Now the HTTPS connection works again!

image

Changing the Tomcat Keystore

Because JRE 6 removes it’s cacerts file when it is uninstalled or upgraded I wanted to re-configure Tomcat to use a different keystore so that my certificates being used for HTTPS endpoints wouldn't be deleted. Figuring out how turned out to be quite an adventure.

First I checked the Tomcat documentation for server.xml and found this:

keystoreFile The pathname of the keystore file where you have stored the server certificate to be loaded. By default, the pathname is the file ".keystore" in the operating system home directory of the user that is running Tomcat. If your keystoreType doesn't need a file use "" (empty string) for this parameter.
truststoreFile The trust store file to use to validate client certificates. The default is the value of thejavax.net.ssl.trustStore system property. If neither this attribute nor the default system property is set, no trust store will be configured.

The keystoreFile setting is for Tomcat’s private key for HTTPS connectors and the truststoreFile setting is for client authentication but hey lets try both!

Below is a connector configured to point to a single JKS keystore with the VeriSign certificate inside. Here is the server.xml connector for port 8080.

… And what is inside of C:\mykeystore.jks:

image

After restarting Tomcat things aren't looking good:

image

The solution

It turns out a couple of Java JVM parameters are needed to point Tomcat to a different keystore.

These can be set via the Tomcat configuration utility: "C:\Program Files\Apache Software Foundation\Tomcat 6.0\bin\tomcat6w.exe" //ES//Tomcat6

image

This tool stores values in the registry: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Apache Software Foundation\Procrun 2.0\Tomcat6\Parameters\Java\Options

image

The Tomcat connector was reverted back to the default:

After restarting Tomcat again… The final result: Success! Tomcat is now using C:\mykeystore.jks instead of the JRE cacerts keystore.

image

test.jsp Source Code