|
@@ -33,12 +33,19 @@ var clientPassword = "tiger"
|
|
|
// tryAuth runs a handshake with a given config against an SSH server
|
|
// tryAuth runs a handshake with a given config against an SSH server
|
|
|
// with config serverConfig. Returns both client and server side errors.
|
|
// with config serverConfig. Returns both client and server side errors.
|
|
|
func tryAuth(t *testing.T, config *ClientConfig) error {
|
|
func tryAuth(t *testing.T, config *ClientConfig) error {
|
|
|
- err, _ := tryAuthBothSides(t, config)
|
|
|
|
|
|
|
+ err, _ := tryAuthBothSides(t, config, nil)
|
|
|
|
|
+ return err
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// tryAuth runs a handshake with a given config against an SSH server
|
|
|
|
|
+// with a given GSSAPIWithMICConfig and config serverConfig. Returns both client and server side errors.
|
|
|
|
|
+func tryAuthWithGSSAPIWithMICConfig(t *testing.T, clientConfig *ClientConfig, gssAPIWithMICConfig *GSSAPIWithMICConfig) error {
|
|
|
|
|
+ err, _ := tryAuthBothSides(t, clientConfig, gssAPIWithMICConfig)
|
|
|
return err
|
|
return err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// tryAuthBothSides runs the handshake and returns the resulting errors from both sides of the connection.
|
|
// tryAuthBothSides runs the handshake and returns the resulting errors from both sides of the connection.
|
|
|
-func tryAuthBothSides(t *testing.T, config *ClientConfig) (clientError error, serverAuthErrors []error) {
|
|
|
|
|
|
|
+func tryAuthBothSides(t *testing.T, config *ClientConfig, gssAPIWithMICConfig *GSSAPIWithMICConfig) (clientError error, serverAuthErrors []error) {
|
|
|
c1, c2, err := netPipe()
|
|
c1, c2, err := netPipe()
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
t.Fatalf("netPipe: %v", err)
|
|
t.Fatalf("netPipe: %v", err)
|
|
@@ -61,7 +68,6 @@ func tryAuthBothSides(t *testing.T, config *ClientConfig) (clientError error, se
|
|
|
return c.Serial == 666
|
|
return c.Serial == 666
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
serverConfig := &ServerConfig{
|
|
serverConfig := &ServerConfig{
|
|
|
PasswordCallback: func(conn ConnMetadata, pass []byte) (*Permissions, error) {
|
|
PasswordCallback: func(conn ConnMetadata, pass []byte) (*Permissions, error) {
|
|
|
if conn.User() == "testuser" && string(pass) == clientPassword {
|
|
if conn.User() == "testuser" && string(pass) == clientPassword {
|
|
@@ -85,6 +91,7 @@ func tryAuthBothSides(t *testing.T, config *ClientConfig) (clientError error, se
|
|
|
}
|
|
}
|
|
|
return nil, errors.New("keyboard-interactive failed")
|
|
return nil, errors.New("keyboard-interactive failed")
|
|
|
},
|
|
},
|
|
|
|
|
+ GSSAPIWithMICConfig: gssAPIWithMICConfig,
|
|
|
}
|
|
}
|
|
|
serverConfig.AddHostKey(testSigners["rsa"])
|
|
serverConfig.AddHostKey(testSigners["rsa"])
|
|
|
|
|
|
|
@@ -247,7 +254,7 @@ func TestMethodInvalidAlgorithm(t *testing.T) {
|
|
|
HostKeyCallback: InsecureIgnoreHostKey(),
|
|
HostKeyCallback: InsecureIgnoreHostKey(),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- err, serverErrors := tryAuthBothSides(t, config)
|
|
|
|
|
|
|
+ err, serverErrors := tryAuthBothSides(t, config, nil)
|
|
|
if err == nil {
|
|
if err == nil {
|
|
|
t.Fatalf("login succeeded")
|
|
t.Fatalf("login succeeded")
|
|
|
}
|
|
}
|
|
@@ -686,3 +693,206 @@ func TestClientAuthErrorList(t *testing.T) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+func TestAuthMethodGSSAPIWithMIC(t *testing.T) {
|
|
|
|
|
+ type testcase struct {
|
|
|
|
|
+ config *ClientConfig
|
|
|
|
|
+ gssConfig *GSSAPIWithMICConfig
|
|
|
|
|
+ clientWantErr string
|
|
|
|
|
+ serverWantErr string
|
|
|
|
|
+ }
|
|
|
|
|
+ testcases := []*testcase{
|
|
|
|
|
+ {
|
|
|
|
|
+ config: &ClientConfig{
|
|
|
|
|
+ User: "testuser",
|
|
|
|
|
+ Auth: []AuthMethod{
|
|
|
|
|
+ GSSAPIWithMICAuthMethod(
|
|
|
|
|
+ &FakeClient{
|
|
|
|
|
+ exchanges: []*exchange{
|
|
|
|
|
+ {
|
|
|
|
|
+ outToken: "client-valid-token-1",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ expectedToken: "server-valid-token-1",
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ mic: []byte("valid-mic"),
|
|
|
|
|
+ maxRound: 2,
|
|
|
|
|
+ }, "testtarget",
|
|
|
|
|
+ ),
|
|
|
|
|
+ },
|
|
|
|
|
+ HostKeyCallback: InsecureIgnoreHostKey(),
|
|
|
|
|
+ },
|
|
|
|
|
+ gssConfig: &GSSAPIWithMICConfig{
|
|
|
|
|
+ AllowLogin: func(conn ConnMetadata, srcName string) (*Permissions, error) {
|
|
|
|
|
+ if srcName != conn.User()+"@DOMAIN" {
|
|
|
|
|
+ return nil, fmt.Errorf("srcName is %s, conn user is %s", srcName, conn.User())
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil, nil
|
|
|
|
|
+ },
|
|
|
|
|
+ Server: &FakeServer{
|
|
|
|
|
+ exchanges: []*exchange{
|
|
|
|
|
+ {
|
|
|
|
|
+ outToken: "server-valid-token-1",
|
|
|
|
|
+ expectedToken: "client-valid-token-1",
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ maxRound: 1,
|
|
|
|
|
+ expectedMIC: []byte("valid-mic"),
|
|
|
|
|
+ srcName: "testuser@DOMAIN",
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ config: &ClientConfig{
|
|
|
|
|
+ User: "testuser",
|
|
|
|
|
+ Auth: []AuthMethod{
|
|
|
|
|
+ GSSAPIWithMICAuthMethod(
|
|
|
|
|
+ &FakeClient{
|
|
|
|
|
+ exchanges: []*exchange{
|
|
|
|
|
+ {
|
|
|
|
|
+ outToken: "client-valid-token-1",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ expectedToken: "server-valid-token-1",
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ mic: []byte("valid-mic"),
|
|
|
|
|
+ maxRound: 2,
|
|
|
|
|
+ }, "testtarget",
|
|
|
|
|
+ ),
|
|
|
|
|
+ },
|
|
|
|
|
+ HostKeyCallback: InsecureIgnoreHostKey(),
|
|
|
|
|
+ },
|
|
|
|
|
+ gssConfig: &GSSAPIWithMICConfig{
|
|
|
|
|
+ AllowLogin: func(conn ConnMetadata, srcName string) (*Permissions, error) {
|
|
|
|
|
+ return nil, fmt.Errorf("user is not allowed to login")
|
|
|
|
|
+ },
|
|
|
|
|
+ Server: &FakeServer{
|
|
|
|
|
+ exchanges: []*exchange{
|
|
|
|
|
+ {
|
|
|
|
|
+ outToken: "server-valid-token-1",
|
|
|
|
|
+ expectedToken: "client-valid-token-1",
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ maxRound: 1,
|
|
|
|
|
+ expectedMIC: []byte("valid-mic"),
|
|
|
|
|
+ srcName: "testuser@DOMAIN",
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ serverWantErr: "user is not allowed to login",
|
|
|
|
|
+ clientWantErr: "ssh: handshake failed: ssh: unable to authenticate",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ config: &ClientConfig{
|
|
|
|
|
+ User: "testuser",
|
|
|
|
|
+ Auth: []AuthMethod{
|
|
|
|
|
+ GSSAPIWithMICAuthMethod(
|
|
|
|
|
+ &FakeClient{
|
|
|
|
|
+ exchanges: []*exchange{
|
|
|
|
|
+ {
|
|
|
|
|
+ outToken: "client-valid-token-1",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ expectedToken: "server-valid-token-1",
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ mic: []byte("valid-mic"),
|
|
|
|
|
+ maxRound: 2,
|
|
|
|
|
+ }, "testtarget",
|
|
|
|
|
+ ),
|
|
|
|
|
+ },
|
|
|
|
|
+ HostKeyCallback: InsecureIgnoreHostKey(),
|
|
|
|
|
+ },
|
|
|
|
|
+ gssConfig: &GSSAPIWithMICConfig{
|
|
|
|
|
+ AllowLogin: func(conn ConnMetadata, srcName string) (*Permissions, error) {
|
|
|
|
|
+ if srcName != conn.User() {
|
|
|
|
|
+ return nil, fmt.Errorf("srcName is %s, conn user is %s", srcName, conn.User())
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil, nil
|
|
|
|
|
+ },
|
|
|
|
|
+ Server: &FakeServer{
|
|
|
|
|
+ exchanges: []*exchange{
|
|
|
|
|
+ {
|
|
|
|
|
+ outToken: "server-invalid-token-1",
|
|
|
|
|
+ expectedToken: "client-valid-token-1",
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ maxRound: 1,
|
|
|
|
|
+ expectedMIC: []byte("valid-mic"),
|
|
|
|
|
+ srcName: "testuser@DOMAIN",
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ clientWantErr: "ssh: handshake failed: got \"server-invalid-token-1\", want token \"server-valid-token-1\"",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ config: &ClientConfig{
|
|
|
|
|
+ User: "testuser",
|
|
|
|
|
+ Auth: []AuthMethod{
|
|
|
|
|
+ GSSAPIWithMICAuthMethod(
|
|
|
|
|
+ &FakeClient{
|
|
|
|
|
+ exchanges: []*exchange{
|
|
|
|
|
+ {
|
|
|
|
|
+ outToken: "client-valid-token-1",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ expectedToken: "server-valid-token-1",
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ mic: []byte("invalid-mic"),
|
|
|
|
|
+ maxRound: 2,
|
|
|
|
|
+ }, "testtarget",
|
|
|
|
|
+ ),
|
|
|
|
|
+ },
|
|
|
|
|
+ HostKeyCallback: InsecureIgnoreHostKey(),
|
|
|
|
|
+ },
|
|
|
|
|
+ gssConfig: &GSSAPIWithMICConfig{
|
|
|
|
|
+ AllowLogin: func(conn ConnMetadata, srcName string) (*Permissions, error) {
|
|
|
|
|
+ if srcName != conn.User() {
|
|
|
|
|
+ return nil, fmt.Errorf("srcName is %s, conn user is %s", srcName, conn.User())
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil, nil
|
|
|
|
|
+ },
|
|
|
|
|
+ Server: &FakeServer{
|
|
|
|
|
+ exchanges: []*exchange{
|
|
|
|
|
+ {
|
|
|
|
|
+ outToken: "server-valid-token-1",
|
|
|
|
|
+ expectedToken: "client-valid-token-1",
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ maxRound: 1,
|
|
|
|
|
+ expectedMIC: []byte("valid-mic"),
|
|
|
|
|
+ srcName: "testuser@DOMAIN",
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ serverWantErr: "got MICToken \"invalid-mic\", want \"valid-mic\"",
|
|
|
|
|
+ clientWantErr: "ssh: handshake failed: ssh: unable to authenticate",
|
|
|
|
|
+ },
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for i, c := range testcases {
|
|
|
|
|
+ clientErr, serverErrs := tryAuthBothSides(t, c.config, c.gssConfig)
|
|
|
|
|
+ if (c.clientWantErr == "") != (clientErr == nil) {
|
|
|
|
|
+ t.Fatalf("client got %v, want %s, case %d", clientErr, c.clientWantErr, i)
|
|
|
|
|
+ }
|
|
|
|
|
+ if (c.serverWantErr == "") != (len(serverErrs) == 2 && serverErrs[1] == nil || len(serverErrs) == 1) {
|
|
|
|
|
+ t.Fatalf("server got err %v, want %s", serverErrs, c.serverWantErr)
|
|
|
|
|
+ }
|
|
|
|
|
+ if c.clientWantErr != "" {
|
|
|
|
|
+ if clientErr != nil && !strings.Contains(clientErr.Error(), c.clientWantErr) {
|
|
|
|
|
+ t.Fatalf("client got %v, want %s, case %d", clientErr, c.clientWantErr, i)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ found := false
|
|
|
|
|
+ var errStrings []string
|
|
|
|
|
+ if c.serverWantErr != "" {
|
|
|
|
|
+ for _, err := range serverErrs {
|
|
|
|
|
+ found = found || (err != nil && strings.Contains(err.Error(), c.serverWantErr))
|
|
|
|
|
+ errStrings = append(errStrings, err.Error())
|
|
|
|
|
+ }
|
|
|
|
|
+ if !found {
|
|
|
|
|
+ t.Errorf("server got error %q, want substring %q, case %d", errStrings, c.serverWantErr, i)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|