@@ -307,7 +307,7 @@ public WindowsCredentialBackend(string credentialsTargetName)
307307
308308 public Task < RawCredentials ? > ReadCredentials ( CancellationToken ct = default )
309309 {
310- var raw = NativeApi . ReadCredentials ( _credentialsTargetName ) ;
310+ var raw = Wincred . ReadCredentials ( _credentialsTargetName ) ;
311311 if ( raw == null ) return Task . FromResult < RawCredentials ? > ( null ) ;
312312
313313 RawCredentials ? credentials ;
@@ -326,115 +326,179 @@ public WindowsCredentialBackend(string credentialsTargetName)
326326 public Task WriteCredentials ( RawCredentials credentials , CancellationToken ct = default )
327327 {
328328 var raw = JsonSerializer . Serialize ( credentials , RawCredentialsJsonContext . Default . RawCredentials ) ;
329- NativeApi . WriteCredentials ( _credentialsTargetName , raw ) ;
329+ Wincred . WriteCredentials ( _credentialsTargetName , raw ) ;
330330 return Task . CompletedTask ;
331331 }
332332
333333 public Task DeleteCredentials ( CancellationToken ct = default )
334334 {
335- NativeApi . DeleteCredentials ( _credentialsTargetName ) ;
335+ Wincred . DeleteCredentials ( _credentialsTargetName ) ;
336336 return Task . CompletedTask ;
337337 }
338338
339- private static class NativeApi
339+ }
340+
341+ /// <summary>
342+ /// Wincred provides relatively low level wrapped calls to the Wincred.h native API.
343+ /// </summary>
344+ internal static class Wincred
345+ {
346+ private const int CredentialTypeGeneric = 1 ;
347+ private const int CredentialTypeDomainPassword = 2 ;
348+ private const int PersistenceTypeLocalComputer = 2 ;
349+ private const int ErrorNotFound = 1168 ;
350+ private const int CredMaxCredentialBlobSize = 5 * 512 ;
351+ private const string PackageNTLM = "NTLM" ;
352+
353+ public static string ? ReadCredentials ( string targetName )
340354 {
341- private const int CredentialTypeGeneric = 1 ;
342- private const int PersistenceTypeLocalComputer = 2 ;
343- private const int ErrorNotFound = 1168 ;
344- private const int CredMaxCredentialBlobSize = 5 * 512 ;
355+ if ( ! CredReadW ( targetName , CredentialTypeGeneric , 0 , out var credentialPtr ) )
356+ {
357+ var error = Marshal . GetLastWin32Error ( ) ;
358+ if ( error == ErrorNotFound ) return null ;
359+ throw new InvalidOperationException ( $ "Failed to read credentials (Error { error } )") ;
360+ }
345361
346- public static string ? ReadCredentials ( string targetName )
362+ try
347363 {
348- if ( ! CredReadW ( targetName , CredentialTypeGeneric , 0 , out var credentialPtr ) )
349- {
350- var error = Marshal . GetLastWin32Error ( ) ;
351- if ( error == ErrorNotFound ) return null ;
352- throw new InvalidOperationException ( $ "Failed to read credentials (Error { error } )") ;
353- }
364+ var cred = Marshal . PtrToStructure < CREDENTIALW > ( credentialPtr ) ;
365+ return Marshal . PtrToStringUni ( cred . CredentialBlob , cred . CredentialBlobSize / sizeof ( char ) ) ;
366+ }
367+ finally
368+ {
369+ CredFree ( credentialPtr ) ;
370+ }
371+ }
354372
355- try
356- {
357- var cred = Marshal . PtrToStructure < CREDENTIAL > ( credentialPtr ) ;
358- return Marshal . PtrToStringUni ( cred . CredentialBlob , cred . CredentialBlobSize / sizeof ( char ) ) ;
359- }
360- finally
373+ public static void WriteCredentials ( string targetName , string secret )
374+ {
375+ var byteCount = Encoding . Unicode . GetByteCount ( secret ) ;
376+ if ( byteCount > CredMaxCredentialBlobSize )
377+ throw new ArgumentOutOfRangeException ( nameof ( secret ) ,
378+ $ "The secret is greater than { CredMaxCredentialBlobSize } bytes") ;
379+
380+ var credentialBlob = Marshal . StringToHGlobalUni ( secret ) ;
381+ var cred = new CREDENTIALW
382+ {
383+ Type = CredentialTypeGeneric ,
384+ TargetName = targetName ,
385+ CredentialBlobSize = byteCount ,
386+ CredentialBlob = credentialBlob ,
387+ Persist = PersistenceTypeLocalComputer ,
388+ } ;
389+ try
390+ {
391+ if ( ! CredWriteW ( ref cred , 0 ) )
361392 {
362- CredFree ( credentialPtr ) ;
393+ var error = Marshal . GetLastWin32Error ( ) ;
394+ throw new InvalidOperationException ( $ "Failed to write credentials (Error { error } )") ;
363395 }
364396 }
365-
366- public static void WriteCredentials ( string targetName , string secret )
397+ finally
367398 {
368- var byteCount = Encoding . Unicode . GetByteCount ( secret ) ;
369- if ( byteCount > CredMaxCredentialBlobSize )
370- throw new ArgumentOutOfRangeException ( nameof ( secret ) ,
371- $ "The secret is greater than { CredMaxCredentialBlobSize } bytes") ;
399+ Marshal . FreeHGlobal ( credentialBlob ) ;
400+ }
401+ }
372402
373- var credentialBlob = Marshal . StringToHGlobalUni ( secret ) ;
374- var cred = new CREDENTIAL
375- {
376- Type = CredentialTypeGeneric ,
377- TargetName = targetName ,
378- CredentialBlobSize = byteCount ,
379- CredentialBlob = credentialBlob ,
380- Persist = PersistenceTypeLocalComputer ,
381- } ;
382- try
383- {
384- if ( ! CredWriteW ( ref cred , 0 ) )
385- {
386- var error = Marshal . GetLastWin32Error ( ) ;
387- throw new InvalidOperationException ( $ "Failed to write credentials (Error { error } )") ;
388- }
389- }
390- finally
391- {
392- Marshal . FreeHGlobal ( credentialBlob ) ;
393- }
403+ public static void DeleteCredentials ( string targetName )
404+ {
405+ if ( ! CredDeleteW ( targetName , CredentialTypeGeneric , 0 ) )
406+ {
407+ var error = Marshal . GetLastWin32Error ( ) ;
408+ if ( error == ErrorNotFound ) return ;
409+ throw new InvalidOperationException ( $ "Failed to delete credentials (Error { error } )") ;
394410 }
411+ }
412+
413+ public static void WriteDomainCredentials ( string domainName , string serverName , string username , string password )
414+ {
415+ var targetName = $ "{ domainName } /{ serverName } ";
416+ var targetInfo = new CREDENTIAL_TARGET_INFORMATIONW
417+ {
418+ TargetName = targetName ,
419+ DnsServerName = serverName ,
420+ DnsDomainName = domainName ,
421+ PackageName = PackageNTLM ,
422+ } ;
423+ var byteCount = Encoding . Unicode . GetByteCount ( password ) ;
424+ if ( byteCount > CredMaxCredentialBlobSize )
425+ throw new ArgumentOutOfRangeException ( nameof ( password ) ,
426+ $ "The secret is greater than { CredMaxCredentialBlobSize } bytes") ;
395427
396- public static void DeleteCredentials ( string targetName )
428+ var credentialBlob = Marshal . StringToHGlobalUni ( password ) ;
429+ var cred = new CREDENTIALW
397430 {
398- if ( ! CredDeleteW ( targetName , CredentialTypeGeneric , 0 ) )
431+ Type = CredentialTypeDomainPassword ,
432+ TargetName = targetName ,
433+ CredentialBlobSize = byteCount ,
434+ CredentialBlob = credentialBlob ,
435+ Persist = PersistenceTypeLocalComputer ,
436+ UserName = username ,
437+ } ;
438+ try
439+ {
440+ if ( ! CredWriteDomainCredentialsW ( ref targetInfo , ref cred , 0 ) )
399441 {
400442 var error = Marshal . GetLastWin32Error ( ) ;
401- if ( error == ErrorNotFound ) return ;
402- throw new InvalidOperationException ( $ "Failed to delete credentials (Error { error } )") ;
443+ throw new InvalidOperationException ( $ "Failed to write credentials (Error { error } )") ;
403444 }
404445 }
446+ finally
447+ {
448+ Marshal . FreeHGlobal ( credentialBlob ) ;
449+ }
450+ }
405451
406- [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
407- private static extern bool CredReadW ( string target , int type , int reservedFlag , out IntPtr credentialPtr ) ;
452+ [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
453+ private static extern bool CredReadW ( string target , int type , int reservedFlag , out IntPtr credentialPtr ) ;
408454
409- [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
410- private static extern bool CredWriteW ( [ In ] ref CREDENTIAL userCredential , [ In ] uint flags ) ;
455+ [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
456+ private static extern bool CredWriteW ( [ In ] ref CREDENTIALW userCredential , [ In ] uint flags ) ;
411457
412- [ DllImport ( "Advapi32.dll" , SetLastError = true ) ]
413- private static extern void CredFree ( [ In ] IntPtr cred ) ;
458+ [ DllImport ( "Advapi32.dll" , SetLastError = true ) ]
459+ private static extern void CredFree ( [ In ] IntPtr cred ) ;
414460
415- [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
416- private static extern bool CredDeleteW ( string target , int type , int flags ) ;
461+ [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
462+ private static extern bool CredDeleteW ( string target , int type , int flags ) ;
417463
418- [ StructLayout ( LayoutKind . Sequential ) ]
419- private struct CREDENTIAL
420- {
421- public int Flags ;
422- public int Type ;
464+ [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
465+ private static extern bool CredWriteDomainCredentialsW ( [ In ] ref CREDENTIAL_TARGET_INFORMATIONW target , [ In ] ref CREDENTIALW userCredential , [ In ] uint flags ) ;
423466
424- [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetName ;
467+ [ StructLayout ( LayoutKind . Sequential ) ]
468+ private struct CREDENTIALW
469+ {
470+ public int Flags ;
471+ public int Type ;
425472
426- [ MarshalAs ( UnmanagedType . LPWStr ) ] public string Comment ;
473+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetName ;
427474
428- public long LastWritten ;
429- public int CredentialBlobSize ;
430- public IntPtr CredentialBlob ;
431- public int Persist ;
432- public int AttributeCount ;
433- public IntPtr Attributes ;
475+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string Comment ;
434476
435- [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetAlias ;
477+ public long LastWritten ;
478+ public int CredentialBlobSize ;
479+ public IntPtr CredentialBlob ;
480+ public int Persist ;
481+ public int AttributeCount ;
482+ public IntPtr Attributes ;
436483
437- [ MarshalAs ( UnmanagedType . LPWStr ) ] public string UserName ;
438- }
484+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetAlias ;
485+
486+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string UserName ;
487+ }
488+
489+ [ StructLayout ( LayoutKind . Sequential ) ]
490+ private struct CREDENTIAL_TARGET_INFORMATIONW
491+ {
492+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetName ;
493+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string NetbiosServerName ;
494+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string DnsServerName ;
495+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string NetbiosDomainName ;
496+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string DnsDomainName ;
497+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string DnsTreeName ;
498+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string PackageName ;
499+
500+ public uint Flags ;
501+ public uint CredTypeCount ;
502+ public IntPtr CredTypes ;
439503 }
440504}
0 commit comments