1
1
using System ;
2
+ using System . Net . Sockets ;
2
3
using System . Runtime . InteropServices ;
3
4
using System . Text ;
4
5
using System . Text . Json ;
@@ -307,7 +308,7 @@ public WindowsCredentialBackend(string credentialsTargetName)
307
308
308
309
public Task < RawCredentials ? > ReadCredentials ( CancellationToken ct = default )
309
310
{
310
- var raw = NativeApi . ReadCredentials ( _credentialsTargetName ) ;
311
+ var raw = Wincred . ReadCredentials ( _credentialsTargetName ) ;
311
312
if ( raw == null ) return Task . FromResult < RawCredentials ? > ( null ) ;
312
313
313
314
RawCredentials ? credentials ;
@@ -326,115 +327,179 @@ public WindowsCredentialBackend(string credentialsTargetName)
326
327
public Task WriteCredentials ( RawCredentials credentials , CancellationToken ct = default )
327
328
{
328
329
var raw = JsonSerializer . Serialize ( credentials , RawCredentialsJsonContext . Default . RawCredentials ) ;
329
- NativeApi . WriteCredentials ( _credentialsTargetName , raw ) ;
330
+ Wincred . WriteCredentials ( _credentialsTargetName , raw ) ;
330
331
return Task . CompletedTask ;
331
332
}
332
333
333
334
public Task DeleteCredentials ( CancellationToken ct = default )
334
335
{
335
- NativeApi . DeleteCredentials ( _credentialsTargetName ) ;
336
+ Wincred . DeleteCredentials ( _credentialsTargetName ) ;
336
337
return Task . CompletedTask ;
337
338
}
338
339
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 )
340
355
{
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
+ }
345
362
346
- public static string ? ReadCredentials ( string targetName )
363
+ try
347
364
{
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
+ }
354
373
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 ) )
361
393
{
362
- CredFree ( credentialPtr ) ;
394
+ var error = Marshal . GetLastWin32Error ( ) ;
395
+ throw new InvalidOperationException ( $ "Failed to write credentials (Error { error } )") ;
363
396
}
364
397
}
365
-
366
- public static void WriteCredentials ( string targetName , string secret )
398
+ finally
367
399
{
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
+ }
372
403
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 } )") ;
394
411
}
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") ;
395
428
396
- public static void DeleteCredentials ( string targetName )
429
+ var credentialBlob = Marshal . StringToHGlobalUni ( password ) ;
430
+ var cred = new CREDENTIALW
397
431
{
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 ) )
399
442
{
400
443
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 } )") ;
403
445
}
404
446
}
447
+ finally
448
+ {
449
+ Marshal . FreeHGlobal ( credentialBlob ) ;
450
+ }
451
+ }
405
452
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 ) ;
408
455
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 ) ;
411
458
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 ) ;
414
461
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 ) ;
417
464
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 ) ;
423
467
424
- [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetName ;
468
+ [ StructLayout ( LayoutKind . Sequential ) ]
469
+ private struct CREDENTIALW
470
+ {
471
+ public int Flags ;
472
+ public int Type ;
425
473
426
- [ MarshalAs ( UnmanagedType . LPWStr ) ] public string Comment ;
474
+ [ MarshalAs ( UnmanagedType . LPWStr ) ] public string TargetName ;
427
475
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 ;
434
477
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 ;
436
484
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 ;
439
504
}
440
505
}
0 commit comments