views:

161

answers:

4

What prompted this question: I'm trying to change the account under which a windows service runs. I decided to use the win32 api rather than WMI and started looking at ChangeServiceConfig.

I thought I could simply use the SecureString type in the unmanaged method signature and it would work fine. Hmmm, not quite. This made me wonder, does anyone know what native (win32) type the SecureString class is marshalled as (by default) to unmanaged code?

A: 

SecureString is a non-blittable type. So - there isn't a 1:1 win32 type.

Keep in mind that i'm not a COM expert at all.

Arnis L.
+4  A: 

It looks as though you can use the helper functions in the InteropServices namespace to assist you with this:

Note that SecureString has no members that inspect, compare, or convert the value of a SecureString. The absence of such members helps protect the value of the instance from accidental or malicious exposure. Use appropriate members of the System.Runtime.InteropServices..::.Marshal class, such as the SecureStringToBSTR method, to manipulate the value of a SecureString object.

You can use SecureStringToBSTR to convert the string to a BSTR, then pass it to an appropriate function for use.

1800 INFORMATION
+2  A: 

Take a look at the MarshalSecureStringToBSTR method.

Joe
+1  A: 

I never got an answer to the question in the title, so I still don't know SecureString is marshalled to unmanaged code.

I did, however, get my code working by using the suggestions in some of the other answers. I have provided the code below.

internal static class NativeService
{
    ...
    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool ChangeServiceConfig(IntPtr hService, uint nServiceType, uint nStartType, uint nErrorControl, string lpBinaryPathName, string lpLoadOrderGroup, IntPtr lpdwTagId, [In] char[] lpDependencies, string lpServiceStartName, IntPtr lpPassword, string lpDisplayName);
    ...
    public const uint SERVICE_NO_CHANGE = 0xffffffff;
}

internal class ClassThatConfiguresService
{
    ...
    public void ConfigureStartupAccount(IntPtr service, string userName, SecureString password)
    {
        passwordPointer = Marshal.SecureStringToGlobalAllocUnicode(password);
        try
        {
            if(!NativeService.ChangeServiceConfig(service, NativeService.SERVICE_NO_CHANGE, NativeService.SERVICE_NO_CHANGE, NativeService.SERVICE_NO_CHANGE, null, null, IntPtr.Zero, null, userName, passwordPointer, null))
                throw new Win32Exception(Marshal.GetLastWin32Error());
        }
        finally
        {
            Marshal.ZeroFreeGlobalAllocUnicode(passwordPointer);
        }
    }
    ...
}

Instead of Marshal.SecureStringToBSTR I used Marshal.SecureStringToGlobalAllocUnicode because thats's what the unmanaged function expects, but other than that it works a treat.

Hopefully someone else finds this useful. Kep.

Keith Moore
As any other .NET object that's not specifically handled otherwise (like `String` or `StringBuilder`), `SecureString` is marshaled as `IUnknown`.
Pavel Minaev