Skip to content

SSH API Implementation #852

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
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
Binary file modified Lib/NativeBinaries/amd64/git2-9bbc8f3.dll
Binary file not shown.
Binary file modified Lib/NativeBinaries/amd64/git2-9bbc8f3.pdb
Binary file not shown.
Binary file modified Lib/NativeBinaries/x86/git2-9bbc8f3.dll
Binary file not shown.
Binary file modified Lib/NativeBinaries/x86/git2-9bbc8f3.pdb
Binary file not shown.
10 changes: 10 additions & 0 deletions LibGit2Sharp.Tests/GlobalSettingsFixture.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Text.RegularExpressions;
using LibGit2Sharp.Tests.TestHelpers;
using Xunit;
using System.Globalization;
using System.IO;

namespace LibGit2Sharp.Tests
{
Expand All @@ -13,6 +15,14 @@ public void CanGetMinimumCompiledInFeatures()

Assert.True(features.HasFlag(BuiltInFeatures.Threads));
Assert.True(features.HasFlag(BuiltInFeatures.Https));

bool hasSsh;
using (var sr = new StreamReader(typeof(GlobalSettingsFixture).Assembly.GetManifestResourceStream("LibGit2Sharp.Tests.ssh_used.txt")))
{
if (!bool.TryParse(sr.ReadLine(), out hasSsh))
hasSsh = false;
}
Assert.Equal(hasSsh, features.HasFlag(BuiltInFeatures.Ssh));
}

[Fact]
Expand Down
3 changes: 3 additions & 0 deletions LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,7 @@
<Target Name="AfterBuild">
</Target>
-->
<ItemGroup>
<EmbeddedResource Include="ssh_used.txt" />
</ItemGroup>
</Project>
1 change: 1 addition & 0 deletions LibGit2Sharp.Tests/ssh_used.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
False
55 changes: 55 additions & 0 deletions LibGit2Sharp/AuthenticationException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System;
using System.Runtime.Serialization;
using LibGit2Sharp.Core;

namespace LibGit2Sharp
{
/// <summary>
/// The exception that is thrown when an operation which requires an
/// authentication fails.
/// </summary>
[Serializable]
public class AuthenticationException : LibGit2SharpException
{
/// <summary>
/// Initializes a new instance of the <see cref="LibGit2Sharp.AuthenticationException"/> class.
/// </summary>
public AuthenticationException()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="LibGit2Sharp.AuthenticationException"/> class with a specified error message.
/// </summary>
/// <param name="message">A message that describes the error.</param>
public AuthenticationException(string message)
: base(message)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="LibGit2Sharp.AuthenticationException"/> class with a specified error message and a reference to the inner exception that is the cause of this exception.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception. If the <paramref name="innerException"/> parameter is not a null reference, the current exception is raised in a catch block that handles the inner exception.</param>
public AuthenticationException(string message, Exception innerException)
: base(message, innerException)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="LibGit2Sharp.AuthenticationException"/> class with a serialized data.
/// </summary>
/// <param name="info">The <see cref="SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="StreamingContext"/> that contains contextual information about the source or destination.</param>
protected AuthenticationException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}

internal AuthenticationException(string message, GitErrorCode code, GitErrorCategory category)
: base(message, code, category)
{
}
}
}
1 change: 1 addition & 0 deletions LibGit2Sharp/Core/Ensure.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ private static readonly Dictionary<GitErrorCode, Func<string, GitErrorCode, GitE
{ GitErrorCode.LockedFile, (m, r, c) => new LockedFileException(m, r, c) },
{ GitErrorCode.NotFound, (m, r, c) => new NotFoundException(m, r, c) },
{ GitErrorCode.Peel, (m, r, c) => new PeelException(m, r, c) },
{ GitErrorCode.Auth, (m, r, c) => new AuthenticationException(m, r, c) },
};

private static void HandleError(int result)
Expand Down
5 changes: 5 additions & 0 deletions LibGit2Sharp/Core/GitCredentialType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,10 @@ internal enum GitCredentialType
/// TODO
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea. I will.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, SshInteractive isn't implemented. So it's fine like this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I won't be implementing interactive support due to lack of time at the moment. Maybe later, since it requires exposing structs from libssh2 land and lots of binding.

Sorry. 🍶

/// </summary>
SshInteractive = (1 << 4),

/// <summary>
/// Username only information.
/// </summary>
UsernameQuery = (1 << 5),
}
}
18 changes: 18 additions & 0 deletions LibGit2Sharp/Core/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,24 @@ internal static extern int git_cred_userpass_plaintext_new(
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof (StrictUtf8Marshaler))] string username,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof (StrictUtf8Marshaler))] string password);

[DllImport(libgit2)]
internal static extern int git_cred_ssh_key_new(
out IntPtr cred,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string username,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string publickey,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string privatekey,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string passphrase);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is most likely wrong, we need an ASCII string passed, from what I saw.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/cc @carlosmn

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why we would want to restrict this to ASCII. UTF-8 is compatible with ASCII and what happens if I write my password in Spanish or German? I would need more letters than ASCII there.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, dunno why sending the passphrase in the current state isn't succeeding authentication.

I thought maybe libssh2 or something didn't support unicode, I'll investigate


[DllImport(libgit2)]
internal static extern int git_cred_username_new(
out IntPtr cred,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string username);

[DllImport(libgit2)]
internal static extern int git_cred_ssh_key_from_agent(
out IntPtr cred,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string username);

[DllImport(libgit2)]
internal static extern int git_describe_commit(
out DescribeResultSafeHandle describe,
Expand Down
4 changes: 4 additions & 0 deletions LibGit2Sharp/LibGit2Sharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,10 @@
<Compile Include="Core\RawContentStream.cs" />
<Compile Include="Core\Handles\OdbStreamSafeHandle.cs" />
<Compile Include="SupportedCredentialTypes.cs" />
<Compile Include="SshUserKeyCredentials.cs" />
<Compile Include="SshAgentCredentials.cs" />
<Compile Include="UsernameQueryCredentials.cs" />
<Compile Include="AuthenticationException.cs" />
</ItemGroup>
<ItemGroup>
<CodeAnalysisDictionary Include="CustomDictionary.xml" />
Expand Down
8 changes: 8 additions & 0 deletions LibGit2Sharp/RemoteCallbacks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,14 @@ private int GitCredentialHandler(out IntPtr ptr, IntPtr cUrl, IntPtr usernameFro
{
types |= SupportedCredentialTypes.Default;
}
if (credTypes.HasFlag(GitCredentialType.SshKey))
{
types |= SupportedCredentialTypes.Ssh;
}
if (credTypes.HasFlag(GitCredentialType.UsernameQuery))
{
types |= SupportedCredentialTypes.UsernameQuery;
}

var cred = CredentialsProvider(url, username, types);

Expand Down
32 changes: 32 additions & 0 deletions LibGit2Sharp/SshAgentCredentials.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using LibGit2Sharp.Core;

namespace LibGit2Sharp
{
/// <summary>
/// Class that holds SSH agent credentials for remote repository access.
/// </summary>
public sealed class SshAgentCredentials : Credentials
{
/// <summary>
/// Callback to acquire a credential object.
/// </summary>
/// <param name="cred">The newly created credential object.</param>
/// <returns>0 for success, &lt; 0 to indicate an error, &gt; 0 to indicate no credential was acquired.</returns>
protected internal override int GitCredentialHandler(out IntPtr cred)
{
if (Username == null)
{
throw new InvalidOperationException("SshAgentCredentials contains a null Username.");
}

return NativeMethods.git_cred_ssh_key_from_agent(out cred, Username);
}

/// <summary>
/// Username for SSH authentication.
/// </summary>
public string Username { get; set; }
}
}

61 changes: 61 additions & 0 deletions LibGit2Sharp/SshUserKeyCredentials.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System;
using LibGit2Sharp.Core;

namespace LibGit2Sharp
{
/// <summary>
/// Class that holds SSH username with key credentials for remote repository access.
/// </summary>
public sealed class SshUserKeyCredentials : Credentials
{
/// <summary>
/// Callback to acquire a credential object.
/// </summary>
/// <param name="cred">The newly created credential object.</param>
/// <returns>0 for success, &lt; 0 to indicate an error, &gt; 0 to indicate no credential was acquired.</returns>
protected internal override int GitCredentialHandler(out IntPtr cred)
{
if (Username == null)
{
throw new InvalidOperationException("SshUserKeyCredentials contains a null Username.");
}

if (Passphrase == null)
{
throw new InvalidOperationException("SshUserKeyCredentials contains a null Passphrase.");
}

if (PublicKey == null)
{
throw new InvalidOperationException("SshUserKeyCredentials contains a null PublicKey.");
}

if (PrivateKey == null)
{
throw new InvalidOperationException("SshUserKeyCredentials contains a null PrivateKey.");
}

return NativeMethods.git_cred_ssh_key_new(out cred, Username, PublicKey, PrivateKey, Passphrase);
}

/// <summary>
/// Username for SSH authentication.
/// </summary>
public string Username { get; set; }

/// <summary>
/// Public key file location for SSH authentication.
/// </summary>
public string PublicKey { get; set; }

/// <summary>
/// Private key file location for SSH authentication.
/// </summary>
public string PrivateKey { get; set; }

/// <summary>
/// Passphrase for SSH authentication.
/// </summary>
public string Passphrase { get; set; }
}
}
10 changes: 10 additions & 0 deletions LibGit2Sharp/SupportedCredentialTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,15 @@ public enum SupportedCredentialTypes
/// Ask Windows to provide its default credentials for the current user (e.g. NTLM)
/// </summary>
Default = (1 << 1),

/// <summary>
/// SSH with username and public/private key. (SshUserKeyCredentials, SshAgentCredentials)
/// </summary>
Ssh = (1 << 2),

/// <summary>
/// Queries the server with the specified username, then later returns the supported credential types.
/// </summary>
UsernameQuery = (1 << 3),
}
}
31 changes: 31 additions & 0 deletions LibGit2Sharp/UsernameQueryCredentials.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using LibGit2Sharp.Core;

namespace LibGit2Sharp
{
/// <summary>
/// Class that holds username query credentials for remote repository access.
/// </summary>
public sealed class UsernameQueryCredentials : Credentials
{
/// <summary>
/// Callback to acquire a credential object.
/// </summary>
/// <param name="cred">The newly created credential object.</param>
/// <returns>0 for success, &lt; 0 to indicate an error, &gt; 0 to indicate no credential was acquired.</returns>
protected internal override int GitCredentialHandler(out IntPtr cred)
{
if (Username == null)
{
throw new InvalidOperationException("UsernameQueryCredentials contains a null Username.");
}

return NativeMethods.git_cred_username_new(out cred, Username);
}

/// <summary>
/// Username for querying the server for supported authentication.
/// </summary>
public string Username { get; set; }
}
}
17 changes: 14 additions & 3 deletions UpdateLibgit2ToSha.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@
If set, run the libgit2 tests on the desired version.
.PARAMETER debug
If set, build the "Debug" configuration of libgit2, rather than "RelWithDebInfo" (default).
.PARAMETER ssh
If set embeds SSH at the path pointed to by the value.
#>

Param(
[string]$sha = 'HEAD',
[string]$vs = '10',
[string]$libgit2Name = '',
[switch]$test,
[switch]$debug
[switch]$debug,
[string]$ssh = ''
)

Set-StrictMode -Version Latest
Expand All @@ -28,12 +31,19 @@ $libgit2sharpDirectory = Split-Path $MyInvocation.MyCommand.Path
$libgit2Directory = Join-Path $libgit2sharpDirectory "libgit2"
$x86Directory = Join-Path $libgit2sharpDirectory "Lib\NativeBinaries\x86"
$x64Directory = Join-Path $libgit2sharpDirectory "Lib\NativeBinaries\amd64"
$sshFile = Join-Path $libgit2sharpDirectory "LibGit2Sharp.Tests\ssh_used.txt"

$build_clar = 'OFF'
if ($test.IsPresent) { $build_clar = 'ON' }
$configuration = "RelWithDebInfo"
if ($debug.IsPresent) { $configuration = "Debug" }

if (![string]::IsNullOrEmpty($ssh)) {
$embed_ssh = "-D EMBED_SSH_PATH=""$ssh"""
} else {
$embed_ssh = ''
}

function Run-Command([scriptblock]$Command, [switch]$Fatal, [switch]$Quiet) {
$output = ""
if ($Quiet) {
Expand Down Expand Up @@ -144,7 +154,7 @@ function Assert-Consistent-Naming($expected, $path) {
Run-Command -Quiet { & remove-item build -recurse -force }
Run-Command -Quiet { & mkdir build }
cd build
Run-Command -Quiet -Fatal { & $cmake -G "Visual Studio $vs" -D ENABLE_TRACE=ON -D "BUILD_CLAR=$build_clar" -D "LIBGIT2_FILENAME=$binaryFilename" -DSTDCALL=ON .. }
Run-Command -Quiet -Fatal { & $cmake -G "Visual Studio $vs" -D THREADSAFE=ON -D ENABLE_TRACE=ON -D "BUILD_CLAR=$build_clar" -D "LIBGIT2_FILENAME=$binaryFilename" -DSTDCALL=ON $embed_ssh .. }
Run-Command -Quiet -Fatal { & $cmake --build . --config $configuration }
if ($test.IsPresent) { Run-Command -Quiet -Fatal { & $ctest -V . } }
cd $configuration
Expand All @@ -157,7 +167,7 @@ function Assert-Consistent-Naming($expected, $path) {
cd ..
Run-Command -Quiet { & mkdir build64 }
cd build64
Run-Command -Quiet -Fatal { & $cmake -G "Visual Studio $vs Win64" -D THREADSAFE=ON -D ENABLE_TRACE=ON -D "BUILD_CLAR=$build_clar" -D "LIBGIT2_FILENAME=$binaryFilename" -DSTDCALL=ON ../.. }
Run-Command -Quiet -Fatal { & $cmake -G "Visual Studio $vs Win64" -D THREADSAFE=ON -D ENABLE_TRACE=ON -D "BUILD_CLAR=$build_clar" -D "LIBGIT2_FILENAME=$binaryFilename" -DSTDCALL=ON $embed_ssh ../.. }
Run-Command -Quiet -Fatal { & $cmake --build . --config $configuration }
if ($test.IsPresent) { Run-Command -Quiet -Fatal { & $ctest -V . } }
cd $configuration
Expand All @@ -180,6 +190,7 @@ namespace LibGit2Sharp.Core

sc -Encoding ASCII (Join-Path $libgit2sharpDirectory "Libgit2sharp\Core\NativeDllName.cs") $dllNameClass
sc -Encoding ASCII (Join-Path $libgit2sharpDirectory "Libgit2sharp\libgit2_hash.txt") $sha
sc -Encoding ASCII (Join-Path $libgit2sharpDirectory "Libgit2sharp.Tests\ssh_used.txt") (![string]::IsNullOrEmpty($ssh))

$buildProperties = @"
<?xml version="1.0" encoding="utf-8"?>
Expand Down
8 changes: 7 additions & 1 deletion build.libgit2sharp.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
LIBGIT2SHA=`cat ./LibGit2Sharp/libgit2_hash.txt`
SHORTSHA=${LIBGIT2SHA:0:7}
EXTRADEFINE="$1"
USESSH=${1-OFF}
SSH_FILE="$(pwd)/LibGit2Sharp.Tests/ssh_used.txt"

rm -rf libgit2/build
mkdir libgit2/build
Expand All @@ -11,13 +13,17 @@ export _BINPATH=`pwd`

cmake -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo \
-DBUILD_CLAR:BOOL=OFF \
-DUSE_SSH=OFF \
-DUSE_SSH=$USESSH \
-DENABLE_TRACE=ON \
-DLIBGIT2_FILENAME=git2-$SHORTSHA \
-DCMAKE_OSX_ARCHITECTURES="i386;x86_64" \
..
cmake --build .

shopt -s nocasematch
[[ $USESSH == "ON" ]] && echo "True" > $SSH_FILE || echo "False" > $SSH_FILE
shopt -u nocasematch

export LD_LIBRARY_PATH=$_BINPATH:$LD_LIBRARY_PATH
export DYLD_LIBRARY_PATH=$_BINPATH:$DYLD_LIBRARY_PATH

Expand Down