github.com/maenmax/kairep@v0.0.0-20210218001208-55bf3df36788/src/golang.org/x/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  	"os"
    13  	"strings"
    14  	"testing"
    15  )
    16  
    17  type keyboardInteractive map[string]string
    18  
    19  func (cr keyboardInteractive) Challenge(user string, instruction string, questions []string, echos []bool) ([]string, error) {
    20  	var answers []string
    21  	for _, q := range questions {
    22  		answers = append(answers, cr[q])
    23  	}
    24  	return answers, nil
    25  }
    26  
    27  // reused internally by tests
    28  var clientPassword = "tiger"
    29  
    30  // tryAuth runs a handshake with a given config against an SSH server
    31  // with config serverConfig
    32  func tryAuth(t *testing.T, config *ClientConfig) error {
    33  	c1, c2, err := netPipe()
    34  	if err != nil {
    35  		t.Fatalf("netPipe: %v", err)
    36  	}
    37  	defer c1.Close()
    38  	defer c2.Close()
    39  
    40  	certChecker := CertChecker{
    41  		IsAuthority: func(k PublicKey) bool {
    42  			return bytes.Equal(k.Marshal(), testPublicKeys["ecdsa"].Marshal())
    43  		},
    44  		UserKeyFallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
    45  			if conn.User() == "testuser" && bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
    46  				return nil, nil
    47  			}
    48  
    49  			return nil, fmt.Errorf("pubkey for %q not acceptable", conn.User())
    50  		},
    51  		IsRevoked: func(c *Certificate) bool {
    52  			return c.Serial == 666
    53  		},
    54  	}
    55  
    56  	serverConfig := &ServerConfig{
    57  		PasswordCallback: func(conn ConnMetadata, pass []byte) (*Permissions, error) {
    58  			if conn.User() == "testuser" && string(pass) == clientPassword {
    59  				return nil, nil
    60  			}
    61  			return nil, errors.New("password auth failed")
    62  		},
    63  		PublicKeyCallback: certChecker.Authenticate,
    64  		KeyboardInteractiveCallback: func(conn ConnMetadata, challenge KeyboardInteractiveChallenge) (*Permissions, error) {
    65  			ans, err := challenge("user",
    66  				"instruction",
    67  				[]string{"question1", "question2"},
    68  				[]bool{true, true})
    69  			if err != nil {
    70  				return nil, err
    71  			}
    72  			ok := conn.User() == "testuser" && ans[0] == "answer1" && ans[1] == "answer2"
    73  			if ok {
    74  				challenge("user", "motd", nil, nil)
    75  				return nil, nil
    76  			}
    77  			return nil, errors.New("keyboard-interactive failed")
    78  		},
    79  		AuthLogCallback: func(conn ConnMetadata, method string, err error) {
    80  			t.Logf("user %q, method %q: %v", conn.User(), method, err)
    81  		},
    82  	}
    83  	serverConfig.AddHostKey(testSigners["rsa"])
    84  
    85  	go newServer(c1, serverConfig)
    86  	_, _, _, err = NewClientConn(c2, "", config)
    87  	return err
    88  }
    89  
    90  func TestClientAuthPublicKey(t *testing.T) {
    91  	config := &ClientConfig{
    92  		User: "testuser",
    93  		Auth: []AuthMethod{
    94  			PublicKeys(testSigners["rsa"]),
    95  		},
    96  	}
    97  	if err := tryAuth(t, config); err != nil {
    98  		t.Fatalf("unable to dial remote side: %s", err)
    99  	}
   100  }
   101  
   102  func TestAuthMethodPassword(t *testing.T) {
   103  	config := &ClientConfig{
   104  		User: "testuser",
   105  		Auth: []AuthMethod{
   106  			Password(clientPassword),
   107  		},
   108  	}
   109  
   110  	if err := tryAuth(t, config); err != nil {
   111  		t.Fatalf("unable to dial remote side: %s", err)
   112  	}
   113  }
   114  
   115  func TestAuthMethodFallback(t *testing.T) {
   116  	var passwordCalled bool
   117  	config := &ClientConfig{
   118  		User: "testuser",
   119  		Auth: []AuthMethod{
   120  			PublicKeys(testSigners["rsa"]),
   121  			PasswordCallback(
   122  				func() (string, error) {
   123  					passwordCalled = true
   124  					return "WRONG", nil
   125  				}),
   126  		},
   127  	}
   128  
   129  	if err := tryAuth(t, config); err != nil {
   130  		t.Fatalf("unable to dial remote side: %s", err)
   131  	}
   132  
   133  	if passwordCalled {
   134  		t.Errorf("password auth tried before public-key auth.")
   135  	}
   136  }
   137  
   138  func TestAuthMethodWrongPassword(t *testing.T) {
   139  	config := &ClientConfig{
   140  		User: "testuser",
   141  		Auth: []AuthMethod{
   142  			Password("wrong"),
   143  			PublicKeys(testSigners["rsa"]),
   144  		},
   145  	}
   146  
   147  	if err := tryAuth(t, config); err != nil {
   148  		t.Fatalf("unable to dial remote side: %s", err)
   149  	}
   150  }
   151  
   152  func TestAuthMethodKeyboardInteractive(t *testing.T) {
   153  	answers := keyboardInteractive(map[string]string{
   154  		"question1": "answer1",
   155  		"question2": "answer2",
   156  	})
   157  	config := &ClientConfig{
   158  		User: "testuser",
   159  		Auth: []AuthMethod{
   160  			KeyboardInteractive(answers.Challenge),
   161  		},
   162  	}
   163  
   164  	if err := tryAuth(t, config); err != nil {
   165  		t.Fatalf("unable to dial remote side: %s", err)
   166  	}
   167  }
   168  
   169  func TestAuthMethodWrongKeyboardInteractive(t *testing.T) {
   170  	answers := keyboardInteractive(map[string]string{
   171  		"question1": "answer1",
   172  		"question2": "WRONG",
   173  	})
   174  	config := &ClientConfig{
   175  		User: "testuser",
   176  		Auth: []AuthMethod{
   177  			KeyboardInteractive(answers.Challenge),
   178  		},
   179  	}
   180  
   181  	if err := tryAuth(t, config); err == nil {
   182  		t.Fatalf("wrong answers should not have authenticated with KeyboardInteractive")
   183  	}
   184  }
   185  
   186  // the mock server will only authenticate ssh-rsa keys
   187  func TestAuthMethodInvalidPublicKey(t *testing.T) {
   188  	config := &ClientConfig{
   189  		User: "testuser",
   190  		Auth: []AuthMethod{
   191  			PublicKeys(testSigners["dsa"]),
   192  		},
   193  	}
   194  
   195  	if err := tryAuth(t, config); err == nil {
   196  		t.Fatalf("dsa private key should not have authenticated with rsa public key")
   197  	}
   198  }
   199  
   200  // the client should authenticate with the second key
   201  func TestAuthMethodRSAandDSA(t *testing.T) {
   202  	config := &ClientConfig{
   203  		User: "testuser",
   204  		Auth: []AuthMethod{
   205  			PublicKeys(testSigners["dsa"], testSigners["rsa"]),
   206  		},
   207  	}
   208  	if err := tryAuth(t, config); err != nil {
   209  		t.Fatalf("client could not authenticate with rsa key: %v", err)
   210  	}
   211  }
   212  
   213  func TestClientHMAC(t *testing.T) {
   214  	for _, mac := range supportedMACs {
   215  		config := &ClientConfig{
   216  			User: "testuser",
   217  			Auth: []AuthMethod{
   218  				PublicKeys(testSigners["rsa"]),
   219  			},
   220  			Config: Config{
   221  				MACs: []string{mac},
   222  			},
   223  		}
   224  		if err := tryAuth(t, config); err != nil {
   225  			t.Fatalf("client could not authenticate with mac algo %s: %v", mac, err)
   226  		}
   227  	}
   228  }
   229  
   230  // issue 4285.
   231  func TestClientUnsupportedCipher(t *testing.T) {
   232  	config := &ClientConfig{
   233  		User: "testuser",
   234  		Auth: []AuthMethod{
   235  			PublicKeys(),
   236  		},
   237  		Config: Config{
   238  			Ciphers: []string{"aes128-cbc"}, // not currently supported
   239  		},
   240  	}
   241  	if err := tryAuth(t, config); err == nil {
   242  		t.Errorf("expected no ciphers in common")
   243  	}
   244  }
   245  
   246  func TestClientUnsupportedKex(t *testing.T) {
   247  	if os.Getenv("GO_BUILDER_NAME") != "" {
   248  		t.Skip("skipping known-flaky test on the Go build dashboard; see golang.org/issue/15198")
   249  	}
   250  	config := &ClientConfig{
   251  		User: "testuser",
   252  		Auth: []AuthMethod{
   253  			PublicKeys(),
   254  		},
   255  		Config: Config{
   256  			KeyExchanges: []string{"diffie-hellman-group-exchange-sha256"}, // not currently supported
   257  		},
   258  	}
   259  	if err := tryAuth(t, config); err == nil || !strings.Contains(err.Error(), "common algorithm") {
   260  		t.Errorf("got %v, expected 'common algorithm'", err)
   261  	}
   262  }
   263  
   264  func TestClientLoginCert(t *testing.T) {
   265  	cert := &Certificate{
   266  		Key:         testPublicKeys["rsa"],
   267  		ValidBefore: CertTimeInfinity,
   268  		CertType:    UserCert,
   269  	}
   270  	cert.SignCert(rand.Reader, testSigners["ecdsa"])
   271  	certSigner, err := NewCertSigner(cert, testSigners["rsa"])
   272  	if err != nil {
   273  		t.Fatalf("NewCertSigner: %v", err)
   274  	}
   275  
   276  	clientConfig := &ClientConfig{
   277  		User: "user",
   278  	}
   279  	clientConfig.Auth = append(clientConfig.Auth, PublicKeys(certSigner))
   280  
   281  	t.Log("should succeed")
   282  	if err := tryAuth(t, clientConfig); err != nil {
   283  		t.Errorf("cert login failed: %v", err)
   284  	}
   285  
   286  	t.Log("corrupted signature")
   287  	cert.Signature.Blob[0]++
   288  	if err := tryAuth(t, clientConfig); err == nil {
   289  		t.Errorf("cert login passed with corrupted sig")
   290  	}
   291  
   292  	t.Log("revoked")
   293  	cert.Serial = 666
   294  	cert.SignCert(rand.Reader, testSigners["ecdsa"])
   295  	if err := tryAuth(t, clientConfig); err == nil {
   296  		t.Errorf("revoked cert login succeeded")
   297  	}
   298  	cert.Serial = 1
   299  
   300  	t.Log("sign with wrong key")
   301  	cert.SignCert(rand.Reader, testSigners["dsa"])
   302  	if err := tryAuth(t, clientConfig); err == nil {
   303  		t.Errorf("cert login passed with non-authoritative key")
   304  	}
   305  
   306  	t.Log("host cert")
   307  	cert.CertType = HostCert
   308  	cert.SignCert(rand.Reader, testSigners["ecdsa"])
   309  	if err := tryAuth(t, clientConfig); err == nil {
   310  		t.Errorf("cert login passed with wrong type")
   311  	}
   312  	cert.CertType = UserCert
   313  
   314  	t.Log("principal specified")
   315  	cert.ValidPrincipals = []string{"user"}
   316  	cert.SignCert(rand.Reader, testSigners["ecdsa"])
   317  	if err := tryAuth(t, clientConfig); err != nil {
   318  		t.Errorf("cert login failed: %v", err)
   319  	}
   320  
   321  	t.Log("wrong principal specified")
   322  	cert.ValidPrincipals = []string{"fred"}
   323  	cert.SignCert(rand.Reader, testSigners["ecdsa"])
   324  	if err := tryAuth(t, clientConfig); err == nil {
   325  		t.Errorf("cert login passed with wrong principal")
   326  	}
   327  	cert.ValidPrincipals = nil
   328  
   329  	t.Log("added critical option")
   330  	cert.CriticalOptions = map[string]string{"root-access": "yes"}
   331  	cert.SignCert(rand.Reader, testSigners["ecdsa"])
   332  	if err := tryAuth(t, clientConfig); err == nil {
   333  		t.Errorf("cert login passed with unrecognized critical option")
   334  	}
   335  
   336  	t.Log("allowed source address")
   337  	cert.CriticalOptions = map[string]string{"source-address": "127.0.0.42/24"}
   338  	cert.SignCert(rand.Reader, testSigners["ecdsa"])
   339  	if err := tryAuth(t, clientConfig); err != nil {
   340  		t.Errorf("cert login with source-address failed: %v", err)
   341  	}
   342  
   343  	t.Log("disallowed source address")
   344  	cert.CriticalOptions = map[string]string{"source-address": "127.0.0.42"}
   345  	cert.SignCert(rand.Reader, testSigners["ecdsa"])
   346  	if err := tryAuth(t, clientConfig); err == nil {
   347  		t.Errorf("cert login with source-address succeeded")
   348  	}
   349  }
   350  
   351  func testPermissionsPassing(withPermissions bool, t *testing.T) {
   352  	serverConfig := &ServerConfig{
   353  		PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
   354  			if conn.User() == "nopermissions" {
   355  				return nil, nil
   356  			} else {
   357  				return &Permissions{}, nil
   358  			}
   359  		},
   360  	}
   361  	serverConfig.AddHostKey(testSigners["rsa"])
   362  
   363  	clientConfig := &ClientConfig{
   364  		Auth: []AuthMethod{
   365  			PublicKeys(testSigners["rsa"]),
   366  		},
   367  	}
   368  	if withPermissions {
   369  		clientConfig.User = "permissions"
   370  	} else {
   371  		clientConfig.User = "nopermissions"
   372  	}
   373  
   374  	c1, c2, err := netPipe()
   375  	if err != nil {
   376  		t.Fatalf("netPipe: %v", err)
   377  	}
   378  	defer c1.Close()
   379  	defer c2.Close()
   380  
   381  	go NewClientConn(c2, "", clientConfig)
   382  	serverConn, err := newServer(c1, serverConfig)
   383  	if err != nil {
   384  		t.Fatal(err)
   385  	}
   386  	if p := serverConn.Permissions; (p != nil) != withPermissions {
   387  		t.Fatalf("withPermissions is %t, but Permissions object is %#v", withPermissions, p)
   388  	}
   389  }
   390  
   391  func TestPermissionsPassing(t *testing.T) {
   392  	testPermissionsPassing(true, t)
   393  }
   394  
   395  func TestNoPermissionsPassing(t *testing.T) {
   396  	testPermissionsPassing(false, t)
   397  }
   398  
   399  func TestRetryableAuth(t *testing.T) {
   400  	n := 0
   401  	passwords := []string{"WRONG1", "WRONG2"}
   402  
   403  	config := &ClientConfig{
   404  		User: "testuser",
   405  		Auth: []AuthMethod{
   406  			RetryableAuthMethod(PasswordCallback(func() (string, error) {
   407  				p := passwords[n]
   408  				n++
   409  				return p, nil
   410  			}), 2),
   411  			PublicKeys(testSigners["rsa"]),
   412  		},
   413  	}
   414  
   415  	if err := tryAuth(t, config); err != nil {
   416  		t.Fatalf("unable to dial remote side: %s", err)
   417  	}
   418  	if n != 2 {
   419  		t.Fatalf("Did not try all passwords")
   420  	}
   421  }
   422  
   423  func ExampleRetryableAuthMethod(t *testing.T) {
   424  	user := "testuser"
   425  	NumberOfPrompts := 3
   426  
   427  	// Normally this would be a callback that prompts the user to answer the
   428  	// provided questions
   429  	Cb := func(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
   430  		return []string{"answer1", "answer2"}, nil
   431  	}
   432  
   433  	config := &ClientConfig{
   434  		User: user,
   435  		Auth: []AuthMethod{
   436  			RetryableAuthMethod(KeyboardInteractiveChallenge(Cb), NumberOfPrompts),
   437  		},
   438  	}
   439  
   440  	if err := tryAuth(t, config); err != nil {
   441  		t.Fatalf("unable to dial remote side: %s", err)
   442  	}
   443  }