ssh: Support multiple source-addresses, don't require IPv4 in tests.

The ssh tests currently require 127.0.0.1 to work which isn't
necessarily available everywhere. To fix the source-address tests,
support comma-separated source-address values per the PROTOCOL.certkeys
file:

  Comma-separated list of source addresses
  from which this certificate is accepted
  for authentication. Addresses are
  specified in CIDR format (nn.nn.nn.nn/nn
  or hhhh::hhhh/nn).
  If this option is not present then
  certificates may be presented from any
  source address.

Change-Id: I87536ff81ffa005c073da103021ebc0dfb12b620
Reviewed-on: https://go-review.googlesource.com/36110
Reviewed-by: Han-Wen Nienhuys <hanwen@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
diff --git a/ssh/client_auth_test.go b/ssh/client_auth_test.go
index 1d9681a..e384c79 100644
--- a/ssh/client_auth_test.go
+++ b/ssh/client_auth_test.go
@@ -333,14 +333,14 @@
 	}
 
 	// allowed source address
-	cert.CriticalOptions = map[string]string{"source-address": "127.0.0.42/24"}
+	cert.CriticalOptions = map[string]string{"source-address": "127.0.0.42/24,::42/120"}
 	cert.SignCert(rand.Reader, testSigners["ecdsa"])
 	if err := tryAuth(t, clientConfig); err != nil {
 		t.Errorf("cert login with source-address failed: %v", err)
 	}
 
 	// disallowed source address
-	cert.CriticalOptions = map[string]string{"source-address": "127.0.0.42"}
+	cert.CriticalOptions = map[string]string{"source-address": "127.0.0.42,::42"}
 	cert.SignCert(rand.Reader, testSigners["ecdsa"])
 	if err := tryAuth(t, clientConfig); err == nil {
 		t.Errorf("cert login with source-address succeeded")
diff --git a/ssh/handshake_test.go b/ssh/handshake_test.go
index 4d64376..1b83112 100644
--- a/ssh/handshake_test.go
+++ b/ssh/handshake_test.go
@@ -40,7 +40,7 @@
 // therefore is buffered (net.Pipe deadlocks if both sides start with
 // a write.)
 func netPipe() (net.Conn, net.Conn, error) {
-	listener, err := net.Listen("tcp", "127.0.0.1:0")
+	listener, err := net.Listen("tcp", ":0")
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/ssh/server.go b/ssh/server.go
index 28b109a..77c84d1 100644
--- a/ssh/server.go
+++ b/ssh/server.go
@@ -10,6 +10,7 @@
 	"fmt"
 	"io"
 	"net"
+	"strings"
 )
 
 // The Permissions type holds fine-grained permissions that are
@@ -231,7 +232,7 @@
 	return false
 }
 
-func checkSourceAddress(addr net.Addr, sourceAddr string) error {
+func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
 	if addr == nil {
 		return errors.New("ssh: no address known for client, but source-address match required")
 	}
@@ -241,18 +242,20 @@
 		return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", addr)
 	}
 
-	if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil {
-		if allowedIP.Equal(tcpAddr.IP) {
-			return nil
-		}
-	} else {
-		_, ipNet, err := net.ParseCIDR(sourceAddr)
-		if err != nil {
-			return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", sourceAddr, err)
-		}
+	for _, sourceAddr := range strings.Split(sourceAddrs, ",") {
+		if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil {
+			if allowedIP.Equal(tcpAddr.IP) {
+				return nil
+			}
+		} else {
+			_, ipNet, err := net.ParseCIDR(sourceAddr)
+			if err != nil {
+				return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", sourceAddr, err)
+			}
 
-		if ipNet.Contains(tcpAddr.IP) {
-			return nil
+			if ipNet.Contains(tcpAddr.IP) {
+				return nil
+			}
 		}
 	}