11using System ;
2+ using System . Net . Sockets ;
23using System . Runtime . InteropServices ;
34using System . Text ;
45using System . Text . Json ;
@@ -307,7 +308,7 @@ public WindowsCredentialBackend(string credentialsTargetName)
307308
308309 public Task < RawCredentials ? > ReadCredentials ( CancellationToken ct = default )
309310 {
310- var raw = NativeApi . ReadCredentials ( _credentialsTargetName ) ;
311+ var raw = Wincred . ReadCredentials ( _credentialsTargetName ) ;
311312 if ( raw == null ) return Task . FromResult < RawCredentials ? > ( null ) ;
312313
313314 RawCredentials ? credentials ;
@@ -326,115 +327,179 @@ public WindowsCredentialBackend(string credentialsTargetName)
326327 public Task WriteCredentials ( RawCredentials credentials , CancellationToken ct = default )
327328 {
328329 var raw = JsonSerializer . Serialize ( credentials , RawCredentialsJsonContext . Default . RawCredentials ) ;
329- NativeApi . WriteCredentials ( _credentialsTargetName , raw ) ;
330+ Wincred . WriteCredentials ( _credentialsTargetName , raw ) ;
330331 return Task . CompletedTask ;
331332 }
332333
333334 public Task DeleteCredentials ( CancellationToken ct = default )
334335 {
335- NativeApi . DeleteCredentials ( _credentialsTargetName ) ;
336+ Wincred . DeleteCredentials ( _credentialsTargetName ) ;
336337 return Task . CompletedTask ;
337338 }
338339
339- private static class NativeApi
340+ }
341+
342+ /// <summary>
343+ /// Wincred provides relatively low level wrapped calls to the Wincred.h native API.
344+ /// </summary>
345+ internal static class Wincred
346+ {
347+ private const int CredentialTypeGeneric = 1 ;
348+ private const int CredentialTypeDomainPassword = 2 ;
349+ private const int PersistenceTypeLocalComputer = 2 ;
350+ private const int ErrorNotFound = 1168 ;
351+ private const int CredMaxCredentialBlobSize = 5 * 512 ;
352+ private const string PackageNTLM = "NTLM" ;
353+
354+ public static string ? ReadCredentials ( string targetName )
340355 {
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 ;
356+ if ( ! CredReadW ( targetName , CredentialTypeGeneric , 0 , out var credentialPtr ) )
357+ {
358+ var error = Marshal . GetLastWin32Error ( ) ;
359+ if ( error == ErrorNotFound ) return null ;
360+ throw new InvalidOperationException ( $ "Failed to read credentials (Error { error } )") ;
361+ }
345362
346- public static string ? ReadCredentials ( string targetName )
363+ try
347364 {
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- }
365+ var cred = Marshal . PtrToStructure < CREDENTIALW > ( credentialPtr ) ;
366+ return Marshal . PtrToStringUni ( cred . CredentialBlob , cred . CredentialBlobSize / sizeof ( char ) ) ;
367+ }
368+ finally
369+ {
370+ CredFree ( credentialPtr ) ;
371+ }
372+ }
354373
355- try
356- {
357- var cred = Marshal . PtrToStructure < CREDENTIAL > ( credentialPtr ) ;
358- return Marshal . PtrToStringUni ( cred . CredentialBlob , cred . CredentialBlobSize / sizeof ( char ) ) ;
359- }
360- finally
374+ public static void WriteCredentials ( string targetName , string secret )
375+ {
376+ var byteCount = Encoding . Unicode . GetByteCount ( secret ) ;
377+ if ( byteCount > CredMaxCredentialBlobSize )
378+ throw new ArgumentOutOfRangeException ( nameof ( secret ) ,
379+ $ "The secret is greater than { CredMaxCredentialBlobSize } bytes") ;
380+
381+ var credentialBlob = Marshal . StringToHGlobalUni ( secret ) ;
382+ var cred = new CREDENTIALW
383+ {
384+ Type = CredentialTypeGeneric ,
385+ TargetName = targetName ,
386+ CredentialBlobSize = byteCount ,
387+ CredentialBlob = credentialBlob ,
388+ Persist = PersistenceTypeLocalComputer ,
389+ } ;
390+ try
391+ {
392+ if ( ! CredWriteW ( ref cred , 0 ) )
361393 {
362- CredFree ( credentialPtr ) ;
394+ var error = Marshal . GetLastWin32Error ( ) ;
395+ throw new InvalidOperationException ( $ "Failed to write credentials (Error { error } )") ;
363396 }
364397 }
365-
366- public static void WriteCredentials ( string targetName , string secret )
398+ finally
367399 {
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") ;
400+ Marshal . FreeHGlobal ( credentialBlob ) ;
401+ }
402+ }
372403
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- }
404+ public static void DeleteCredentials ( string targetName )
405+ {
406+ if ( ! CredDeleteW ( targetName , CredentialTypeGeneric , 0 ) )
407+ {
408+ var error = Marshal . GetLastWin32Error ( ) ;
409+ if ( error == ErrorNotFound ) return ;
410+ throw new InvalidOperationException ( $ "Failed to delete credentials (Error { error } )") ;
394411 }
412+ }
413+
414+ public static void WriteDomainCredentials ( string domainName , string serverName , string username , string password )
415+ {
416+ var targetName = $ "{ domainName } /{ serverName } ";
417+ var targetInfo = new CREDENTIAL_TARGET_INFORMATIONW
418+ {
419+ TargetName = targetName ,
420+ DnsServerName = serverName ,
421+ DnsDomainName = domainName ,
422+ PackageName = PackageNTLM ,
423+ } ;
424+ var byteCount = Encoding . Unicode . GetByteCount ( password ) ;
425+ if ( byteCount > CredMaxCredentialBlobSize )
426+ throw new ArgumentOutOfRangeException ( nameof ( password ) ,
427+ $ "The secret is greater than { CredMaxCredentialBlobSize } bytes") ;
395428
396- public static void DeleteCredentials ( string targetName )
429+ var credentialBlob = Marshal . StringToHGlobalUni ( password ) ;
430+ var cred = new CREDENTIALW
397431 {
398- if ( ! CredDeleteW ( targetName , CredentialTypeGeneric , 0 ) )
432+ Type = CredentialTypeDomainPassword ,
433+ TargetName = targetName ,
434+ CredentialBlobSize = byteCount ,
435+ CredentialBlob = credentialBlob ,
436+ Persist = PersistenceTypeLocalComputer ,
437+ UserName = username ,
438+ } ;
439+ try
440+ {
441+ if ( ! CredWriteDomainCredentialsW ( ref targetInfo , ref cred , 0 ) )
399442 {
400443 var error = Marshal . GetLastWin32Error ( ) ;
401- if ( error == ErrorNotFound ) return ;
402- throw new InvalidOperationException ( $ "Failed to delete credentials (Error { error } )") ;
444+ throw new InvalidOperationException ( $ "Failed to write credentials (Error { error } )") ;
403445 }
404446 }
447+ finally
448+ {
449+ Marshal . FreeHGlobal ( credentialBlob ) ;
450+ }
451+ }
405452
406- [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
407- private static extern bool CredReadW ( string target , int type , int reservedFlag , out IntPtr credentialPtr ) ;
453+ [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
454+ private static extern bool CredReadW ( string target , int type , int reservedFlag , out IntPtr credentialPtr ) ;
408455
409- [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
410- private static extern bool CredWriteW ( [ In ] ref CREDENTIAL userCredential , [ In ] uint flags ) ;
456+ [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
457+ private static extern bool CredWriteW ( [ In ] ref CREDENTIALW userCredential , [ In ] uint flags ) ;
411458
412- [ DllImport ( "Advapi32.dll" , SetLastError = true ) ]
413- private static extern void CredFree ( [ In ] IntPtr cred ) ;
459+ [ DllImport ( "Advapi32.dll" , SetLastError = true ) ]
460+ private static extern void CredFree ( [ In ] IntPtr cred ) ;
414461
415- [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
416- private static extern bool CredDeleteW ( string target , int type , int flags ) ;
462+ [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
463+ private static extern bool CredDeleteW ( string target , int type , int flags ) ;
417464
418- [ StructLayout ( LayoutKind . Sequential ) ]
419- private struct CREDENTIAL
420- {
421- public int Flags ;
422- public int Type ;
465+ [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
466+ private static extern bool CredWriteDomainCredentialsW ( [ In ] ref CREDENTIAL_TARGET_INFORMATIONW target , [ In ] ref CREDENTIALW userCredential , [ In ] uint flags ) ;
423467
424- [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetName ;
468+ [ StructLayout ( LayoutKind . Sequential ) ]
469+ private struct CREDENTIALW
470+ {
471+ public int Flags ;
472+ public int Type ;
425473
426- [ MarshalAs ( UnmanagedType . LPWStr ) ] public string Comment ;
474+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetName ;
427475
428- public long LastWritten ;
429- public int CredentialBlobSize ;
430- public IntPtr CredentialBlob ;
431- public int Persist ;
432- public int AttributeCount ;
433- public IntPtr Attributes ;
476+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string Comment ;
434477
435- [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetAlias ;
478+ public long LastWritten ;
479+ public int CredentialBlobSize ;
480+ public IntPtr CredentialBlob ;
481+ public int Persist ;
482+ public int AttributeCount ;
483+ public IntPtr Attributes ;
436484
437- [ MarshalAs ( UnmanagedType . LPWStr ) ] public string UserName ;
438- }
485+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetAlias ;
486+
487+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string UserName ;
488+ }
489+
490+ [ StructLayout ( LayoutKind . Sequential ) ]
491+ private struct CREDENTIAL_TARGET_INFORMATIONW
492+ {
493+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetName ;
494+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string NetbiosServerName ;
495+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string DnsServerName ;
496+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string NetbiosDomainName ;
497+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string DnsDomainName ;
498+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string DnsTreeName ;
499+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string PackageName ;
500+
501+ public uint Flags ;
502+ public uint CredTypeCount ;
503+ public IntPtr CredTypes ;
439504 }
440505}
0 commit comments