Search code examples
c#dllimportimpersonationcredentials

CredWrite returning 1312 when called under impersonation


I have an application where the user enters credential using which I create a WindowsIdentity object which I subsequently use to impersonate the user corresponding to the entered credential and make the CredWrite call. However, it is returning

ERROR_NO_SUCH_LOGON_SESSION 1312 (0x520) A specified logon session does not exist. It may already have been terminated.

When I take out the impersonation code, everything works as expected. However, even when I impersonate myself (current logged in user), same 1312 error returns.

I am baffled as to what I am doing wrong. Any pointers are appreciated.

IntPtr token = new IntPtr(0);
securityUtils = new Security.Utility();
string user = "someuser";
string pass = "somepassword";
token = securityUtils.GetToken(user, "somedomain", pass.ConvertToSecureString());
var newId = new WindowsIdentity(token);
string currentUser1 = WindowsIdentity.GetCurrent().Name;
Impersonation.Impersonate(newId);
string currentUser2 = WindowsIdentity.GetCurrent().Name;
int result = WriteCredential("dummykey", user.ConvertToSecureString(), pass.ConvertToSecureString());
Impersonation.UnImpersonate();
string currentUser3 = WindowsIdentity.GetCurrent().Name;

The GetToken method makes the LogonUser call. I have verified that the impersonation is working by looking at currentUser1/2/3.

The WriteCredential method looks like this:

public static int WriteCredential(string key, SecureString userName, SecureString password)
{
    if (string.IsNullOrEmpty(key))
    {
        throw new ArgumentNullException("key is null or empty");
    }

    if (userName == null || userName.Length == 0)
    {
        throw new ArgumentNullException("userName is null or empty");
    }

    if (password == null || password.Length == 0)
    {
        throw new ArgumentNullException("password is null or empty");
    }

    string passwordAsString = password.ConvertToUnsecureString();
    byte[] byteArray = Encoding.Unicode.GetBytes(passwordAsString);

    if (byteArray.Length > MaximumCredentialBlobSize)
    {
        throw new ArgumentOutOfRangeException(string.Format("The password message has exceeded {0} bytes.", MaximumCredentialBlobSize));
    }

    Credential cred = new Credential();
    cred.TargetName = System.Runtime.InteropServices.Marshal.StringToCoTaskMemUni(key);
    cred.CredentialBlob = System.Runtime.InteropServices.Marshal.StringToCoTaskMemUni(passwordAsString);
    cred.CredentialBlobSize = (uint)Encoding.Unicode.GetBytes(passwordAsString).Length;
    cred.AttributeCount = 0;
    cred.Attributes = IntPtr.Zero;
    cred.Comment = IntPtr.Zero;
    cred.TargetAlias = System.Runtime.InteropServices.Marshal.StringToCoTaskMemUni(key);
    cred.Type = CredentialType.CRED_TYPE_GENERIC;
    cred.Persist = Persistance.CRED_PERSIST_ENTERPRISE;
    cred.UserName = System.Runtime.InteropServices.Marshal.StringToCoTaskMemUni(userName.ConvertToUnsecureString());

    bool written = CredWrite(ref cred, 0);
    int lastError = Marshal.GetLastWin32Error();

    if (!written)
    {
        return lastError;
    }

    return 0;
}

The Credential struct:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct Credential
{
    public uint Flags;
    public CredentialType Type;
    public IntPtr TargetName;
    public IntPtr Comment;
    public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
    public uint CredentialBlobSize;
    public IntPtr CredentialBlob;
    public Persistance Persist;
    public uint AttributeCount;
    public IntPtr Attributes;
    public IntPtr TargetAlias;
    public IntPtr UserName;
}

The DllImport:

[DllImport("Advapi32.dll", EntryPoint = "CredWriteW", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool CredWrite([In] ref Credential userCredential, [In] uint flags);

Solution

  • The solution was to explicitly load profile for the user being impersonated before making the WriteCredential call. It is important to make the LoadProfile call before impersonating that user.