aboutsummaryrefslogtreecommitdiff
path: root/ssh/server_test.go
diff options
context:
space:
mode:
authorNicola Murino <nicola.murino@gmail.com>2024-12-15 20:02:38 +0100
committerNicola Murino <nicola.murino@gmail.com>2025-09-27 12:43:41 -0700
commit2beaa59a3c994e5d01b6d58dc348dcd6d814ef26 (patch)
treea6ca9948e1e9a7dd335bb182059a0a21c8518105 /ssh/server_test.go
parent66c3d8ce714c31eb5a8adb6c931b4e29f5bebcf5 (diff)
downloadgo-x-crypto-2beaa59a3c994e5d01b6d58dc348dcd6d814ef26.tar.xz
ssh: add VerifiedPublicKeyCallback
Fixes golang/go#70795 Change-Id: I9b7c91f35f89495d1e9b5f6ec0c036c02a61d774 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/636335 Reviewed-by: Michael Knyszek <mknyszek@google.com> Reviewed-by: Junyang Shao <shaojunyang@google.com> Reviewed-by: Ilia Mirkin <imirkin@alum.mit.edu> Reviewed-by: Filippo Valsorda <filippo@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Jorge Hernández <jorgehcrda39@gmail.com>
Diffstat (limited to 'ssh/server_test.go')
-rw-r--r--ssh/server_test.go374
1 files changed, 374 insertions, 0 deletions
diff --git a/ssh/server_test.go b/ssh/server_test.go
index 5bd18db..e48f7e3 100644
--- a/ssh/server_test.go
+++ b/ssh/server_test.go
@@ -434,6 +434,380 @@ func TestPreAuthConnAndBanners(t *testing.T) {
}
}
+func TestVerifiedPublicKeyCallback(t *testing.T) {
+ c1, c2, err := netPipe()
+ if err != nil {
+ t.Fatalf("netPipe: %v", err)
+ }
+ defer c1.Close()
+ defer c2.Close()
+
+ extraKey := "extra"
+ extraDataString := "just a string"
+
+ serverConf := &ServerConfig{
+ VerifiedPublicKeyCallback: func(conn ConnMetadata, key PublicKey, permissions *Permissions, signatureAlgorithm string) (*Permissions, error) {
+ if permissions != nil && permissions.ExtraData != nil {
+ if !reflect.DeepEqual(map[any]any{extraKey: extraDataString}, permissions.ExtraData) {
+ t.Errorf("expected extra data: %v; got: %v", extraDataString, permissions.ExtraData)
+ }
+ } else {
+ t.Error("expected extra data is missing")
+ }
+ if signatureAlgorithm != KeyAlgoRSASHA256 {
+ t.Errorf("expected signature algorithm: %q; got: %q", KeyAlgoRSASHA256, signatureAlgorithm)
+ }
+ return permissions, nil
+ },
+ PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
+ return &Permissions{ExtraData: map[any]any{extraKey: extraDataString}}, nil
+ },
+ }
+ serverConf.AddHostKey(testSigners["rsa"])
+
+ done := make(chan struct{})
+ go func() {
+ defer close(done)
+ conn, _, _, err := NewServerConn(c1, serverConf)
+ if err != nil {
+ t.Errorf("unexpected server error: %v", err)
+ }
+ if !reflect.DeepEqual(map[any]any{extraKey: extraDataString}, conn.Permissions.ExtraData) {
+ t.Errorf("expected extra data: %v; got: %v", extraDataString, conn.Permissions.ExtraData)
+ }
+ }()
+
+ clientConf := ClientConfig{
+ User: "user",
+ Auth: []AuthMethod{
+ PublicKeys(testSigners["rsa"]),
+ },
+ HostKeyCallback: InsecureIgnoreHostKey(),
+ }
+
+ _, _, _, err = NewClientConn(c2, "", &clientConf)
+ if err != nil {
+ t.Fatal(err)
+ }
+ <-done
+}
+
+func TestVerifiedPublicCallbackPartialSuccess(t *testing.T) {
+ c1, c2, err := netPipe()
+ if err != nil {
+ t.Fatalf("netPipe: %v", err)
+ }
+ defer c1.Close()
+ defer c2.Close()
+
+ serverConf := &ServerConfig{
+ PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
+ if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
+ return nil, nil
+ }
+ return nil, errors.New("invalid credentials")
+ },
+ VerifiedPublicKeyCallback: func(conn ConnMetadata, key PublicKey, permissions *Permissions, signatureAlgorithm string) (*Permissions, error) {
+ if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
+ return nil, &PartialSuccessError{
+ Next: ServerAuthCallbacks{
+ PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
+ if string(password) == clientPassword {
+ return nil, nil
+ }
+ return nil, nil
+ },
+ },
+ }
+ }
+ return nil, errors.New("invalid credentials")
+ },
+ }
+ serverConf.AddHostKey(testSigners["rsa"])
+
+ clientConf := ClientConfig{
+ User: "user",
+ Auth: []AuthMethod{
+ PublicKeys(testSigners["rsa"]),
+ Password(clientPassword),
+ },
+ HostKeyCallback: InsecureIgnoreHostKey(),
+ }
+
+ go NewServerConn(c1, serverConf)
+
+ _, _, _, err = NewClientConn(c2, "", &clientConf)
+ if err != nil {
+ t.Fatalf("client login error: %s", err)
+ }
+}
+
+func TestVerifiedPublicKeyCallbackPwdAndKey(t *testing.T) {
+ c1, c2, err := netPipe()
+ if err != nil {
+ t.Fatalf("netPipe: %v", err)
+ }
+ defer c1.Close()
+ defer c2.Close()
+
+ serverConf := &ServerConfig{
+ PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
+ if string(password) == clientPassword {
+ return nil, &PartialSuccessError{
+ Next: ServerAuthCallbacks{
+ PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
+ if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
+ return nil, nil
+ }
+ return nil, errors.New("invalid credentials")
+ },
+ },
+ }
+ }
+ return nil, errors.New("invalid credentials")
+
+ },
+ VerifiedPublicKeyCallback: func(conn ConnMetadata, key PublicKey, permissions *Permissions, signatureAlgorithm string) (*Permissions, error) {
+ if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
+ return nil, nil
+ }
+ return nil, errors.New("invalid credentials")
+ },
+ }
+ serverConf.AddHostKey(testSigners["rsa"])
+
+ clientConf := ClientConfig{
+ User: "user",
+ Auth: []AuthMethod{
+ Password(clientPassword),
+ PublicKeys(testSigners["rsa"]),
+ },
+ HostKeyCallback: InsecureIgnoreHostKey(),
+ }
+
+ go NewServerConn(c1, serverConf)
+
+ _, _, _, err = NewClientConn(c2, "", &clientConf)
+ if err != nil {
+ t.Fatalf("client login error: %s", err)
+ }
+}
+
+func TestVerifiedPubKeyCallbackAuthMethods(t *testing.T) {
+ c1, c2, err := netPipe()
+ if err != nil {
+ t.Fatalf("netPipe: %v", err)
+ }
+ defer c1.Close()
+ defer c2.Close()
+
+ serverConf := &ServerConfig{
+ PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
+ return nil, nil
+ },
+ VerifiedPublicKeyCallback: func(conn ConnMetadata, key PublicKey, permissions *Permissions, signatureAlgorithm string) (*Permissions, error) {
+ if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
+ return nil, nil
+ }
+ return nil, errors.New("invalid credentials")
+ },
+ }
+ serverConf.AddHostKey(testSigners["rsa"])
+
+ clientConf := ClientConfig{
+ User: "user",
+ Auth: []AuthMethod{
+ PublicKeys(testSigners["rsa"]),
+ },
+ HostKeyCallback: InsecureIgnoreHostKey(),
+ }
+
+ go NewServerConn(c1, serverConf)
+
+ _, _, _, err = NewClientConn(c2, "", &clientConf)
+ if err == nil {
+ t.Fatal("client login succeed with only VerifiedPublicKeyCallback defined")
+ }
+}
+
+func TestVerifiedPubKeyCallbackError(t *testing.T) {
+ c1, c2, err := netPipe()
+ if err != nil {
+ t.Fatalf("netPipe: %v", err)
+ }
+ defer c1.Close()
+ defer c2.Close()
+
+ serverConf := &ServerConfig{
+ PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
+ return nil, nil
+ },
+ VerifiedPublicKeyCallback: func(conn ConnMetadata, key PublicKey, permissions *Permissions, signatureAlgorithm string) (*Permissions, error) {
+ return nil, errors.New("invalid credentials")
+ },
+ }
+ serverConf.AddHostKey(testSigners["rsa"])
+
+ clientConf := ClientConfig{
+ User: "user",
+ Auth: []AuthMethod{
+ PublicKeys(testSigners["rsa"]),
+ },
+ HostKeyCallback: InsecureIgnoreHostKey(),
+ }
+
+ go NewServerConn(c1, serverConf)
+
+ _, _, _, err = NewClientConn(c2, "", &clientConf)
+ if err == nil {
+ t.Fatal("client login succeed with VerifiedPublicKeyCallback returning an error")
+ }
+}
+
+func TestVerifiedPublicCallbackPartialSuccessBadUsage(t *testing.T) {
+ c1, c2, err := netPipe()
+ if err != nil {
+ t.Fatalf("netPipe: %v", err)
+ }
+ defer c1.Close()
+ defer c2.Close()
+
+ serverConf := &ServerConfig{
+ PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
+ if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
+ // Returning PartialSuccessError is not permitted when
+ // VerifiedPublicKeyCallback is defined. This callback is
+ // invoked for both query requests and real authentications,
+ // while VerifiedPublicKeyCallback is only triggered if the
+ // client has proven control of the key.
+ return nil, &PartialSuccessError{
+ Next: ServerAuthCallbacks{
+ PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
+ if string(password) == clientPassword {
+ return nil, nil
+ }
+ return nil, nil
+ },
+ },
+ }
+ }
+ return nil, errors.New("invalid credentials")
+ },
+ VerifiedPublicKeyCallback: func(conn ConnMetadata, key PublicKey, permissions *Permissions, signatureAlgorithm string) (*Permissions, error) {
+ if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
+ return nil, &PartialSuccessError{
+ Next: ServerAuthCallbacks{
+ PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
+ if string(password) == clientPassword {
+ return nil, nil
+ }
+ return nil, nil
+ },
+ },
+ }
+ }
+ return nil, errors.New("invalid credentials")
+ },
+ }
+ serverConf.AddHostKey(testSigners["rsa"])
+
+ clientConf := ClientConfig{
+ User: "user",
+ Auth: []AuthMethod{
+ PublicKeys(testSigners["rsa"]),
+ Password(clientPassword),
+ },
+ HostKeyCallback: InsecureIgnoreHostKey(),
+ }
+
+ go NewServerConn(c1, serverConf)
+
+ _, _, _, err = NewClientConn(c2, "", &clientConf)
+ if err == nil {
+ t.Fatal("authentication suceeded with PartialSuccess returned from PublicKeyCallback and VerifiedPublicKeyCallback defined")
+ }
+}
+
+func TestVerifiedPublicKeyCallbackOnError(t *testing.T) {
+ c1, c2, err := netPipe()
+ if err != nil {
+ t.Fatalf("netPipe: %v", err)
+ }
+ defer c1.Close()
+ defer c2.Close()
+
+ var verifiedCallbackCalled bool
+
+ serverConf := &ServerConfig{
+ VerifiedPublicKeyCallback: func(conn ConnMetadata, key PublicKey, permissions *Permissions, signatureAlgorithm string) (*Permissions, error) {
+ verifiedCallbackCalled = true
+ return nil, nil
+ },
+ PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
+ return nil, errors.New("invalid key")
+ },
+ }
+ serverConf.AddHostKey(testSigners["rsa"])
+
+ done := make(chan struct{})
+ go func() {
+ defer close(done)
+ NewServerConn(c1, serverConf)
+ }()
+
+ clientConf := ClientConfig{
+ User: "user",
+ Auth: []AuthMethod{
+ PublicKeys(testSigners["rsa"]),
+ },
+ HostKeyCallback: InsecureIgnoreHostKey(),
+ }
+
+ _, _, _, err = NewClientConn(c2, "", &clientConf)
+ if err == nil {
+ t.Fatal("authentication should fail")
+ }
+ <-done
+ if verifiedCallbackCalled {
+ t.Error("VerifiedPublicKeyCallback called after PublicKeyCallback returned an error")
+ }
+}
+
+func TestVerifiedPublicKeyCallbackOnly(t *testing.T) {
+ c1, c2, err := netPipe()
+ if err != nil {
+ t.Fatalf("netPipe: %v", err)
+ }
+ defer c1.Close()
+ defer c2.Close()
+
+ serverConf := &ServerConfig{
+ VerifiedPublicKeyCallback: func(conn ConnMetadata, key PublicKey, permissions *Permissions, signatureAlgorithm string) (*Permissions, error) {
+ return nil, nil
+ },
+ }
+ serverConf.AddHostKey(testSigners["rsa"])
+ done := make(chan struct{})
+ go func() {
+ defer close(done)
+ NewServerConn(c1, serverConf)
+ }()
+
+ clientConf := ClientConfig{
+ User: "user",
+ Auth: []AuthMethod{
+ PublicKeys(testSigners["rsa"]),
+ },
+ HostKeyCallback: InsecureIgnoreHostKey(),
+ }
+
+ _, _, _, err = NewClientConn(c2, "", &clientConf)
+ if err == nil {
+ t.Fatal("authentication suceeded with only VerifiedPublicKeyCallback defined")
+ }
+ <-done
+}
+
type markerConn struct {
closed uint32
used uint32