crypto/ssh: return unexpected msg error when server fails keyboard-in… · golang/crypto@4f1243e (original) (raw)

`@@ -16,6 +16,7 @@ import (

`

16

16

`"runtime"

`

17

17

`"strings"

`

18

18

`"testing"

`

``

19

`+

"time"

`

19

20

`)

`

20

21

``

21

22

`type keyboardInteractive map[string]string

`

`@@ -1293,3 +1294,97 @@ func TestCertAuthOpenSSHCompat(t *testing.T) {

`

1293

1294

`t.Fatalf("unable to dial remote side: %s", err)

`

1294

1295

` }

`

1295

1296

`}

`

``

1297

+

``

1298

`+

func TestKeyboardInteractiveAuthEarlyFail(t *testing.T) {

`

``

1299

`+

const maxAuthTries = 2

`

``

1300

+

``

1301

`+

c1, c2, err := netPipe()

`

``

1302

`+

if err != nil {

`

``

1303

`+

t.Fatalf("netPipe: %v", err)

`

``

1304

`+

}

`

``

1305

`+

defer c1.Close()

`

``

1306

`+

defer c2.Close()

`

``

1307

+

``

1308

`+

// Start testserver

`

``

1309

`+

serverConfig := &ServerConfig{

`

``

1310

`+

MaxAuthTries: maxAuthTries,

`

``

1311

`+

KeyboardInteractiveCallback: func(c ConnMetadata,

`

``

1312

`+

client KeyboardInteractiveChallenge) (*Permissions, error) {

`

``

1313

`+

// Fail keyboard-interactive authentication early before

`

``

1314

`+

// any prompt is sent to client.

`

``

1315

`+

return nil, errors.New("keyboard-interactive auth failed")

`

``

1316

`+

},

`

``

1317

`+

PasswordCallback: func(c ConnMetadata,

`

``

1318

`+

pass []byte) (*Permissions, error) {

`

``

1319

`+

if string(pass) == clientPassword {

`

``

1320

`+

return nil, nil

`

``

1321

`+

}

`

``

1322

`+

return nil, errors.New("password auth failed")

`

``

1323

`+

},

`

``

1324

`+

}

`

``

1325

`+

serverConfig.AddHostKey(testSigners["rsa"])

`

``

1326

+

``

1327

`+

serverDone := make(chan struct{})

`

``

1328

`+

go func() {

`

``

1329

`+

defer func() { serverDone <- struct{}{} }()

`

``

1330

`+

conn, chans, reqs, err := NewServerConn(c2, serverConfig)

`

``

1331

`+

if err != nil {

`

``

1332

`+

return

`

``

1333

`+

}

`

``

1334

`+

_ = conn.Close()

`

``

1335

+

``

1336

`+

discarderDone := make(chan struct{})

`

``

1337

`+

go func() {

`

``

1338

`+

defer func() { discarderDone <- struct{}{} }()

`

``

1339

`+

DiscardRequests(reqs)

`

``

1340

`+

}()

`

``

1341

`+

for newChannel := range chans {

`

``

1342

`+

newChannel.Reject(Prohibited,

`

``

1343

`+

"testserver not accepting requests")

`

``

1344

`+

}

`

``

1345

+

``

1346

`+

<-discarderDone

`

``

1347

`+

}()

`

``

1348

+

``

1349

`+

// Connect to testserver, expect KeyboardInteractive() to be not called,

`

``

1350

`+

// PasswordCallback() to be called and connection to succeed.

`

``

1351

`+

passwordCallbackCalled := false

`

``

1352

`+

clientConfig := &ClientConfig{

`

``

1353

`+

User: "testuser",

`

``

1354

`+

Auth: []AuthMethod{

`

``

1355

`+

RetryableAuthMethod(KeyboardInteractive(func(name,

`

``

1356

`+

instruction string, questions []string,

`

``

1357

`+

echos []bool) ([]string, error) {

`

``

1358

`+

t.Errorf("unexpected call to KeyboardInteractive()")

`

``

1359

`+

return []string{clientPassword}, nil

`

``

1360

`+

}), maxAuthTries),

`

``

1361

`+

RetryableAuthMethod(PasswordCallback(func() (secret string,

`

``

1362

`+

err error) {

`

``

1363

`+

t.Logf("PasswordCallback()")

`

``

1364

`+

passwordCallbackCalled = true

`

``

1365

`+

return clientPassword, nil

`

``

1366

`+

}), maxAuthTries),

`

``

1367

`+

},

`

``

1368

`+

HostKeyCallback: InsecureIgnoreHostKey(),

`

``

1369

`+

}

`

``

1370

+

``

1371

`+

conn, _, _, err := NewClientConn(c1, "", clientConfig)

`

``

1372

`+

if err != nil {

`

``

1373

`+

t.Errorf("unexpected NewClientConn() error: %v", err)

`

``

1374

`+

}

`

``

1375

`+

if conn != nil {

`

``

1376

`+

conn.Close()

`

``

1377

`+

}

`

``

1378

+

``

1379

`+

// Wait for server to finish. Fail test eventually in case server does not

`

``

1380

`+

// finish in reasonable time.

`

``

1381

`+

select {

`

``

1382

`+

case <-serverDone:

`

``

1383

`+

case <-time.After(60 * time.Second):

`

``

1384

`+

t.Fatalf("server did not finish")

`

``

1385

`+

}

`

``

1386

+

``

1387

`+

if !passwordCallbackCalled {

`

``

1388

`+

t.Errorf("expected PasswordCallback() to be called")

`

``

1389

`+

}

`

``

1390

`+

}

`