2012-04-06 17 views
1

現在、ドメインに参加していないユーザーがターミナルサーバー/ WebDAVサーバーの資格情報を簡単に保存できるように、C#で書かれたプログラムがCredRead/CredWrite from Advapi32.dllです。 (資格情報の構造についてはhttp://msdn.microsoft.com/en-us/library/aa374788%28v=vs.85%29.aspxWindows XP ProfessionalでCredWriteの問題を解決しました

これは問題なく動作していますが、先日すべてのXPオペレーティングシステムで問題が発生しました。ターミナルサーバーの資格情報を保存できません。ネイティブのCredWriteメソッドがエラーコード87(ERROR_INVALID_PARAMETER)を返しています。

資格情報の永続性とタイプのさまざまな組み合わせを試した後、問題はTargetName自体であることに気付きました。 Windowsキーリングにターミナルサーバーを保存するには、TargetNameはTERMSRV/server.domain.comです。 Windows Vista以上では、これは私のコードで正常に動作しますが、Windows XPではこれはありません。

奇妙なことに、Windows XPでリモートデスクトップアプリケーションを実行して資格情報を保存すると、それほど大したことはありません。 RDPを使用した後に保存された資格情報を列挙するメソッドを作成したとき(明らかにパスワードのblobは返されませんでした)、ターゲットが私のプログラムが書き込もうとしたものと同一であることがわかりました。

問題がTargetNameにあることを確認するために、「/」を削除して正常に機能しました。

以下は私のコードの関連部分である:

var writeInt = NativeCredMan.WriteCred("TERMSRV/host.server.com", samAccountName, password, CRED_TYPE.DOMAIN_PASSWORD, CRED_PERSIST.LOCAL_MACHINE); 

... ...

public enum CRED_TYPE : uint 
{ 
    GENERIC = 1, 
    DOMAIN_PASSWORD = 2, 
    DOMAIN_CERTIFICATE = 3, 
    DOMAIN_VISIBLE_PASSWORD = 4, 
    GENERIC_CERTIFICATE = 5, 
    DOMAIN_EXTENDED = 6, 
    MAXIMUM = 7,  // Maximum supported cred type 
    MAXIMUM_EX = (MAXIMUM + 1000), // Allow new applications to run on old OSes 
} 

public enum CRED_PERSIST : uint 
{ 
    SESSION = 1, 
    LOCAL_MACHINE = 2, 
    ENTERPRISE = 3, 
} 

... ...

[DllImport("Advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)] 
    static extern bool CredRead(string target, CRED_TYPE type, int reservedFlag, out IntPtr CredentialPtr); 

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

    [DllImport("Advapi32.dll", EntryPoint = "CredFree", SetLastError = true)] 
    static extern bool CredFree([In] IntPtr cred); 

    [DllImport("Advapi32.dll", EntryPoint = "CredDeleteW", CharSet = CharSet.Unicode)] 
    static extern bool CredDelete(string target, CRED_TYPE type, int flags); 

    //[DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)] 
    //static extern bool CredEnumerateold(string filter, int flag, out int count, out IntPtr pCredentials); 

    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 
    public static extern bool CredEnumerate(string filter, uint flag, out uint count, out IntPtr pCredentials); 

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

     internal static NativeCredential GetNativeCredential(Credential cred) 
     { 
      var ncred = new NativeCredential 
          { 
           AttributeCount = 0, 
           Attributes = IntPtr.Zero, 
           Comment = IntPtr.Zero, 
           TargetAlias = IntPtr.Zero, 
           Type = CRED_TYPE.DOMAIN_PASSWORD, 
           Persist = (UInt32) cred.Persist, 
           CredentialBlobSize = (UInt32) cred.CredentialBlobSize, 
           TargetName = Marshal.StringToCoTaskMemUni(cred.TargetName), 
           CredentialBlob = Marshal.StringToCoTaskMemUni(cred.CredentialBlob), 
           UserName = Marshal.StringToCoTaskMemUni(cred.UserName) 
          }; 
      return ncred; 
     } 
    } 

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

.. 。 ...

 public static int WriteCred(string key, string userName, string secret, CRED_TYPE type, CRED_PERSIST credPersist) 
    { 

     var byteArray = Encoding.Unicode.GetBytes(secret); 
     if (byteArray.Length > 512) 
      throw new ArgumentOutOfRangeException("The secret message has exceeded 512 bytes."); 

     var cred = new Credential 
         { 
          TargetName = key, 
          CredentialBlob = secret, 
          CredentialBlobSize = (UInt32) Encoding.Unicode.GetBytes(secret).Length, 
          AttributeCount = 0, 
          Attributes = IntPtr.Zero, 
          UserName = userName, 
          Comment = null, 
          TargetAlias = null, 
          Type = type, 
          Persist = credPersist 
         }; 
     var ncred = NativeCredential.GetNativeCredential(cred); 

     var written = CredWrite(ref ncred, 0); 
     var lastError = Marshal.GetLastWin32Error(); 
     if (written) 
     { 
      return 0; 
     } 
     var message = ""; 
     if (lastError == 1312) 
     { 
      message = (string.Format("Failed to save " + key + " with error code {0}.", lastError) + " This error typically occurrs on home editions of Windows XP and Vista. Verify the version of Windows is Pro/Business or higher."); 
     } 
     else 
     { 
      message = string.Format("Failed to save " + key + " with error code {0}.", lastError); 
     } 
     MessageBox.Show(message); 
     return 1; 
    } 

これらの種類の資格情報をどのように保存することができるかについて、正しい方向を指し示すことができますか?

ありがとう

+1

古いオペレーティングシステムではバックスラッシュを使用することをお勧めします。 –

答えて

1

Nevermind。私は問題を発見した。私の質問を確認した後、私はここでは一部の管理資格カウンターことに気づい:

internal static NativeCredential GetNativeCredential(Credential cred) 
    { 
     var ncred = new NativeCredential 
         { 
          AttributeCount = 0, 
          Attributes = IntPtr.Zero, 
          Comment = IntPtr.Zero, 
          TargetAlias = IntPtr.Zero, 
          Type = CRED_TYPE.DOMAIN_PASSWORD, 
          Persist = (UInt32) cred.Persist, 
          CredentialBlobSize = (UInt32) cred.CredentialBlobSize, 
          TargetName = Marshal.StringToCoTaskMemUni(cred.TargetName), 
          CredentialBlob = Marshal.StringToCoTaskMemUni(cred.CredentialBlob), 
          UserName = Marshal.StringToCoTaskMemUni(cred.UserName) 
         }; 
     return ncred; 
    } 

TYPEだけではなく、タイプをdecalringのCRED.TYPE.DOMAIN_PASSWORDにあらかじめ定義されていました。

に変更:

internal static NativeCredential GetNativeCredential(Credential cred) 
    { 
     var ncred = new NativeCredential 
         { 
          AttributeCount = 0, 
          Attributes = IntPtr.Zero, 
          Comment = IntPtr.Zero, 
          TargetAlias = IntPtr.Zero, 
          Type = cred.type, 
          Persist = (UInt32) cred.Persist, 
          CredentialBlobSize = (UInt32) cred.CredentialBlobSize, 
          TargetName = Marshal.StringToCoTaskMemUni(cred.TargetName), 
          CredentialBlob = Marshal.StringToCoTaskMemUni(cred.CredentialBlob), 
          UserName = Marshal.StringToCoTaskMemUni(cred.UserName) 
         }; 
     return ncred; 
    } 

とGENERICは、問題を解決して資格情報を送信します。どうやら、Windows Vista/7ではDOMAIN_PASSWORDとGENERICの永続パスワードで「/」が許可されていましたが、XPではこれをGENERICに対してのみ許可しています。

関連する問題