@@ -322,7 +322,7 @@ public WindowsCredentialBackend(string credentialsTargetName)
322322
323323 public Task < RawCredentials ? > ReadCredentials ( CancellationToken ct = default )
324324 {
325- var raw = NativeApi . ReadCredentials ( _credentialsTargetName ) ;
325+ var raw = Wincred . ReadCredentials ( _credentialsTargetName ) ;
326326 if ( raw == null ) return Task . FromResult < RawCredentials ? > ( null ) ;
327327
328328 RawCredentials ? credentials ;
@@ -341,115 +341,179 @@ public WindowsCredentialBackend(string credentialsTargetName)
341341 public Task WriteCredentials ( RawCredentials credentials , CancellationToken ct = default )
342342 {
343343 var raw = JsonSerializer . Serialize ( credentials , RawCredentialsJsonContext . Default . RawCredentials ) ;
344- NativeApi . WriteCredentials ( _credentialsTargetName , raw ) ;
344+ Wincred . WriteCredentials ( _credentialsTargetName , raw ) ;
345345 return Task . CompletedTask ;
346346 }
347347
348348 public Task DeleteCredentials ( CancellationToken ct = default )
349349 {
350- NativeApi . DeleteCredentials ( _credentialsTargetName ) ;
350+ Wincred . DeleteCredentials ( _credentialsTargetName ) ;
351351 return Task . CompletedTask ;
352352 }
353353
354- private static class NativeApi
354+ }
355+
356+ /// <summary>
357+ /// Wincred provides relatively low level wrapped calls to the Wincred.h native API.
358+ /// </summary>
359+ internal static class Wincred
360+ {
361+ private const int CredentialTypeGeneric = 1 ;
362+ private const int CredentialTypeDomainPassword = 2 ;
363+ private const int PersistenceTypeLocalComputer = 2 ;
364+ private const int ErrorNotFound = 1168 ;
365+ private const int CredMaxCredentialBlobSize = 5 * 512 ;
366+ private const string PackageNTLM = "NTLM" ;
367+
368+ public static string ? ReadCredentials ( string targetName )
355369 {
356- private const int CredentialTypeGeneric = 1 ;
357- private const int PersistenceTypeLocalComputer = 2 ;
358- private const int ErrorNotFound = 1168 ;
359- private const int CredMaxCredentialBlobSize = 5 * 512 ;
370+ if ( ! CredReadW ( targetName , CredentialTypeGeneric , 0 , out var credentialPtr ) )
371+ {
372+ var error = Marshal . GetLastWin32Error ( ) ;
373+ if ( error == ErrorNotFound ) return null ;
374+ throw new InvalidOperationException ( $ "Failed to read credentials (Error { error } )") ;
375+ }
360376
361- public static string ? ReadCredentials ( string targetName )
377+ try
362378 {
363- if ( ! CredReadW ( targetName , CredentialTypeGeneric , 0 , out var credentialPtr ) )
364- {
365- var error = Marshal . GetLastWin32Error ( ) ;
366- if ( error == ErrorNotFound ) return null ;
367- throw new InvalidOperationException ( $ "Failed to read credentials (Error { error } )") ;
368- }
379+ var cred = Marshal . PtrToStructure < CREDENTIALW > ( credentialPtr ) ;
380+ return Marshal . PtrToStringUni ( cred . CredentialBlob , cred . CredentialBlobSize / sizeof ( char ) ) ;
381+ }
382+ finally
383+ {
384+ CredFree ( credentialPtr ) ;
385+ }
386+ }
369387
370- try
371- {
372- var cred = Marshal . PtrToStructure < CREDENTIAL > ( credentialPtr ) ;
373- return Marshal . PtrToStringUni ( cred . CredentialBlob , cred . CredentialBlobSize / sizeof ( char ) ) ;
374- }
375- finally
388+ public static void WriteCredentials ( string targetName , string secret )
389+ {
390+ var byteCount = Encoding . Unicode . GetByteCount ( secret ) ;
391+ if ( byteCount > CredMaxCredentialBlobSize )
392+ throw new ArgumentOutOfRangeException ( nameof ( secret ) ,
393+ $ "The secret is greater than { CredMaxCredentialBlobSize } bytes") ;
394+
395+ var credentialBlob = Marshal . StringToHGlobalUni ( secret ) ;
396+ var cred = new CREDENTIALW
397+ {
398+ Type = CredentialTypeGeneric ,
399+ TargetName = targetName ,
400+ CredentialBlobSize = byteCount ,
401+ CredentialBlob = credentialBlob ,
402+ Persist = PersistenceTypeLocalComputer ,
403+ } ;
404+ try
405+ {
406+ if ( ! CredWriteW ( ref cred , 0 ) )
376407 {
377- CredFree ( credentialPtr ) ;
408+ var error = Marshal . GetLastWin32Error ( ) ;
409+ throw new InvalidOperationException ( $ "Failed to write credentials (Error { error } )") ;
378410 }
379411 }
380-
381- public static void WriteCredentials ( string targetName , string secret )
412+ finally
382413 {
383- var byteCount = Encoding . Unicode . GetByteCount ( secret ) ;
384- if ( byteCount > CredMaxCredentialBlobSize )
385- throw new ArgumentOutOfRangeException ( nameof ( secret ) ,
386- $ "The secret is greater than { CredMaxCredentialBlobSize } bytes") ;
414+ Marshal . FreeHGlobal ( credentialBlob ) ;
415+ }
416+ }
387417
388- var credentialBlob = Marshal . StringToHGlobalUni ( secret ) ;
389- var cred = new CREDENTIAL
390- {
391- Type = CredentialTypeGeneric ,
392- TargetName = targetName ,
393- CredentialBlobSize = byteCount ,
394- CredentialBlob = credentialBlob ,
395- Persist = PersistenceTypeLocalComputer ,
396- } ;
397- try
398- {
399- if ( ! CredWriteW ( ref cred , 0 ) )
400- {
401- var error = Marshal . GetLastWin32Error ( ) ;
402- throw new InvalidOperationException ( $ "Failed to write credentials (Error { error } )") ;
403- }
404- }
405- finally
406- {
407- Marshal . FreeHGlobal ( credentialBlob ) ;
408- }
418+ public static void DeleteCredentials ( string targetName )
419+ {
420+ if ( ! CredDeleteW ( targetName , CredentialTypeGeneric , 0 ) )
421+ {
422+ var error = Marshal . GetLastWin32Error ( ) ;
423+ if ( error == ErrorNotFound ) return ;
424+ throw new InvalidOperationException ( $ "Failed to delete credentials (Error { error } )") ;
409425 }
426+ }
427+
428+ public static void WriteDomainCredentials ( string domainName , string serverName , string username , string password )
429+ {
430+ var targetName = $ "{ domainName } /{ serverName } ";
431+ var targetInfo = new CREDENTIAL_TARGET_INFORMATIONW
432+ {
433+ TargetName = targetName ,
434+ DnsServerName = serverName ,
435+ DnsDomainName = domainName ,
436+ PackageName = PackageNTLM ,
437+ } ;
438+ var byteCount = Encoding . Unicode . GetByteCount ( password ) ;
439+ if ( byteCount > CredMaxCredentialBlobSize )
440+ throw new ArgumentOutOfRangeException ( nameof ( password ) ,
441+ $ "The secret is greater than { CredMaxCredentialBlobSize } bytes") ;
410442
411- public static void DeleteCredentials ( string targetName )
443+ var credentialBlob = Marshal . StringToHGlobalUni ( password ) ;
444+ var cred = new CREDENTIALW
412445 {
413- if ( ! CredDeleteW ( targetName , CredentialTypeGeneric , 0 ) )
446+ Type = CredentialTypeDomainPassword ,
447+ TargetName = targetName ,
448+ CredentialBlobSize = byteCount ,
449+ CredentialBlob = credentialBlob ,
450+ Persist = PersistenceTypeLocalComputer ,
451+ UserName = username ,
452+ } ;
453+ try
454+ {
455+ if ( ! CredWriteDomainCredentialsW ( ref targetInfo , ref cred , 0 ) )
414456 {
415457 var error = Marshal . GetLastWin32Error ( ) ;
416- if ( error == ErrorNotFound ) return ;
417- throw new InvalidOperationException ( $ "Failed to delete credentials (Error { error } )") ;
458+ throw new InvalidOperationException ( $ "Failed to write credentials (Error { error } )") ;
418459 }
419460 }
461+ finally
462+ {
463+ Marshal . FreeHGlobal ( credentialBlob ) ;
464+ }
465+ }
420466
421- [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
422- private static extern bool CredReadW ( string target , int type , int reservedFlag , out IntPtr credentialPtr ) ;
467+ [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
468+ private static extern bool CredReadW ( string target , int type , int reservedFlag , out IntPtr credentialPtr ) ;
423469
424- [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
425- private static extern bool CredWriteW ( [ In ] ref CREDENTIAL userCredential , [ In ] uint flags ) ;
470+ [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
471+ private static extern bool CredWriteW ( [ In ] ref CREDENTIALW userCredential , [ In ] uint flags ) ;
426472
427- [ DllImport ( "Advapi32.dll" , SetLastError = true ) ]
428- private static extern void CredFree ( [ In ] IntPtr cred ) ;
473+ [ DllImport ( "Advapi32.dll" , SetLastError = true ) ]
474+ private static extern void CredFree ( [ In ] IntPtr cred ) ;
429475
430- [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
431- private static extern bool CredDeleteW ( string target , int type , int flags ) ;
476+ [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
477+ private static extern bool CredDeleteW ( string target , int type , int flags ) ;
432478
433- [ StructLayout ( LayoutKind . Sequential ) ]
434- private struct CREDENTIAL
435- {
436- public int Flags ;
437- public int Type ;
479+ [ DllImport ( "Advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
480+ private static extern bool CredWriteDomainCredentialsW ( [ In ] ref CREDENTIAL_TARGET_INFORMATIONW target , [ In ] ref CREDENTIALW userCredential , [ In ] uint flags ) ;
438481
439- [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetName ;
482+ [ StructLayout ( LayoutKind . Sequential ) ]
483+ private struct CREDENTIALW
484+ {
485+ public int Flags ;
486+ public int Type ;
440487
441- [ MarshalAs ( UnmanagedType . LPWStr ) ] public string Comment ;
488+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetName ;
442489
443- public long LastWritten ;
444- public int CredentialBlobSize ;
445- public IntPtr CredentialBlob ;
446- public int Persist ;
447- public int AttributeCount ;
448- public IntPtr Attributes ;
490+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string Comment ;
449491
450- [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetAlias ;
492+ public long LastWritten ;
493+ public int CredentialBlobSize ;
494+ public IntPtr CredentialBlob ;
495+ public int Persist ;
496+ public int AttributeCount ;
497+ public IntPtr Attributes ;
451498
452- [ MarshalAs ( UnmanagedType . LPWStr ) ] public string UserName ;
453- }
499+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetAlias ;
500+
501+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string UserName ;
502+ }
503+
504+ [ StructLayout ( LayoutKind . Sequential ) ]
505+ private struct CREDENTIAL_TARGET_INFORMATIONW
506+ {
507+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetName ;
508+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string NetbiosServerName ;
509+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string DnsServerName ;
510+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string NetbiosDomainName ;
511+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string DnsDomainName ;
512+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string DnsTreeName ;
513+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string PackageName ;
514+
515+ public uint Flags ;
516+ public uint CredTypeCount ;
517+ public IntPtr CredTypes ;
454518 }
455519}
0 commit comments