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

Security: Do not persists ADA Refesh Tokens #201

Merged
merged 1 commit into from
May 5, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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