Просмотр исходного кода

Merge pull request #132 from rod-hynes/master

Add UseTrustedCACertificatesForStockTLS functionality
Rod Hynes 10 лет назад
Родитель
Сommit
a0542aa540
4 измененных файлов с 70 добавлено и 15 удалено
  1. 11 1
      psiphon/config.go
  2. 43 0
      psiphon/net.go
  3. 1 1
      psiphon/serverApi.go
  4. 15 13
      psiphon/upgradeDownload.go

+ 11 - 1
psiphon/config.go

@@ -251,10 +251,20 @@ type Config struct {
 	EmitBytesTransferred bool
 
 	// UseIndistinguishableTLS enables use of an alternative TLS stack with a less
-	// distinct fingerprint (ClientHello content) than the stock Go TLS. This
+	// distinct fingerprint (ClientHello content) than the stock Go TLS.
+	// UseIndistinguishableTLS only applies to untunneled TLS connections. This
 	// parameter is only supported on platforms built with OpenSSL.
+	// Requires TrustedCACertificatesFilename to be set.
 	UseIndistinguishableTLS bool
 
+	// UseTrustedCACertificates toggles use of the trusted CA certs, specified
+	// in TrustedCACertificatesFilename, for tunneled TLS connections that expect
+	// server certificates signed with public certificate authorities (currently,
+	// only upgrade downloads). This option is used with stock Go TLS in cases where
+	// Go may fail to obtain a list of root CAs from the operating system.
+	// Requires TrustedCACertificatesFilename to be set.
+	UseTrustedCACertificatesForStockTLS bool
+
 	// TrustedCACertificatesFilename specifies a file containing trusted CA certs.
 	// The file contents should be compatible with OpenSSL's SSL_CTX_load_verify_locations.
 	// When specified, this enables use of indistinguishable TLS for HTTPS requests

+ 43 - 0
psiphon/net.go

@@ -20,9 +20,12 @@
 package psiphon
 
 import (
+	"crypto/tls"
 	"crypto/x509"
+	"errors"
 	"fmt"
 	"io"
+	"io/ioutil"
 	"net"
 	"net/http"
 	"net/url"
@@ -302,3 +305,43 @@ func MakeUntunneledHttpsClient(
 
 	return httpClient, urlComponents.String(), nil
 }
+
+// MakeTunneledHttpClient returns a net/http.Client which is
+// configured to use custom dialing features including tunneled
+// dialing and, optionally, UseTrustedCACertificatesForStockTLS.
+// Unlike MakeUntunneledHttpsClient and makePsiphonHttpsClient,
+// This http.Client uses stock TLS and no scheme transformation
+// hack is required.
+func MakeTunneledHttpClient(
+	config *Config,
+	tunnel *Tunnel,
+	requestTimeout time.Duration) (*http.Client, error) {
+
+	tunneledDialer := func(_, addr string) (conn net.Conn, err error) {
+		return tunnel.sshClient.Dial("tcp", addr)
+	}
+
+	transport := &http.Transport{
+		Dial: tunneledDialer,
+		ResponseHeaderTimeout: requestTimeout,
+	}
+
+	if config.UseTrustedCACertificatesForStockTLS {
+		if config.TrustedCACertificatesFilename == "" {
+			return nil, ContextError(errors.New(
+				"UseTrustedCACertificatesForStockTLS requires TrustedCACertificatesFilename"))
+		}
+		rootCAs := x509.NewCertPool()
+		certData, err := ioutil.ReadFile(config.TrustedCACertificatesFilename)
+		if err != nil {
+			return nil, ContextError(err)
+		}
+		rootCAs.AppendCertsFromPEM(certData)
+		transport.TLSClientConfig = &tls.Config{RootCAs: rootCAs}
+	}
+
+	return &http.Client{
+		Transport: transport,
+		Timeout:   requestTimeout,
+	}, nil
+}

+ 1 - 1
psiphon/serverApi.go

@@ -618,7 +618,7 @@ func buildRequestUrl(baseRequestUrl, path string, extraParams ...*ExtraParam) st
 	return requestUrl.String()
 }
 
-// makeHttpsClient creates a Psiphon HTTPS client that tunnels requests and which validates
+// makePsiphonHttpsClient creates a Psiphon HTTPS client that tunnels requests and which validates
 // the web server using the Psiphon server entry web server certificate.
 // This is not a general purpose HTTPS client.
 // As the custom dialer makes an explicit TLS connection, URLs submitted to the returned

+ 15 - 13
psiphon/upgradeDownload.go

@@ -22,7 +22,6 @@ package psiphon
 import (
 	"fmt"
 	"io"
-	"net"
 	"net/http"
 	"os"
 )
@@ -33,6 +32,16 @@ import (
 // config.UpgradeDownloadFilename.
 // NOTE: this code does not check that any existing file at config.UpgradeDownloadFilename
 // is actually the version specified in clientUpgradeVersion.
+//
+// BUG: a download that resumes after automation replaces the server-side upgrade entity
+// will end up with corrupt data (some part of the older entity, followed by part of
+// the newer entity). This is not fatal since authentication of the upgrade package will
+// will detect this and the upgrade will be re-downloaded in its entirety. A fix would
+// involve storing the entity ETag with the partial download and using If-Range
+// (http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.27), or, since S3 doesn't
+// list the If-Range header as supported
+// (http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html), If-Match followed
+// be a re-request on failure.
 func DownloadUpgrade(config *Config, clientUpgradeVersion string, tunnel *Tunnel) error {
 
 	// Check if complete file already downloaded
@@ -41,6 +50,11 @@ func DownloadUpgrade(config *Config, clientUpgradeVersion string, tunnel *Tunnel
 		return nil
 	}
 
+	httpClient, err := MakeTunneledHttpClient(config, tunnel, DOWNLOAD_UPGRADE_TIMEOUT)
+	if err != nil {
+		return ContextError(err)
+	}
+
 	partialFilename := fmt.Sprintf(
 		"%s.%s.part", config.UpgradeDownloadFilename, clientUpgradeVersion)
 
@@ -61,18 +75,6 @@ func DownloadUpgrade(config *Config, clientUpgradeVersion string, tunnel *Tunnel
 	}
 	request.Header.Add("Range", fmt.Sprintf("bytes=%d-", fileInfo.Size()))
 
-	tunneledDialer := func(_, addr string) (conn net.Conn, err error) {
-		return tunnel.sshClient.Dial("tcp", addr)
-	}
-	transport := &http.Transport{
-		Dial: tunneledDialer,
-		ResponseHeaderTimeout: DOWNLOAD_UPGRADE_TIMEOUT,
-	}
-	httpClient := &http.Client{
-		Transport: transport,
-		Timeout:   DOWNLOAD_UPGRADE_TIMEOUT,
-	}
-
 	response, err := httpClient.Do(request)
 
 	// The resumeable download may ask for bytes past the resource range