github.com/psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/crypto/ssh/client_auth_test.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ssh
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/rand"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"log"
    14  	"net"
    15  	"os"
    16  	"strings"
    17  	"testing"
    18  )
    19  
    20  type keyboardInteractive map[string]string
    21  
    22  func (cr keyboardInteractive) Challenge(user string, instruction string, questions []string, echos []bool) ([]string, error) {
    23  	var answers []string
    24  	for _, q := range questions {
    25  		answers = append(answers, cr[q])
    26  	}
    27  	return answers, nil
    28  }
    29  
    30  // reused internally by tests
    31  var clientPassword = "tiger"
    32  
    33  // tryAuth runs a handshake with a given config against an SSH server
    34  // with config serverConfig. Returns both client and server side errors.
    35  func tryAuth(t *testing.T, config *ClientConfig) error {
    36  	err, _ := tryAuthBothSides(t, config, nil)
    37  	return err
    38  }
    39  
    40  // tryAuth runs a handshake with a given config against an SSH server
    41  // with a given GSSAPIWithMICConfig and config serverConfig. Returns both client and server side errors.
    42  func tryAuthWithGSSAPIWithMICConfig(t *testing.T, clientConfig *ClientConfig, gssAPIWithMICConfig *GSSAPIWithMICConfig) error {
    43  	err, _ := tryAuthBothSides(t, clientConfig, gssAPIWithMICConfig)
    44  	return err
    45  }
    46  
    47  // tryAuthBothSides runs the handshake and returns the resulting errors from both sides of the connection.
    48  func tryAuthBothSides(t *testing.T, config *ClientConfig, gssAPIWithMICConfig *GSSAPIWithMICConfig) (clientError error, serverAuthErrors []error) {
    49  	c1, c2, err := netPipe()
    50  	if err != nil {
    51  		t.Fatalf("netPipe: %v", err)
    52  	}
    53  	defer c1.Close()
    54  	defer c2.Close()
    55  
    56  	certChecker := CertChecker{
    57  		IsUserAuthority: func(k PublicKey) bool {
    58  			return bytes.Equal(k.Marshal(), testPublicKeys["ecdsa"].Marshal())
    59  		},
    60  		UserKeyFallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
    61  			if conn.User() == "testuser" && bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
    62  				return nil, nil
    63  			}
    64  
    65  			return nil, fmt.Errorf("pubkey for %q not acceptable", conn.User())
    66  		},
    67  		IsRevoked: func(c *Certificate) bool {
    68  			return c.Serial == 666
    69  		},
    70  	}
    71  	serverConfig := &ServerConfig{
    72  		PasswordCallback: func(conn ConnMetadata, pass []byte) (*Permissions, error) {
    73  			if conn.User() == "testuser" && string(pass) == clientPassword {
    74  				return nil, nil
    75  			}
    76  			return nil, errors.New("password auth failed")
    77  		},
    78  		PublicKeyCallback: certChecker.Authenticate,
    79  		KeyboardInteractiveCallback: func(conn ConnMetadata, challenge KeyboardInteractiveChallenge) (*Permissions, error) {
    80  			ans, err := challenge("user",
    81  				"instruction",
    82  				[]string{"question1", "question2"},
    83  				[]bool{true, true})
    84  			if err != nil {
    85  				return nil, err
    86  			}
    87  			ok := conn.User() == "testuser" && ans[0] == "answer1" && ans[1] == "answer2"
    88  			if ok {
    89  				challenge("user", "motd", nil, nil)
    90  				return nil, nil
    91  			}
    92  			return nil, errors.New("keyboard-interactive failed")
    93  		},
    94  		GSSAPIWithMICConfig: gssAPIWithMICConfig,
    95  	}
    96  	serverConfig.AddHostKey(testSigners["rsa"])
    97  
    98  	serverConfig.AuthLogCallback = func(conn ConnMetadata, method string, err error) {
    99  		serverAuthErrors = append(serverAuthErrors, err)
   100  	}
   101  
   102  	go newServer(c1, serverConfig)
   103  	_, _, _, err = NewClientConn(c2, "", config)
   104  	return err, serverAuthErrors
   105  }
   106  
   107  func TestClientAuthPublicKey(t *testing.T) {
   108  	config := &ClientConfig{
   109  		User: "testuser",
   110  		Auth: []AuthMethod{
   111  			PublicKeys(testSigners["rsa"]),
   112  		},
   113  		HostKeyCallback: InsecureIgnoreHostKey(),
   114  	}
   115  	if err := tryAuth(t, config); err != nil {
   116  		t.Fatalf("unable to dial remote side: %s", err)
   117  	}
   118  }
   119  
   120  func TestAuthMethodPassword(t *testing.T) {
   121  	config := &ClientConfig{
   122  		User: "testuser",
   123  		Auth: []AuthMethod{
   124  			Password(clientPassword),
   125  		},
   126  		HostKeyCallback: InsecureIgnoreHostKey(),
   127  	}
   128  
   129  	if err := tryAuth(t, config); err != nil {
   130  		t.Fatalf("unable to dial remote side: %s", err)
   131  	}
   132  }
   133  
   134  func TestAuthMethodFallback(t *testing.T) {
   135  	var passwordCalled bool
   136  	config := &ClientConfig{
   137  		User: "testuser",
   138  		Auth: []AuthMethod{
   139  			PublicKeys(testSigners["rsa"]),
   140  			PasswordCallback(
   141  				func() (string, error) {
   142  					passwordCalled = true
   143  					return "WRONG", nil
   144  				}),
   145  		},
   146  		HostKeyCallback: InsecureIgnoreHostKey(),
   147  	}
   148  
   149  	if err := tryAuth(t, config); err != nil {
   150  		t.Fatalf("unable to dial remote side: %s", err)
   151  	}
   152  
   153  	if passwordCalled {
   154  		t.Errorf("password auth tried before public-key auth.")
   155  	}
   156  }
   157  
   158  func TestAuthMethodWrongPassword(t *testing.T) {
   159  	config := &ClientConfig{
   160  		User: "testuser",
   161  		Auth: []AuthMethod{
   162  			Password("wrong"),
   163  			PublicKeys(testSigners["rsa"]),
   164  		},
   165  		HostKeyCallback: InsecureIgnoreHostKey(),
   166  	}
   167  
   168  	if err := tryAuth(t, config); err != nil {
   169  		t.Fatalf("unable to dial remote side: %s", err)
   170  	}
   171  }
   172  
   173  func TestAuthMethodKeyboardInteractive(t *testing.T) {
   174  	answers := keyboardInteractive(map[string]string{
   175  		"question1": "answer1",
   176  		"question2": "answer2",
   177  	})
   178  	config := &ClientConfig{
   179  		User: "testuser",
   180  		Auth: []AuthMethod{
   181  			KeyboardInteractive(answers.Challenge),
   182  		},
   183  		HostKeyCallback: InsecureIgnoreHostKey(),
   184  	}
   185  
   186  	if err := tryAuth(t, config); err != nil {
   187  		t.Fatalf("unable to dial remote side: %s", err)
   188  	}
   189  }
   190  
   191  func TestAuthMethodWrongKeyboardInteractive(t *testing.T) {
   192  	answers := keyboardInteractive(map[string]string{
   193  		"question1": "answer1",
   194  		"question2": "WRONG",
   195  	})
   196  	config := &ClientConfig{
   197  		User: "testuser",
   198  		Auth: []AuthMethod{
   199  			KeyboardInteractive(answers.Challenge),
   200  		},
   201  	}
   202  
   203  	if err := tryAuth(t, config); err == nil {
   204  		t.Fatalf("wrong answers should not have authenticated with KeyboardInteractive")
   205  	}
   206  }
   207  
   208  // the mock server will only authenticate ssh-rsa keys
   209  func TestAuthMethodInvalidPublicKey(t *testing.T) {
   210  	config := &ClientConfig{
   211  		User: "testuser",
   212  		Auth: []AuthMethod{
   213  			PublicKeys(testSigners["dsa"]),
   214  		},
   215  	}
   216  
   217  	if err := tryAuth(t, config); err == nil {
   218  		t.Fatalf("dsa private key should not have authenticated with rsa public key")
   219  	}
   220  }
   221  
   222  // the client should authenticate with the second key
   223  func TestAuthMethodRSAandDSA(t *testing.T) {
   224  	config := &ClientConfig{
   225  		User: "testuser",
   226  		Auth: []AuthMethod{
   227  			PublicKeys(testSigners["dsa"], testSigners["rsa"]),
   228  		},
   229  		HostKeyCallback: InsecureIgnoreHostKey(),
   230  	}
   231  	if err := tryAuth(t, config); err != nil {
   232  		t.Fatalf("client could not authenticate with rsa key: %v", err)
   233  	}
   234  }
   235  
   236  type invalidAlgSigner struct {
   237  	Signer
   238  }
   239  
   240  func (s *invalidAlgSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
   241  	sig, err := s.Signer.Sign(rand, data)
   242  	if sig != nil {
   243  		sig.Format = "invalid"
   244  	}
   245  	return sig, err
   246  }
   247  
   248  func TestMethodInvalidAlgorithm(t *testing.T) {
   249  	config := &ClientConfig{
   250  		User: "testuser",
   251  		Auth: []AuthMethod{
   252  			PublicKeys(&invalidAlgSigner{testSigners["rsa"]}),
   253  		},
   254  		HostKeyCallback: InsecureIgnoreHostKey(),
   255  	}
   256  
   257  	err, serverErrors := tryAuthBothSides(t, config, nil)
   258  	if err == nil {
   259  		t.Fatalf("login succeeded")
   260  	}
   261  
   262  	found := false
   263  	want := "algorithm \"invalid\""
   264  
   265  	var errStrings []string
   266  	for _, err := range serverErrors {
   267  		found = found || (err != nil && strings.Contains(err.Error(), want))
   268  		errStrings = append(errStrings, err.Error())
   269  	}
   270  	if !found {
   271  		t.Errorf("server got error %q, want substring %q", errStrings, want)
   272  	}
   273  }
   274  
   275  func TestClientHMAC(t *testing.T) {
   276  	for _, mac := range supportedMACs {
   277  		config := &ClientConfig{
   278  			User: "testuser",
   279  			Auth: []AuthMethod{
   280  				PublicKeys(testSigners["rsa"]),
   281  			},
   282  			Config: Config{
   283  				MACs: []string{mac},
   284  			},
   285  			HostKeyCallback: InsecureIgnoreHostKey(),
   286  		}
   287  		if err := tryAuth(t, config); err != nil {
   288  			t.Fatalf("client could not authenticate with mac algo %s: %v", mac, err)
   289  		}
   290  	}
   291  }
   292  
   293  // issue 4285.
   294  func TestClientUnsupportedCipher(t *testing.T) {
   295  	config := &ClientConfig{
   296  		User: "testuser",
   297  		Auth: []AuthMethod{
   298  			PublicKeys(),
   299  		},
   300  		Config: Config{
   301  			Ciphers: []string{"aes128-cbc"}, // not currently supported
   302  		},
   303  	}
   304  	if err := tryAuth(t, config); err == nil {
   305  		t.Errorf("expected no ciphers in common")
   306  	}
   307  }
   308  
   309  func TestClientUnsupportedKex(t *testing.T) {
   310  	if os.Getenv("GO_BUILDER_NAME") != "" {
   311  		t.Skip("skipping known-flaky test on the Go build dashboard; see golang.org/issue/15198")
   312  	}
   313  	config := &ClientConfig{
   314  		User: "testuser",
   315  		Auth: []AuthMethod{
   316  			PublicKeys(),
   317  		},
   318  		Config: Config{
   319  			KeyExchanges: []string{"non-existent-kex"},
   320  		},
   321  		HostKeyCallback: InsecureIgnoreHostKey(),
   322  	}
   323  	if err := tryAuth(t, config); err == nil || !strings.Contains(err.Error(), "common algorithm") {
   324  		t.Errorf("got %v, expected 'common algorithm'", err)
   325  	}
   326  }
   327  
   328  func TestClientLoginCert(t *testing.T) {
   329  	cert := &Certificate{
   330  		Key:         testPublicKeys["rsa"],
   331  		ValidBefore: CertTimeInfinity,
   332  		CertType:    UserCert,
   333  	}
   334  	cert.SignCert(rand.Reader, testSigners["ecdsa"])
   335  	certSigner, err := NewCertSigner(cert, testSigners["rsa"])
   336  	if err != nil {
   337  		t.Fatalf("NewCertSigner: %v", err)
   338  	}
   339  
   340  	clientConfig := &ClientConfig{
   341  		User:            "user",
   342  		HostKeyCallback: InsecureIgnoreHostKey(),
   343  	}
   344  	clientConfig.Auth = append(clientConfig.Auth, PublicKeys(certSigner))
   345  
   346  	// should succeed
   347  	if err := tryAuth(t, clientConfig); err != nil {
   348  		t.Errorf("cert login failed: %v", err)
   349  	}
   350  
   351  	// corrupted signature
   352  	cert.Signature.Blob[0]++
   353  	if err := tryAuth(t, clientConfig); err == nil {
   354  		t.Errorf("cert login passed with corrupted sig")
   355  	}
   356  
   357  	// revoked
   358  	cert.Serial = 666
   359  	cert.SignCert(rand.Reader, testSigners["ecdsa"])
   360  	if err := tryAuth(t, clientConfig); err == nil {
   361  		t.Errorf("revoked cert login succeeded")
   362  	}
   363  	cert.Serial = 1
   364  
   365  	// sign with wrong key
   366  	cert.SignCert(rand.Reader, testSigners["dsa"])
   367  	if err := tryAuth(t, clientConfig); err == nil {
   368  		t.Errorf("cert login passed with non-authoritative key")
   369  	}
   370  
   371  	// host cert
   372  	cert.CertType = HostCert
   373  	cert.SignCert(rand.Reader, testSigners["ecdsa"])
   374  	if err := tryAuth(t, clientConfig); err == nil {
   375  		t.Errorf("cert login passed with wrong type")
   376  	}
   377  	cert.CertType = UserCert
   378  
   379  	// principal specified
   380  	cert.ValidPrincipals = []string{"user"}
   381  	cert.SignCert(rand.Reader, testSigners["ecdsa"])
   382  	if err := tryAuth(t, clientConfig); err != nil {
   383  		t.Errorf("cert login failed: %v", err)
   384  	}
   385  
   386  	// wrong principal specified
   387  	cert.ValidPrincipals = []string{"fred"}
   388  	cert.SignCert(rand.Reader, testSigners["ecdsa"])
   389  	if err := tryAuth(t, clientConfig); err == nil {
   390  		t.Errorf("cert login passed with wrong principal")
   391  	}
   392  	cert.ValidPrincipals = nil
   393  
   394  	// added critical option
   395  	cert.CriticalOptions = map[string]string{"root-access": "yes"}
   396  	cert.SignCert(rand.Reader, testSigners["ecdsa"])
   397  	if err := tryAuth(t, clientConfig); err == nil {
   398  		t.Errorf("cert login passed with unrecognized critical option")
   399  	}
   400  
   401  	// allowed source address
   402  	cert.CriticalOptions = map[string]string{"source-address": "127.0.0.42/24,::42/120"}
   403  	cert.SignCert(rand.Reader, testSigners["ecdsa"])
   404  	if err := tryAuth(t, clientConfig); err != nil {
   405  		t.Errorf("cert login with source-address failed: %v", err)
   406  	}
   407  
   408  	// disallowed source address
   409  	cert.CriticalOptions = map[string]string{"source-address": "127.0.0.42,::42"}
   410  	cert.SignCert(rand.Reader, testSigners["ecdsa"])
   411  	if err := tryAuth(t, clientConfig); err == nil {
   412  		t.Errorf("cert login with source-address succeeded")
   413  	}
   414  }
   415  
   416  func testPermissionsPassing(withPermissions bool, t *testing.T) {
   417  	serverConfig := &ServerConfig{
   418  		PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
   419  			if conn.User() == "nopermissions" {
   420  				return nil, nil
   421  			}
   422  			return &Permissions{}, nil
   423  		},
   424  	}
   425  	serverConfig.AddHostKey(testSigners["rsa"])
   426  
   427  	clientConfig := &ClientConfig{
   428  		Auth: []AuthMethod{
   429  			PublicKeys(testSigners["rsa"]),
   430  		},
   431  		HostKeyCallback: InsecureIgnoreHostKey(),
   432  	}
   433  	if withPermissions {
   434  		clientConfig.User = "permissions"
   435  	} else {
   436  		clientConfig.User = "nopermissions"
   437  	}
   438  
   439  	c1, c2, err := netPipe()
   440  	if err != nil {
   441  		t.Fatalf("netPipe: %v", err)
   442  	}
   443  	defer c1.Close()
   444  	defer c2.Close()
   445  
   446  	go NewClientConn(c2, "", clientConfig)
   447  	serverConn, err := newServer(c1, serverConfig)
   448  	if err != nil {
   449  		t.Fatal(err)
   450  	}
   451  	if p := serverConn.Permissions; (p != nil) != withPermissions {
   452  		t.Fatalf("withPermissions is %t, but Permissions object is %#v", withPermissions, p)
   453  	}
   454  }
   455  
   456  func TestPermissionsPassing(t *testing.T) {
   457  	testPermissionsPassing(true, t)
   458  }
   459  
   460  func TestNoPermissionsPassing(t *testing.T) {
   461  	testPermissionsPassing(false, t)
   462  }
   463  
   464  func TestRetryableAuth(t *testing.T) {
   465  	n := 0
   466  	passwords := []string{"WRONG1", "WRONG2"}
   467  
   468  	config := &ClientConfig{
   469  		User: "testuser",
   470  		Auth: []AuthMethod{
   471  			RetryableAuthMethod(PasswordCallback(func() (string, error) {
   472  				p := passwords[n]
   473  				n++
   474  				return p, nil
   475  			}), 2),
   476  			PublicKeys(testSigners["rsa"]),
   477  		},
   478  		HostKeyCallback: InsecureIgnoreHostKey(),
   479  	}
   480  
   481  	if err := tryAuth(t, config); err != nil {
   482  		t.Fatalf("unable to dial remote side: %s", err)
   483  	}
   484  	if n != 2 {
   485  		t.Fatalf("Did not try all passwords")
   486  	}
   487  }
   488  
   489  func ExampleRetryableAuthMethod() {
   490  	user := "testuser"
   491  	NumberOfPrompts := 3
   492  
   493  	// Normally this would be a callback that prompts the user to answer the
   494  	// provided questions
   495  	Cb := func(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
   496  		return []string{"answer1", "answer2"}, nil
   497  	}
   498  
   499  	config := &ClientConfig{
   500  		HostKeyCallback: InsecureIgnoreHostKey(),
   501  		User:            user,
   502  		Auth: []AuthMethod{
   503  			RetryableAuthMethod(KeyboardInteractiveChallenge(Cb), NumberOfPrompts),
   504  		},
   505  	}
   506  
   507  	host := "mysshserver"
   508  	netConn, err := net.Dial("tcp", host)
   509  	if err != nil {
   510  		log.Fatal(err)
   511  	}
   512  
   513  	sshConn, _, _, err := NewClientConn(netConn, host, config)
   514  	if err != nil {
   515  		log.Fatal(err)
   516  	}
   517  	_ = sshConn
   518  }
   519  
   520  // Test if username is received on server side when NoClientAuth is used
   521  func TestClientAuthNone(t *testing.T) {
   522  	user := "testuser"
   523  	serverConfig := &ServerConfig{
   524  		NoClientAuth: true,
   525  	}
   526  	serverConfig.AddHostKey(testSigners["rsa"])
   527  
   528  	clientConfig := &ClientConfig{
   529  		User:            user,
   530  		HostKeyCallback: InsecureIgnoreHostKey(),
   531  	}
   532  
   533  	c1, c2, err := netPipe()
   534  	if err != nil {
   535  		t.Fatalf("netPipe: %v", err)
   536  	}
   537  	defer c1.Close()
   538  	defer c2.Close()
   539  
   540  	go NewClientConn(c2, "", clientConfig)
   541  	serverConn, err := newServer(c1, serverConfig)
   542  	if err != nil {
   543  		t.Fatalf("newServer: %v", err)
   544  	}
   545  	if serverConn.User() != user {
   546  		t.Fatalf("server: got %q, want %q", serverConn.User(), user)
   547  	}
   548  }
   549  
   550  // Test if authentication attempts are limited on server when MaxAuthTries is set
   551  func TestClientAuthMaxAuthTries(t *testing.T) {
   552  	user := "testuser"
   553  
   554  	serverConfig := &ServerConfig{
   555  		MaxAuthTries: 2,
   556  		PasswordCallback: func(conn ConnMetadata, pass []byte) (*Permissions, error) {
   557  			if conn.User() == "testuser" && string(pass) == "right" {
   558  				return nil, nil
   559  			}
   560  			return nil, errors.New("password auth failed")
   561  		},
   562  	}
   563  	serverConfig.AddHostKey(testSigners["rsa"])
   564  
   565  	expectedErr := fmt.Errorf("ssh: handshake failed: %v", &disconnectMsg{
   566  		Reason:  2,
   567  		Message: "too many authentication failures",
   568  	})
   569  
   570  	for tries := 2; tries < 4; tries++ {
   571  		n := tries
   572  		clientConfig := &ClientConfig{
   573  			User: user,
   574  			Auth: []AuthMethod{
   575  				RetryableAuthMethod(PasswordCallback(func() (string, error) {
   576  					n--
   577  					if n == 0 {
   578  						return "right", nil
   579  					}
   580  					return "wrong", nil
   581  				}), tries),
   582  			},
   583  			HostKeyCallback: InsecureIgnoreHostKey(),
   584  		}
   585  
   586  		c1, c2, err := netPipe()
   587  		if err != nil {
   588  			t.Fatalf("netPipe: %v", err)
   589  		}
   590  		defer c1.Close()
   591  		defer c2.Close()
   592  
   593  		go newServer(c1, serverConfig)
   594  		_, _, _, err = NewClientConn(c2, "", clientConfig)
   595  		if tries > 2 {
   596  			if err == nil {
   597  				t.Fatalf("client: got no error, want %s", expectedErr)
   598  			} else if err.Error() != expectedErr.Error() {
   599  				t.Fatalf("client: got %s, want %s", err, expectedErr)
   600  			}
   601  		} else {
   602  			if err != nil {
   603  				t.Fatalf("client: got %s, want no error", err)
   604  			}
   605  		}
   606  	}
   607  }
   608  
   609  // Test if authentication attempts are correctly limited on server
   610  // when more public keys are provided then MaxAuthTries
   611  func TestClientAuthMaxAuthTriesPublicKey(t *testing.T) {
   612  	signers := []Signer{}
   613  	for i := 0; i < 6; i++ {
   614  		signers = append(signers, testSigners["dsa"])
   615  	}
   616  
   617  	validConfig := &ClientConfig{
   618  		User: "testuser",
   619  		Auth: []AuthMethod{
   620  			PublicKeys(append([]Signer{testSigners["rsa"]}, signers...)...),
   621  		},
   622  		HostKeyCallback: InsecureIgnoreHostKey(),
   623  	}
   624  	if err := tryAuth(t, validConfig); err != nil {
   625  		t.Fatalf("unable to dial remote side: %s", err)
   626  	}
   627  
   628  	expectedErr := fmt.Errorf("ssh: handshake failed: %v", &disconnectMsg{
   629  		Reason:  2,
   630  		Message: "too many authentication failures",
   631  	})
   632  	invalidConfig := &ClientConfig{
   633  		User: "testuser",
   634  		Auth: []AuthMethod{
   635  			PublicKeys(append(signers, testSigners["rsa"])...),
   636  		},
   637  		HostKeyCallback: InsecureIgnoreHostKey(),
   638  	}
   639  	if err := tryAuth(t, invalidConfig); err == nil {
   640  		t.Fatalf("client: got no error, want %s", expectedErr)
   641  	} else if err.Error() != expectedErr.Error() {
   642  		t.Fatalf("client: got %s, want %s", err, expectedErr)
   643  	}
   644  }
   645  
   646  // Test whether authentication errors are being properly logged if all
   647  // authentication methods have been exhausted
   648  func TestClientAuthErrorList(t *testing.T) {
   649  	publicKeyErr := errors.New("This is an error from PublicKeyCallback")
   650  
   651  	clientConfig := &ClientConfig{
   652  		Auth: []AuthMethod{
   653  			PublicKeys(testSigners["rsa"]),
   654  		},
   655  		HostKeyCallback: InsecureIgnoreHostKey(),
   656  	}
   657  	serverConfig := &ServerConfig{
   658  		PublicKeyCallback: func(_ ConnMetadata, _ PublicKey) (*Permissions, error) {
   659  			return nil, publicKeyErr
   660  		},
   661  	}
   662  	serverConfig.AddHostKey(testSigners["rsa"])
   663  
   664  	c1, c2, err := netPipe()
   665  	if err != nil {
   666  		t.Fatalf("netPipe: %v", err)
   667  	}
   668  	defer c1.Close()
   669  	defer c2.Close()
   670  
   671  	go NewClientConn(c2, "", clientConfig)
   672  	_, err = newServer(c1, serverConfig)
   673  	if err == nil {
   674  		t.Fatal("newServer: got nil, expected errors")
   675  	}
   676  
   677  	authErrs, ok := err.(*ServerAuthError)
   678  	if !ok {
   679  		t.Fatalf("errors: got %T, want *ssh.ServerAuthError", err)
   680  	}
   681  	for i, e := range authErrs.Errors {
   682  		switch i {
   683  		case 0:
   684  			if e != ErrNoAuth {
   685  				t.Fatalf("errors: got error %v, want ErrNoAuth", e)
   686  			}
   687  		case 1:
   688  			if e != publicKeyErr {
   689  				t.Fatalf("errors: got %v, want %v", e, publicKeyErr)
   690  			}
   691  		default:
   692  			t.Fatalf("errors: got %v, expected 2 errors", authErrs.Errors)
   693  		}
   694  	}
   695  }
   696  
   697  func TestAuthMethodGSSAPIWithMIC(t *testing.T) {
   698  	type testcase struct {
   699  		config        *ClientConfig
   700  		gssConfig     *GSSAPIWithMICConfig
   701  		clientWantErr string
   702  		serverWantErr string
   703  	}
   704  	testcases := []*testcase{
   705  		{
   706  			config: &ClientConfig{
   707  				User: "testuser",
   708  				Auth: []AuthMethod{
   709  					GSSAPIWithMICAuthMethod(
   710  						&FakeClient{
   711  							exchanges: []*exchange{
   712  								{
   713  									outToken: "client-valid-token-1",
   714  								},
   715  								{
   716  									expectedToken: "server-valid-token-1",
   717  								},
   718  							},
   719  							mic:      []byte("valid-mic"),
   720  							maxRound: 2,
   721  						}, "testtarget",
   722  					),
   723  				},
   724  				HostKeyCallback: InsecureIgnoreHostKey(),
   725  			},
   726  			gssConfig: &GSSAPIWithMICConfig{
   727  				AllowLogin: func(conn ConnMetadata, srcName string) (*Permissions, error) {
   728  					if srcName != conn.User()+"@DOMAIN" {
   729  						return nil, fmt.Errorf("srcName is %s, conn user is %s", srcName, conn.User())
   730  					}
   731  					return nil, nil
   732  				},
   733  				Server: &FakeServer{
   734  					exchanges: []*exchange{
   735  						{
   736  							outToken:      "server-valid-token-1",
   737  							expectedToken: "client-valid-token-1",
   738  						},
   739  					},
   740  					maxRound:    1,
   741  					expectedMIC: []byte("valid-mic"),
   742  					srcName:     "testuser@DOMAIN",
   743  				},
   744  			},
   745  		},
   746  		{
   747  			config: &ClientConfig{
   748  				User: "testuser",
   749  				Auth: []AuthMethod{
   750  					GSSAPIWithMICAuthMethod(
   751  						&FakeClient{
   752  							exchanges: []*exchange{
   753  								{
   754  									outToken: "client-valid-token-1",
   755  								},
   756  								{
   757  									expectedToken: "server-valid-token-1",
   758  								},
   759  							},
   760  							mic:      []byte("valid-mic"),
   761  							maxRound: 2,
   762  						}, "testtarget",
   763  					),
   764  				},
   765  				HostKeyCallback: InsecureIgnoreHostKey(),
   766  			},
   767  			gssConfig: &GSSAPIWithMICConfig{
   768  				AllowLogin: func(conn ConnMetadata, srcName string) (*Permissions, error) {
   769  					return nil, fmt.Errorf("user is not allowed to login")
   770  				},
   771  				Server: &FakeServer{
   772  					exchanges: []*exchange{
   773  						{
   774  							outToken:      "server-valid-token-1",
   775  							expectedToken: "client-valid-token-1",
   776  						},
   777  					},
   778  					maxRound:    1,
   779  					expectedMIC: []byte("valid-mic"),
   780  					srcName:     "testuser@DOMAIN",
   781  				},
   782  			},
   783  			serverWantErr: "user is not allowed to login",
   784  			clientWantErr: "ssh: handshake failed: ssh: unable to authenticate",
   785  		},
   786  		{
   787  			config: &ClientConfig{
   788  				User: "testuser",
   789  				Auth: []AuthMethod{
   790  					GSSAPIWithMICAuthMethod(
   791  						&FakeClient{
   792  							exchanges: []*exchange{
   793  								{
   794  									outToken: "client-valid-token-1",
   795  								},
   796  								{
   797  									expectedToken: "server-valid-token-1",
   798  								},
   799  							},
   800  							mic:      []byte("valid-mic"),
   801  							maxRound: 2,
   802  						}, "testtarget",
   803  					),
   804  				},
   805  				HostKeyCallback: InsecureIgnoreHostKey(),
   806  			},
   807  			gssConfig: &GSSAPIWithMICConfig{
   808  				AllowLogin: func(conn ConnMetadata, srcName string) (*Permissions, error) {
   809  					if srcName != conn.User() {
   810  						return nil, fmt.Errorf("srcName is %s, conn user is %s", srcName, conn.User())
   811  					}
   812  					return nil, nil
   813  				},
   814  				Server: &FakeServer{
   815  					exchanges: []*exchange{
   816  						{
   817  							outToken:      "server-invalid-token-1",
   818  							expectedToken: "client-valid-token-1",
   819  						},
   820  					},
   821  					maxRound:    1,
   822  					expectedMIC: []byte("valid-mic"),
   823  					srcName:     "testuser@DOMAIN",
   824  				},
   825  			},
   826  			clientWantErr: "ssh: handshake failed: got \"server-invalid-token-1\", want token \"server-valid-token-1\"",
   827  		},
   828  		{
   829  			config: &ClientConfig{
   830  				User: "testuser",
   831  				Auth: []AuthMethod{
   832  					GSSAPIWithMICAuthMethod(
   833  						&FakeClient{
   834  							exchanges: []*exchange{
   835  								{
   836  									outToken: "client-valid-token-1",
   837  								},
   838  								{
   839  									expectedToken: "server-valid-token-1",
   840  								},
   841  							},
   842  							mic:      []byte("invalid-mic"),
   843  							maxRound: 2,
   844  						}, "testtarget",
   845  					),
   846  				},
   847  				HostKeyCallback: InsecureIgnoreHostKey(),
   848  			},
   849  			gssConfig: &GSSAPIWithMICConfig{
   850  				AllowLogin: func(conn ConnMetadata, srcName string) (*Permissions, error) {
   851  					if srcName != conn.User() {
   852  						return nil, fmt.Errorf("srcName is %s, conn user is %s", srcName, conn.User())
   853  					}
   854  					return nil, nil
   855  				},
   856  				Server: &FakeServer{
   857  					exchanges: []*exchange{
   858  						{
   859  							outToken:      "server-valid-token-1",
   860  							expectedToken: "client-valid-token-1",
   861  						},
   862  					},
   863  					maxRound:    1,
   864  					expectedMIC: []byte("valid-mic"),
   865  					srcName:     "testuser@DOMAIN",
   866  				},
   867  			},
   868  			serverWantErr: "got MICToken \"invalid-mic\", want \"valid-mic\"",
   869  			clientWantErr: "ssh: handshake failed: ssh: unable to authenticate",
   870  		},
   871  	}
   872  
   873  	for i, c := range testcases {
   874  		clientErr, serverErrs := tryAuthBothSides(t, c.config, c.gssConfig)
   875  		if (c.clientWantErr == "") != (clientErr == nil) {
   876  			t.Fatalf("client got %v, want %s, case %d", clientErr, c.clientWantErr, i)
   877  		}
   878  		if (c.serverWantErr == "") != (len(serverErrs) == 2 && serverErrs[1] == nil || len(serverErrs) == 1) {
   879  			t.Fatalf("server got err %v, want %s", serverErrs, c.serverWantErr)
   880  		}
   881  		if c.clientWantErr != "" {
   882  			if clientErr != nil && !strings.Contains(clientErr.Error(), c.clientWantErr) {
   883  				t.Fatalf("client  got %v, want %s, case %d", clientErr, c.clientWantErr, i)
   884  			}
   885  		}
   886  		found := false
   887  		var errStrings []string
   888  		if c.serverWantErr != "" {
   889  			for _, err := range serverErrs {
   890  				found = found || (err != nil && strings.Contains(err.Error(), c.serverWantErr))
   891  				errStrings = append(errStrings, err.Error())
   892  			}
   893  			if !found {
   894  				t.Errorf("server got error %q, want substring %q, case %d", errStrings, c.serverWantErr, i)
   895  			}
   896  		}
   897  	}
   898  }