10000 Merge pull request #1134 from libgit2/cmn/certs · Skybladev2/libgit2sharp@c7f10bb · GitHub
[go: up one dir, main page]

Skip to content

Commit c7f10bb

Browse files
committed
Merge pull request libgit2#1134 from libgit2/cmn/certs
Certificate validation callback
2 parents 6564390 + 0558216 commit c7f10bb

16 files changed

+323
-1
lines changed

LibGit2Sharp.Tests/CloneFixture.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,66 @@ public void CanCloneFromBBWithCredentials(string url, string user, string pass,
237237
}
238238
}
239239

240+
[SkippableTheory]
241+
[InlineData("https://github.com/libgit2/TestGitRepository.git", "github.com", typeof(CertificateX509))]
242+
[InlineData("git@github.com:libgit2/TestGitRepository.git", "github.com", typeof(CertificateSsh))]
243+
public void CanInspectCertificateOnClone(string url, string hostname, Type certType)
244+
{
245+
var scd = BuildSelfCleaningDirectory();
246+
247+
InconclusiveIf(
248+
() =>
249+
certType == typeof (CertificateSsh) && !GlobalSettings.Version.Features.HasFlag(BuiltInFeatures.Ssh),
250+
"SSH not supported");
251+
252+
bool wasCalled = false;
253+
bool checksHappy = false;
254+
255+
var options = new CloneOptions {
256+
CertificateCheck = (cert, valid, host) => {
257+
wasCalled = true;
258+
259+
Assert.Equal(hostname, host);
260+
Assert.Equal(certType, cert.GetType());
261+
262+
if (certType == typeof(CertificateX509)) {
263+
Assert.True(valid);
264+
var x509 = ((CertificateX509)cert).Certificate;
265+
// we get a string with the different fields instead of a structure, so...
266+
Assert.True(x509.Subject.Contains("CN=github.com,"));
267+
checksHappy = true;
268+
return false;
269+
}
270+
271+
if (certType == typeof(CertificateSsh)) {
272+
var hostkey = (CertificateSsh)cert;
273+
Assert.True(hostkey.HasMD5);
274+
/*
275+
* Once you've connected and thus your ssh has stored the hostkey,
276+
* you can get the hostkey for a host with
277+
*
278+
* ssh-keygen -F github.com -l | tail -n 1 | cut -d ' ' -f 2 | tr -d ':'
279+
*
280+
* though GitHub's hostkey won't change anytime soon.
< F438 /code>
281+
*/
282+
Assert.Equal("1627aca576282d36631b564debdfa648",
283+
BitConverter.ToString(hostkey.HashMD5).ToLower().Replace("-", ""));
284+
checksHappy = true;
285+
return false;
286+
}
287+
288+
return false;
289+
},
290+
};
291+
292+
Assert.Throws<UserCancelledException>(() =>
293+
Repository.Clone(url, scd.DirectoryPath, options)
294+
);
295+
296+
Assert.True(wasCalled);
297+
Assert.True(checksHappy);
298+
}
299+
240300
[Fact]
241301
public void CloningAnUrlWithoutPathThrows()
242302
{

LibGit2Sharp/Certificate.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace LibGit2Sharp
2+
{
3+
/// <summary>
4+
/// Top-level certificate type. The usable certificates inherit from this class.
5+
/// </summary>
6+
public abstract class Certificate
7+
{
8+
}
9+
}

LibGit2Sharp/CertificateSsh.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using LibGit2Sharp.Core;
2+
3+
namespace LibGit2Sharp
4+
{
5+
/// <summary>
6+
/// This class represents the hostkey which is avaiable when connecting to a SSH host.
7+
/// </summary>
8+
public class CertificateSsh : Certificate
9+
{
10+
/// <summary>
11+
/// For mocking purposes
12+
/// </summary>
13+
protected CertificateSsh()
14+
{ }
15+
16+
/// <summary>
17+
/// The MD5 hash of the host. Meaningful if <see cref="HasMD5"/> is true
18+
/// </summary>
19+
public readonly byte[] HashMD5;
20+
21+
/// <summary>
22+
/// The SHA1 hash of the host. Meaningful if <see cref="HasSHA1"/> is true
23+
/// </summary>
24+
public readonly byte[] HashSHA1;
25+
26+
/// <summary>
27+
/// True if we have the MD5 hostkey hash from the server
28+
/// </summary>
29+
public readonly bool HasMD5;
30+
31+
/// <summary>
32+
/// True if we have the SHA1 hostkey hash from the server
33+
/// </summary>
34+
public readonly bool HasSHA1;
35+
36+
/// <summary>
37+
/// True if we have the SHA1 hostkey hash from the server
38+
/// </summary>public readonly bool HasSHA1;
39+
40+
internal CertificateSsh(GitCertificateSsh cert)
41+
{
42+
43+
HasMD5 = cert.type.HasFlag(GitCertificateSshType.MD5);
44+
HasSHA1 = cert.type.HasFlag(GitCertificateSshType.SHA1);
45+
46+
HashMD5 = new byte[16];
47+
cert.HashMD5.CopyTo(HashMD5, 0);
48+
49+
HashSHA1 = new byte[20];
50+
cert.HashSHA1.CopyTo(HashSHA1, 0);
51+
}
52+
}
53+
}

LibGit2Sharp/CertificateX509.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System.Runtime.InteropServices;
2+
using System.Security.Cryptography.X509Certificates;
3+
using LibGit2Sharp.Core;
4+
5+
namespace LibGit2Sharp
6+
{
7+
/// <summary>
8+
/// Conains a X509 certificate
9+
/// </summary>
10+
public class CertificateX509 : Certificate
11+
{
12+
13+
/// <summary>
14+
/// For mocking purposes
15+
/// </summary>
16+
protected CertificateX509()
17+
{ }
18+
19+
/// <summary>
20+
/// The certificate.
21+
/// </summary>
22+
public virtual X509Certificate Certificate { get; private set; }
23+
24+
internal CertificateX509(GitCertificateX509 cert)
25+
{
26+
int len = checked((int) cert.len.ToUInt32());
27+
byte[] data = new byte[len];
28+
Marshal.Copy(cert.data, data, 0, len);
29+
Certificate = new X509Certificate(data);
30+
}
31+
}
32+
}

LibGit2Sharp/Core/GitCertificate.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System.Runtime.InteropServices;
2+
3+
namespace LibGit2Sharp.Core
4+
{
5+
[StructLayout(LayoutKind.Sequential)]
6+
internal struct GitCertificate
7+
{
8+
public GitCertificateType type;
9+
}
10+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Runtime.InteropServices;
2+
3+
namespace LibGit2Sharp.Core
4+
{
5+
[StructLayout(LayoutKind.Sequential)]
6+
internal struct GitCertificateSsh
7+
{
8+
public GitCertificateType cert_type;
9+
public GitCertificateSshType type;
10+
11+
/// <summary>
12+
/// The MD5 hash (if appropriate)
13+
/// </summary>
14+
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
15+
public byte[] HashMD5;
16+
17+
/// <summary>
18+
/// The MD5 hash (if appropriate)
19+
/// </summary>
20+
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
21+
public byte[] HashSHA1;
22+
}
23+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
3+
namespace LibGit2Sharp.Core
4+
{
5+
[Flags]
6+
internal enum GitCertificateSshType
7+
{
8+
MD5 = (1 << 0),
9+
SHA1 = (1 << 1),
10+
}
11+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
namespace LibGit2Sharp.Core
2+
{
3+
/// <summary>
4+
/// Git certificate types to present to the user
5+
/// </summary>
6+
internal enum GitCertificateType
7+
{
8+
/// <summary>
9+
/// No information about the certificate is available.
10+
/// </summary>
11+
None = 0,
12+
13+
/// <summary>
14+
/// The certificate is a x509 certificate
15+
/// </summary>
16+
X509 = 1,
17+
18+
/// <summary>
19+
/// The "certificate" is in fact a hostkey identification for ssh.
20+
/// </summary>
21+
Hostkey = 2,
22+
23+
/// <summary>
24+
/// The "certificate" is in fact a collection of `name:content` strings
25+
/// containing information about the certificate.
26+
/// </summary>
27+
StrArray = 3,
28+
}
29+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
using System.Runtime.InteropServices;
3+
4+
namespace LibGit2Sharp.Core
5+
{
6+
[StructLayout(LayoutKind.Sequential)]
7+
internal struct GitCertificateX509
8+
{
9+
/// <summary>
10+
/// Type of the certificate, in this case, GitCertificateType.X509
11+
/// </summary>
12+
public GitCertificateType cert_type;
13+
/// <summary>
14+
/// Pointer to the X509 certificate data
15+
/// </summary>
16+
public IntPtr data;
17+
/// <summary>
18+
/// The size of the certificate data
19+
/// </summary>
20+
public UIntPtr len;
21+
}
22+
}

LibGit2Sharp/Core/GitRemoteCallbacks.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ internal struct GitRemoteCallbacks
1717

1818
internal NativeMethods.git_cred_acquire_cb acquire_credentials;
1919

20-
internal IntPtr certificate_check;
20+
internal NativeMethods.git_transport_certificate_check_cb certificate_check;
2121

2222
internal NativeMethods.git_transfer_progress_callback download_progress;
2323

0 commit comments

Comments
 (0)
0