@@ -322,7 +322,7 @@ public WindowsCredentialBackend(string credentialsTargetName)
322
322
323
323
public Task < RawCredentials ? > ReadCredentials ( CancellationToken ct = default )
324
324
{
325
- var raw = NativeApi . ReadCredentials ( _credentialsTargetName ) ;
325
+ var raw = Wincred . ReadCredentials ( _credentialsTargetName ) ;
326
326
if ( raw == null ) return Task . FromResult < RawCredentials ? > ( null ) ;
327
327
328
328
RawCredentials ? credentials ;
@@ -341,115 +341,179 @@ public WindowsCredentialBackend(string credentialsTargetName)
341
341
public Task WriteCredentials ( RawCredentials credentials , CancellationToken ct = default )
342
342
{
343
343
var raw = JsonSerializer . Serialize ( credentials , RawCredentialsJsonContext . Default . RawCredentials ) ;
344
- NativeApi . WriteCredentials ( _credentialsTargetName , raw ) ;
344
+ Wincred . WriteCredentials ( _credentialsTargetName , raw ) ;
345
345
return Task . CompletedTask ;
346
346
}
347
347
348
348
public Task DeleteCredentials ( CancellationToken ct = default )
349
349
{
350
- NativeApi . DeleteCredentials ( _credentialsTargetName ) ;
350
+ Wincred . DeleteCredentials ( _credentialsTargetName ) ;
351
351
return Task . CompletedTask ;
352
352
}
353
353
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 )
355
369
{
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
+ }
360
376
361
- public static string ? ReadCredentials ( string targetName )
377
+ try
362
378
{
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
+ }
369
387
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 ) )
376
407
{
377
- CredFree ( credentialPtr ) ;
408
+ var error = Marshal . GetLastWin32Error ( ) ;
409
+ throw new InvalidOperationException ( $ "Failed to write credentials (Error { error } )") ;
378
410
}
379
411
}
380
-
381
- public static void WriteCredentials ( string targetName , string secret )
412
+ finally
382
413
{
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
+ }
387
417
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 } )") ;
409
425
}
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") ;
410
442
411
- public static void DeleteCredentials ( string targetName )
443
+ var credentialBlob = Marshal . StringToHGlobalUni ( password ) ;
444
+ var cred = new CREDENTIALW
412
445
{
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 ) )
414
456
{
415
457
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 } )") ;
418
459
}
419
460
}
461
+ finally
462
+ {
463
+ Marshal . FreeHGlobal ( credentialBlob ) ;
464
+ }
465
+ }
420
466
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 ) ;
423
469
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 ) ;
426
472
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 ) ;
429
475
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 ) ;
432
478
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 ) ;
438
481
439
- [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetName ;
482
+ [ StructLayout ( LayoutKind . Sequential ) ]
483
+ private struct CREDENTIALW
484
+ {
485
+ public int Flags ;
486
+ public int Type ;
440
487
441
- [ MarshalAs ( UnmanagedType . LPWStr ) ] public string Comment ;
488
+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetName ;
442
489
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 ;
449
491
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 ;
451
498
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 ;
454
518
}
455
519
}
0 commit comments