8000 Harden credentials by grokys · Pull Request #1207 · github/VisualStudio · GitHub
[go: up one dir, main page]

Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Harden credentials #1207

Merged
merged 3 commits into from
Nov 9, 2017
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
22 changes: 22 additions & 0 deletions src/CredentialManagement/Credential.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,28 @@ public Credential(
_lastWriteTime = DateTime.MinValue;
}

public static Credential Load(string key)
{
var result = new Credential();
result.Target = key;
result.Type = CredentialType.Generic;
return result.Load() ? result : null;
}

public static void Save(string key, string username, string password)
{
var result = new Credential(username, password, key);
result.Save();
}

public static void Delete(string key)
{
var result = new Credential();
result.Target = key;
result.Type = CredentialType.Generic;
result.Delete();
}

bool disposed;
void Dispose(bool disposing)
{
Expand Down
74 changes: 45 additions & 29 deletions src/GitHub.Api/WindowsKeychain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,37 @@ public Task<Tuple<string, string>> Load(HostAddress hostAddress)
{
Guard.ArgumentNotNull(hostAddress, nameof(hostAddress));

var key = GetKey(hostAddress.CredentialCacheKeyHost);
var keyGit = GetKeyGit(hostAddress.CredentialCacheKeyHost);
var keyHost = GetKeyHost(hostAddress.CredentialCacheKeyHost);

using (var credential = new Credential())
Tuple<string, string> result = null;

// Visual Studio requires two credentials, keyed as "{hostname}" (e.g. "https://github.com/") and
// "git:{hostname}" (e.g. "git:https://github.com"). We have a problem in that these credentials can
// potentially be overwritten by other applications, so we store an extra "master" key as
// "GitHub for Visual Studio - {hostname}". Whenever we read the credentials we overwrite the other
// two keys with the value from the master key. Older versions of GHfVS did not store this master key
// so if it does not exist, try to get the value from the "{hostname}" key.
using (var credential = Credential.Load(key))
using (var credentialGit = Credential.Load(keyGit))
using (var credentialHost = Credential.Load(keyHost))
{
credential.Target = keyHost;
credential.Type = CredentialType.Generic;
if (credential.Load())
return Task.FromResult(Tuple.Create(credential.Username, credential.Password));
if (credential != null)
{
result = Tuple.Create(credential.Username, credential.Password);
}
else if (credentialHost != null)
{
result = Tuple.Create(credentialHost.Username, credentialHost.Password);
}

if (result != null)
{
Save(result.Item1, result.Item2, hostAddress);
}
}

return Task.FromResult<Tuple<string, string>>(null);
return Task.FromResult(result);
}

/// <inheritdoc/>
Expand All @@ -39,18 +59,13 @@ public Task Save(string userName, string password, HostAddress hostAddress)
Guard.ArgumentNotEmptyString(password, nameof(password));
Guard.ArgumentNotNull(hostAddress, nameof(hostAddress));

var key = GetKey(hostAddress.CredentialCacheKeyHost);
var keyGit = GetKeyGit(hostAddress.CredentialCacheKeyHost);
var keyHost = GetKeyHost(hostAddress.CredentialCacheKeyHost);

using (var credential = new Credential(userName, password, keyGit))
{
credential.Save();
}

using (var credential = new Credential(userName, password, keyHost))
{
credential.Save();
}
Credential.Save(key, userName, password);
Credential.Save(keyGit, userName, password);
Credential.Save(keyHost, userName, password);

return Task.CompletedTask;
}
Expand All @@ -60,26 +75,27 @@ public Task Delete(HostAddress hostAddress)
{
Guard.ArgumentNotNull(hostAddress, nameof(hostAddress));

var key = GetKey(hostAddress.CredentialCacheKeyHost);
var keyGit = GetKeyGit(hostAddress.CredentialCacheKeyHost);
var keyHost = GetKeyHost(hostAddress.CredentialCacheKeyHost);

using (var credential = new Credential())
{
credential.Target = keyGit;
credential.Type = CredentialType.Generic;
credential.Delete();
}

using (var credential = new Credential())
{
credential.Target = keyHost;
credential.Type = CredentialType.Generic;
credential.Delete();
}
Credential.Delete(key);
Credential.Delete(keyGit);
Credential.Delete(keyHost);

return Task.CompletedTask;
}

static string GetKey(string key)
{
key = FormatKey(key);
if (key.StartsWith("git:", StringComparison.Ordinal))
key = key.Substring("git:".Length);
if (!key.EndsWith("/", StringComparison.Ordinal))
key += '/';
return "GitHub for Visual Studio - " + key;
}

static string GetKeyGit(string key)
{
key = FormatKey(key);
Expand Down
0