From c41b18d71a2543f2760aaeb2da8193c4d3565ee1 Mon Sep 17 00:00:00 2001 From: JamesWrigley Date: Mon, 5 Feb 2024 13:19:08 +0100 Subject: [PATCH] Add initial client support for GSSAPI authentication --- docs/src/changelog.md | 6 ++++++ docs/src/sessions_and_channels.md | 1 + docs/src/utilities.md | 1 + src/LibSSH.jl | 9 +++++++++ src/session.jl | 26 ++++++++++++++++++++++++++ test/LibSSHTests.jl | 23 ++++++++++++++++++++--- 6 files changed, 63 insertions(+), 3 deletions(-) diff --git a/docs/src/changelog.md b/docs/src/changelog.md index 885b8ee..463b243 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -3,6 +3,12 @@ This documents notable changes in LibSSH.jl. The format is based on [Keep a Changelog](https://keepachangelog.com). +## Unreleased + +### Added + +- Initial client support for GSSAPI authentication ([#3]). + ## [v0.2.0] - 2024-02-01 ### Changed diff --git a/docs/src/sessions_and_channels.md b/docs/src/sessions_and_channels.md index 4b7b3ac..6e57aee 100644 --- a/docs/src/sessions_and_channels.md +++ b/docs/src/sessions_and_channels.md @@ -51,6 +51,7 @@ userauth_password userauth_kbdint userauth_kbdint_getprompts userauth_kbdint_setanswers +userauth_gssapi get_error(::Session) Base.isopen(::Session) Base.close(::Session) diff --git a/docs/src/utilities.md b/docs/src/utilities.md index 57ccca6..09cca15 100644 --- a/docs/src/utilities.md +++ b/docs/src/utilities.md @@ -20,6 +20,7 @@ Depth = 10 ```@docs get_hexa +gssapi_available ``` ## Messages diff --git a/src/LibSSH.jl b/src/LibSSH.jl index bb35149..e6db170 100644 --- a/src/LibSSH.jl +++ b/src/LibSSH.jl @@ -158,6 +158,15 @@ function lib_version() VersionNumber(lib.LIBSSH_VERSION_MAJOR, lib.LIBSSH_VERSION_MINOR, lib.LIBSSH_VERSION_MICRO) end +""" +$(TYPEDSIGNATURES) + +Check if GSSAPI support is available (currently only Linux and FreeBSD). +""" +function gssapi_available() + Sys.islinux() || Sys.isfreebsd() +end + # Safe wrapper around poll_fd(). There's a race condition in older Julia # versions between the loop condition evaluation and this line, so we wrap # poll_fd() in a try-catch in case the bind (and thus the file descriptor) has diff --git a/src/session.jl b/src/session.jl index 6f09d2e..aab563f 100644 --- a/src/session.jl +++ b/src/session.jl @@ -558,6 +558,32 @@ end """ $(TYPEDSIGNATURES) +Authenticate with GSSAPI. This is not available on all platforms (see +[`gssapi_available`](@ref)). + +# Throws +- `ArgumentError`: If the session isn't connected. +- `ErrorException`: If GSSAPI support isn't available. + +Wrapper around [`lib.ssh_userauth_gssapi()`](@ref). +""" +function userauth_gssapi(session::Session) + if !isconnected(session) + throw(ArgumentError("Session is disconnected, cannot authenticate until it's connected")) + elseif !gssapi_available() + error("GSSAPI support is not available") + end + + ret = _session_trywait(session) do + lib.ssh_userauth_gssapi(session.ptr) + end + + return AuthStatus(ret) +end + +""" +$(TYPEDSIGNATURES) + Attempt to authenticate with the keyboard-interactive method. # Throws diff --git a/test/LibSSHTests.jl b/test/LibSSHTests.jl index d3eeb7f..dc91ca8 100644 --- a/test/LibSSHTests.jl +++ b/test/LibSSHTests.jl @@ -8,7 +8,7 @@ import Aqua import Literate import CURL_jll: curl import OpenSSH_jll -import ReTest: @testset, @test, @test_throws, @test_nowarn, @test_logs +import ReTest: @testset, @test, @test_throws, @test_nowarn, @test_broken, @test_logs import LibSSH import LibSSH as ssh @@ -191,8 +191,6 @@ end end @testset "Session" begin - @test ssh.lib_version() isa VersionNumber - session = ssh.Session("localhost"; auto_connect=false, log_verbosity=lib.SSH_LOG_NOLOG) @test !ssh.isconnected(session) @@ -272,6 +270,20 @@ end close(session) end end + + @testset "GSSAPI authentication" begin + DemoServer(2222; auth_methods=[ssh.AuthMethod_GSSAPI_MIC]) do + session = ssh.Session(Sockets.localhost, 2222) + @test ssh.isconnected(session) + + # TODO: figure out how to write proper tests for this. It's a little + # tricky since we'd need to have Kerberos running and configured + # correctly. In the meantime, this has been tested manually. + @test_broken ssh.userauth_gssapi(session) == ssh.AuthStatus_Success + + close(session) + end + end end # Helper function to start a DemoServer and create a session connected to it @@ -410,6 +422,11 @@ end @test true end +@testset "Utility functions" begin + @test ssh.lib_version() isa VersionNumber + @test ssh.gssapi_available() isa Bool +end + @testset "Aqua.jl" begin Aqua.test_all(ssh) end