ソースを参照

Merge pull request #78 from rod-hynes/master

Experimental circumvention enhancements
Rod Hynes 11 年 前
コミット
4f32c2810f
5 ファイル変更45 行追加6 行削除
  1. 12 1
      psiphon/TCPConn_unix.go
  2. 1 0
      psiphon/config.go
  3. 14 0
      psiphon/controller.go
  4. 8 0
      psiphon/meekConn.go
  5. 10 5
      psiphon/tlsDialer.go

+ 12 - 1
psiphon/TCPConn_unix.go

@@ -92,9 +92,20 @@ func interruptibleTCPDial(addr string, config *DialConfig) (conn *TCPConn, err e
 	if len(ipAddrs) < 1 {
 		return nil, ContextError(errors.New("no IP address"))
 	}
+
+	// Select an IP at random from the list, so we're not always
+	// trying the same IP (when > 1) which may be blocked.
+	// TODO: retry all IPs until one connects? For now, this retry
+	// will happen on subsequent TCPDial calls, when a different IP
+	// is selected.
+	index, err := MakeSecureRandomInt(len(ipAddrs))
+	if err != nil {
+		return nil, ContextError(err)
+	}
+
 	// TODO: IPv6 support
 	var ip [4]byte
-	copy(ip[:], ipAddrs[0].To4())
+	copy(ip[:], ipAddrs[index].To4())
 
 	// Enable interruption
 	conn = &TCPConn{

+ 1 - 0
psiphon/config.go

@@ -91,6 +91,7 @@ type Config struct {
 	SplitTunnelRoutesUrlFormat          string
 	SplitTunnelRoutesSignaturePublicKey string
 	SplitTunnelDnsServer                string
+	AlternateMeekFrontingAddresses      map[string][]string
 }
 
 // LoadConfig parses and validates a JSON format Psiphon config JSON

+ 14 - 0
psiphon/controller.go

@@ -629,6 +629,20 @@ loop:
 				// Completed this iteration
 				break
 			}
+
+			// Override fronting domain, if configured to do so.
+			// TODO: we could generate multiple candidates from
+			// the current server entry when there are many
+			// AlternateMeekFrontingAddresses for this MeekFrontingHost.
+			if addresses, ok := controller.config.AlternateMeekFrontingAddresses[serverEntry.MeekFrontingDomain]; ok {
+				index, err := MakeSecureRandomInt(len(addresses))
+				if err == nil {
+					address := addresses[index]
+					NoticeAlert("using alternate address for %s: %s", serverEntry.MeekFrontingDomain, address)
+					serverEntry.MeekFrontingDomain = address
+				}
+			}
+
 			select {
 			case controller.candidateServerEntries <- serverEntry:
 			case <-controller.stopEstablishingBroadcast:

+ 8 - 0
psiphon/meekConn.go

@@ -112,6 +112,13 @@ func DialMeek(
 	if useFronting {
 		// In this case, host is not what is dialed but is what ends up in the HTTP Host header
 		host = serverEntry.MeekFrontingHost
+
+		// We skip verifying the server certificate when the host address is an IP address. In the
+		// short term, this is a circumvention weakness: it's vulnerable to an active MiM attack
+		// which injects its own cert and decrypts the TLS and reads the custom Host header.
+		// We need to know which server cert to expect in order to perform verification in this case.
+		skipVerify := (net.ParseIP(serverEntry.MeekFrontingDomain) != nil)
+
 		// Custom TLS dialer:
 		//  - ignores the HTTP request address and uses the fronting domain
 		//  - disables SNI -- SNI breaks fronting when used with CDNs that support SNI on the server side.
@@ -121,6 +128,7 @@ func DialMeek(
 				Timeout:        meekConfig.ConnectTimeout,
 				FrontingAddr:   fmt.Sprintf("%s:%d", serverEntry.MeekFrontingDomain, 443),
 				SendServerName: false,
+				SkipVerify:     skipVerify,
 			})
 	} else {
 		// In this case, host is both what is dialed and what ends up in the HTTP Host header

+ 10 - 5
psiphon/tlsDialer.go

@@ -104,6 +104,9 @@ type CustomTLSConfig struct {
 	// (tlsdialer functionality)
 	SendServerName bool
 
+	// SkipVerify completely disables server certificate verification.
+	SkipVerify bool
+
 	// VerifyLegacyCertificate is a special case self-signed server
 	// certificate case. Ignores IP SANs and basic constraints. No
 	// certificate chain. Just checks that the server presented the
@@ -192,11 +195,13 @@ func CustomTLSDial(network, addr string, config *CustomTLSConfig) (*tls.Conn, er
 		err = <-errChannel
 	}
 
-	if err == nil && config.VerifyLegacyCertificate != nil {
-		err = verifyLegacyCertificate(conn, config.VerifyLegacyCertificate)
-	} else if err == nil && !config.SendServerName && !tlsConfig.InsecureSkipVerify {
-		// Manually verify certificates
-		err = verifyServerCerts(conn, serverName, tlsConfigCopy)
+	if !config.SkipVerify {
+		if err == nil && config.VerifyLegacyCertificate != nil {
+			err = verifyLegacyCertificate(conn, config.VerifyLegacyCertificate)
+		} else if err == nil && !config.SendServerName && !tlsConfig.InsecureSkipVerify {
+			// Manually verify certificates
+			err = verifyServerCerts(conn, serverName, tlsConfigCopy)
+		}
 	}
 
 	if err != nil {