@@ -15,6 +15,7 @@ import (
15
15
"os"
16
16
"runtime"
17
17
"strings"
18
+ "sync"
18
19
"testing"
19
20
)
20
21
@@ -1282,3 +1283,147 @@ func TestCertAuthOpenSSHCompat(t *testing.T) {
1282
1283
t .Fatalf ("unable to dial remote side: %s" , err )
1283
1284
}
1284
1285
}
1286
+
1287
+ func TestKeyboardInteractiveAuthEarlyFail (t * testing.T ) {
1288
+ const maxAuthTries = 2
1289
+
1290
+ // Start testserver
1291
+ dst , err := func () (string , error ) {
1292
+ var serverAddr string
1293
+ var serverErr error
1294
+ var wg sync.WaitGroup
1295
+ wg .Add (1 )
1296
+
1297
+ go func () {
1298
+ config := & ServerConfig {
1299
+ MaxAuthTries : maxAuthTries ,
1300
+ KeyboardInteractiveCallback : func (c ConnMetadata ,
1301
+ client KeyboardInteractiveChallenge ) (* Permissions , error ) {
1302
+ // Fail keyboard-interactive authentication early before
1303
+ // any prompt is sent to client.
1304
+ return nil , errors .New ("keyboard-interactive auth failed" )
1305
+ },
1306
+ PasswordCallback : func (c ConnMetadata ,
1307
+ pass []byte ) (* Permissions , error ) {
1308
+ if string (pass ) == clientPassword {
1309
+ return nil , nil
1310
+ }
1311
+ return nil , errors .New ("password auth failed" )
1312
+ },
1313
+ }
1314
+
1315
+ // Use a static hostkey.
1316
+ // This key has been generated with following ssh-keygen command
1317
+ // and used exclusively in this unit test:
1318
+ // $ ssh-keygen -t RSA -b 2048 -f /tmp/static_hostkey \
1319
+ // -C "Static RSA hostkey for golang.org/x/crypto/ssh unit test"
1320
+
1321
+ const privKeyData = `-----BEGIN OPENSSH PRIVATE KEY-----
1322
+ b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
1323
+ NhAAAAAwEAAQAAAQEAsg9ZsQ3vbWppRLe2NzzUIV5NcPbpO5EBvLyzfItURKmYHmwa6aoy
1324
+ P34fmEG3BVVx5f1pgw54Rdaic4hG2p2nvGIijTktDxFz+tREwwMfywpwrlJbGslUTi0TKO
1325
+ jTWkyDACjMwo65yXbsSZLq+8rGD3uinf3Vq1bVlaEckmClhWMLTsynr/YpdF2I/+InPCep
1326
+ 1AuaWj1dHFNL8fbWXd8xNONumkMS1i6xtP3PnzdUqN+DuoGy26x5ic3qxWVrUp69/J2J42
1327
+ /B0WEYbATAfCQiL8iGeeM7Ll45GASI4r93uDnXropnHQy+RThG5BFBRiAqmzN6kncri/k5
1328
+ 65p63Jb33QAAA/AX6WXzF+ll8wAAAAdzc2gtcnNhAAABAQCyD1mxDe9tamlEt7Y3PNQhXk
1329
+ 1w9uk7kQG8vLN8i1REqZgebBrpqjI/fh+YQbcFVXHl/WmDDnhF1qJziEbanae8YiKNOS0P
1330
+ EXP61ETDAx/LCnCuUlsayVROLRMo6NNaTIMAKMzCjrnJduxJkur7ysYPe6Kd/dWrVtWVoR
1331
+ ySYKWFYwtOzKev9il0XYj/4ic8J6nUC5paPV0cU0vx9tZd3zE0426aQxLWLrG0/c+fN1So
1332
+ 34O6gbLbrHmJzerFZWtSnr38nYnjb8HRYRhsBMB8JCIvyIZ54zsuXjkYBIjiv3e4Odeuim
1333
+ cdDL5FOEbkEUFGICqbM3qSdyuL+TnrmnrclvfdAAAAAwEAAQAAAQAJjuVjqbnWh8XK2InB
1334
+ gVRpziQeEkMG3YvYU9DWuKv3W5s81tTDAk3cNqr/g1eNw75veCD31gkCxrjFtuUGyzu70x
1335
+ DDv/P5QRiWuFpQlZRZU+Akm2skjvYllCnZIlZmHIFTutzy/LJgbC/W6zoN9h6Xqi1aicu0
1336
+ fN7OP23HNcTs2gNAhzidpDMGOAxdzpcnXeQ3JCFOcv4LSi7TgmJHvLv1AgXQggSHeB8Nf1
1337
+ DvS+E86O9Wm6Xj+OKRiEgrRlngNQQ0om9yhLmUMat7Nw3hn2ZSb2+ByaaYuDfQ6FAG9nno
1338
+ HjxaqSHF83/8fKXJW/wku6ee3hvjTBNuuvUCLkLF48DBAAAAgQDUV28GyoWTqIR+uOa/4t
1339
+ OyFjfTtTdQ6fnLaqxbiwOaPz6SXCiwE6qIEZF5Ll5QK+7tMPDKWcOak2uGaSUHcjaEXVh0
1340
+ kaKwFqiFIBY3IGwCuyixjJITl+g+48SrFgLWNrNpwVrw1NOv+iBz+z6SGqcsi5f030qyzv
1341
+ O2P2wkSZkQqgAAAIEA5UB0F2+Lh63JHvdoUDZutBvTgrsjpwiReIuy+7WgxeGHe54DaXTY
1342
+ HMOORZM6unDRvi6uBBul7ON9Cs20UGeER+ZMA2SKXTicb0UwJuCYKKO8AIlMP0ykyfaF1p
1343
+ ZJw9DciKEu92jx+e2MdhEOnIIt1jQ9e5UIMLsI/SicnseTDLUAAACBAMbV19NIEhjLczBp
1344
+ MEYSBGDnyN6HWyrHCuCFLSpnHWePd6/apExGE049YRmAgLYaycmnjX9VJRKoumk9zYv1zu
1345
+ W9WTuewZVuLjLpOq/4mO5/jOQortL1dUigiiA7ZTGazTFMHwG+fZdfVqgSxbMfEU2rYhND
1346
+ S0UghNmRaqbzNl+JAAAAOFN0YXRpYyBSU0EgaG9zdGtleSBmb3IgZ29sYW5nLm9yZy94L2
1347
+ NyeXB0by9zc2ggdW5pdCB0ZXN0AQI=
1348
+ -----END OPENSSH PRIVATE KEY-----`
1349
+
1350
+ private , err := ParsePrivateKey ([]byte (privKeyData ))
1351
+ if err != nil {
1352
+ serverErr = err
1353
+ wg .Done ()
1354
+ return
1355
+ }
1356
+ config .AddHostKey (private )
1357
+
1358
+ listener , err := net .Listen ("tcp" , "127.0.0.1:" )
1359
+ if err != nil {
1360
+ serverErr = err
1361
+ wg .Done ()
1362
+ return
1363
+ }
1364
+ defer listener .Close ()
1365
+ serverAddr = listener .Addr ().String ()
1366
+ wg .Done ()
1367
+
1368
+ nConn , err := listener .Accept ()
1369
+ if err != nil {
1370
+ return
1371
+ }
1372
+
1373
+ conn , chans , reqs , err := NewServerConn (nConn , config )
1374
+ if err != nil {
1375
+ return
1376
+ }
1377
+ _ = conn .Close ()
1378
+
1379
+ var connWg sync.WaitGroup
1380
+ connWg .Add (1 )
1381
+ go func () {
1382
+ defer connWg .Done ()
1383
+ DiscardRequests (reqs )
1384
+ }()
1385
+ for newChannel := range chans {
1386
+ newChannel .Reject (Prohibited ,
1387
+ "testserver not accepting requests" )
1388
+ }
1389
+ connWg .Wait ()
1390
+ }()
1391
+
1392
+ wg .Wait ()
1393
+ return serverAddr , serverErr
1394
+ }()
1395
+ if err != nil {
1396
+ t .Fatalf ("failed to start testserver: %v" , err )
1397
+ }
1398
+
1399
+ // Connect to testserver expect KeyboardInteractive() to be not called and
1400
+ // PasswordCallback() to be called.
1401
+ passwordCallbackCalled := false
1402
+ cfg := & ClientConfig {
1403
+ User : "testuser" ,
1404
+ Auth : []AuthMethod {
1405
+ RetryableAuthMethod (KeyboardInteractive (func (name ,
1406
+ instruction string , questions []string ,
1407
+ echos []bool ) ([]string , error ) {
1408
+ t .Errorf ("unexpected call to KeyboardInteractive()" )
1409
+ return []string {clientPassword }, nil
1410
+ }), maxAuthTries ),
1411
+ RetryableAuthMethod (PasswordCallback (func () (secret string ,
1412
+ err error ) {
1413
+ t .Logf ("PasswordCallback()" )
1414
+ passwordCallbackCalled = true
1415
+ return clientPassword , nil
1416
+ }), maxAuthTries ),
1417
+ },
1418
+ HostKeyCallback : InsecureIgnoreHostKey (),
1419
+ }
1420
+
1421
+ conn , _ := Dial ("tcp" , dst , cfg )
1422
+ if conn != nil {
1423
+ conn .Close ()
1424
+ }
1425
+
1426
+ if ! passwordCallbackCalled {
1427
+ t .Errorf ("expected PasswordCallback() to be called" )
1428
+ }
1429
+ }
0 commit comments