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