Skip to content
This repository has been archived by the owner on Jul 15, 2023. It is now read-only.

Commit

Permalink
Merge pull request #201 from whoisj/no-persist-ada-tokens
Browse files Browse the repository at this point in the history
Security: Do not persists ADA Refesh Tokens
  • Loading branch information
J Wyman committed May 5, 2016
2 parents 8e9d4d4 + 4673508 commit 5145956
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 1 deletion.
35 changes: 35 additions & 0 deletions Microsoft.Alm.Authentication/BaseSecureStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,41 @@ protected void Delete(string targetName)

protected abstract string GetTargetName(TargetUri targetUri);

protected void PurgeCredentials(string @namespace)
{
string filter = @namespace + "*";
int count;
IntPtr credentialArrayPtr;

if (NativeMethods.CredEnumerate(filter, 0, out count, out credentialArrayPtr))
{
for (int i = 0; i < count; i += 1)
{
int offset = i * Marshal.SizeOf(typeof(IntPtr));
IntPtr credentialPtr = Marshal.ReadIntPtr(credentialArrayPtr, offset);

if (credentialPtr != IntPtr.Zero)
{
NativeMethods.Credential credential = Marshal.PtrToStructure<NativeMethods.Credential>(credentialPtr);

if (!NativeMethods.CredDelete(credential.TargetName, credential.Type, 0))
{
int error = Marshal.GetLastWin32Error();
Debug.Fail("Failed with error code " + error.ToString("X"));
}

}
}

NativeMethods.CredFree(credentialArrayPtr);
}
else
{
int error = Marshal.GetLastWin32Error();
Debug.Fail("Failed with error code " + error.ToString("X"));
}
}

protected Credential ReadCredentials(string targetName)
{
Trace.WriteLine("BaseSecureStore::ReadCredentials");
Expand Down
21 changes: 20 additions & 1 deletion Microsoft.Alm.Authentication/BaseVstsAuthentication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using System.Net;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Text;
using System.Runtime.InteropServices;

namespace Microsoft.Alm.Authentication
{
Expand All @@ -27,11 +29,14 @@ private BaseVstsAuthentication(VstsTokenScope tokenScope, ICredentialStore perso
AdalTrace.TraceSource.Switch.Level = SourceLevels.Off;
AdalTrace.LegacyTraceSwitch.Level = TraceLevel.Off;

// attempt to purge any cached ada tokens.
SecurityPurgeAdaTokens(new SecretStore(AdalRefreshPrefx));

this.ClientId = DefaultClientId;
this.Resource = DefaultResource;
this.TokenScope = tokenScope;
this.PersonalAccessTokenStore = personalAccessTokenStore;
this.AdaRefreshTokenStore = new SecretStore(AdalRefreshPrefx);
this.AdaRefreshTokenStore = new SecretCache(AdalRefreshPrefx);
this.VstsAuthority = new VstsAzureAuthority();
}
/// <summary>
Expand Down Expand Up @@ -337,5 +342,19 @@ public static bool GetAuthentication(

return authentication != null;
}

/// <summary>
/// Attempts to enumerate and delete any and all Azure Directory Authentication
/// Refresh tokens caches by GCM asynchronously.
/// </summary>
/// <returns>A <see cref="Task"/> for the async action.</returns>
private static Task SecurityPurgeAdaTokens(SecretStore adaStore)
{
// this can and should be done asynchronously to minimize user impact
return Task.Run(() =>
{
adaStore.PurgeCredentials();
});
}
}
}
33 changes: 33 additions & 0 deletions Microsoft.Alm.Authentication/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,39 @@ internal static class NativeMethods
[DllImport(Advapi32, CharSet = CharSet.Unicode, EntryPoint = "CredFree", SetLastError = true)]
internal static extern void CredFree(IntPtr credential);

/// <summary>
/// Enumerates the credentials from the user's credential set. The credential set used
/// is the one associated with the logon session of the current token. The token must
/// not have the user's SID disabled.
/// </summary>
/// <param name="targetNameFilter">
/// <para>Pointer to a null-terminated string that contains the filter for the returned
/// credentials. Only credentials with a TargetName matching the filter will be returned.
/// The filter specifies a name prefix followed by an asterisk. For instance, the filter
/// "FRED*" will return all credentials with a TargetName beginning with the string
/// "FRED".</para>
/// <para>If <see langword="null"/> is specified, all credentials will be returned.</para>
/// </param>
/// <param name="flags">The value of this parameter can be zero or more values combined
/// with a bitwise-OR operation.</param>
/// <param name="count">Count of the credentials returned in the <paramref name="credenitalsArrayPtr"/>.</param>
/// <param name="credenitalsArrayPtr">
/// <para>Pointer to an array of pointers to credentials. The returned credential is a
/// single allocated block. Any pointers contained within the buffer are pointers to
/// locations within this single allocated block.</para>
/// <para>The single returned buffer must be freed by calling <see cref="CredFree"/>.</para>
/// </param>
/// <returns></returns>
[DllImport(Advapi32, CharSet = CharSet.Unicode, EntryPoint = "CredEnumerateW", SetLastError = true)]
internal static extern bool CredEnumerate(string targetNameFilter, CredentialEnumerateFlags flags, out int count, out IntPtr credenitalsArrayPtr);

[Flags]
internal enum CredentialEnumerateFlags : uint
{
None = 0,
AllCredentials = 1 << 0,
}

[Flags]
internal enum CredentialFlags : uint
{
Expand Down
8 changes: 8 additions & 0 deletions Microsoft.Alm.Authentication/SecretStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ public void DeleteToken(TargetUri targetUri)
_tokenCache.DeleteToken(targetUri);
}

/// <summary>
/// Purges all credenitals from the store.
/// </summary>
public void PurgeCredentials()
{
PurgeCredentials(_namespace);
}

/// <summary>
/// Reads credentials for a target URI from the credential store
/// </summary>
Expand Down

0 comments on commit 5145956

Please sign in to comment.