// unpackRemoteServerListFile reads a file that contains a // zlib compressed authenticated data package, validates // the package, and returns the payload. func unpackRemoteServerListFile( config *Config, filename string) (string, error) { fileReader, err := os.Open(filename) if err != nil { return "", common.ContextError(err) } defer fileReader.Close() zlibReader, err := zlib.NewReader(fileReader) if err != nil { return "", common.ContextError(err) } dataPackage, err := ioutil.ReadAll(zlibReader) zlibReader.Close() if err != nil { return "", common.ContextError(err) } payload, err := common.ReadAuthenticatedDataPackage( dataPackage, config.RemoteServerListSignaturePublicKey) if err != nil { return "", common.ContextError(err) } return payload, nil }
// UnpackRegistry decompresses, validates, and loads a // JSON encoded OSL registry. func UnpackRegistry( compressedRegistry []byte, signingPublicKey string) (*Registry, []byte, error) { packagedRegistry, err := uncompress(compressedRegistry) if err != nil { return nil, nil, common.ContextError(err) } encodedRegistry, err := common.ReadAuthenticatedDataPackage( packagedRegistry, signingPublicKey) if err != nil { return nil, nil, common.ContextError(err) } registryJSON, err := base64.StdEncoding.DecodeString(encodedRegistry) if err != nil { return nil, nil, common.ContextError(err) } registry, err := LoadRegistry(registryJSON) return registry, registryJSON, err }
// UnpackOSL reassembles the key for the OSL specified by oslID and uses // that key to decrypt oslFileContents, uncompress the contents, validate // the authenticated package, and extract the payload. // Clients will call UnpackOSL for OSLs indicated by GetSeededOSLIDs along // with their downloaded content. // SLOKLookup is called to determine which SLOKs are seeded with the client. func (registry *Registry) UnpackOSL( lookup SLOKLookup, oslID []byte, oslFileContents []byte, signingPublicKey string) (string, error) { fileSpec, ok := registry.oslIDLookup[string(oslID)] if !ok { return "", common.ContextError(errors.New("unknown OSL ID")) } ok, fileKey, err := fileSpec.KeyShares.reassembleKey(lookup, true) if err != nil { return "", common.ContextError(err) } if !ok { return "", common.ContextError(errors.New("unseeded OSL")) } decryptedContents, err := unbox(fileKey, oslFileContents) if err != nil { return "", common.ContextError(err) } dataPackage, err := uncompress(decryptedContents) if err != nil { return "", common.ContextError(err) } oslPayload, err := common.ReadAuthenticatedDataPackage( dataPackage, signingPublicKey) if err != nil { return "", common.ContextError(err) } return oslPayload, nil }
// getRoutes makes a web request to download fresh routes data for the // given region, as indicated by the tunnel. It uses web caching, If-None-Match/ETag, // to save downloading known routes data repeatedly. If the web request // fails and cached routes data is present, that cached data is returned. func (classifier *SplitTunnelClassifier) getRoutes(tunnel *Tunnel) (routesData []byte, err error) { url := fmt.Sprintf(classifier.fetchRoutesUrlFormat, tunnel.serverContext.clientRegion) request, err := http.NewRequest("GET", url, nil) if err != nil { return nil, common.ContextError(err) } etag, err := GetSplitTunnelRoutesETag(tunnel.serverContext.clientRegion) if err != nil { return nil, common.ContextError(err) } if etag != "" { request.Header.Add("If-None-Match", etag) } tunneledDialer := func(_, addr string) (conn net.Conn, err error) { return tunnel.sshClient.Dial("tcp", addr) } transport := &http.Transport{ Dial: tunneledDialer, ResponseHeaderTimeout: time.Duration(*tunnel.config.FetchRoutesTimeoutSeconds) * time.Second, } httpClient := &http.Client{ Transport: transport, Timeout: time.Duration(*tunnel.config.FetchRoutesTimeoutSeconds) * time.Second, } // At this time, the largest uncompressed routes data set is ~1MB. For now, // the processing pipeline is done all in-memory. useCachedRoutes := false response, err := httpClient.Do(request) if err == nil && (response.StatusCode != http.StatusOK && response.StatusCode != http.StatusNotModified) { response.Body.Close() err = fmt.Errorf("unexpected response status code: %d", response.StatusCode) } if err != nil { NoticeAlert("failed to request split tunnel routes package: %s", common.ContextError(err)) useCachedRoutes = true } if !useCachedRoutes { defer response.Body.Close() if response.StatusCode == http.StatusNotModified { useCachedRoutes = true } } var routesDataPackage []byte if !useCachedRoutes { routesDataPackage, err = ioutil.ReadAll(response.Body) if err != nil { NoticeAlert("failed to download split tunnel routes package: %s", common.ContextError(err)) useCachedRoutes = true } } var encodedRoutesData string if !useCachedRoutes { encodedRoutesData, err = common.ReadAuthenticatedDataPackage( routesDataPackage, classifier.routesSignaturePublicKey) if err != nil { NoticeAlert("failed to read split tunnel routes package: %s", common.ContextError(err)) useCachedRoutes = true } } var compressedRoutesData []byte if !useCachedRoutes { compressedRoutesData, err = base64.StdEncoding.DecodeString(encodedRoutesData) if err != nil { NoticeAlert("failed to decode split tunnel routes: %s", common.ContextError(err)) useCachedRoutes = true } } if !useCachedRoutes { zlibReader, err := zlib.NewReader(bytes.NewReader(compressedRoutesData)) if err == nil { routesData, err = ioutil.ReadAll(zlibReader) zlibReader.Close() } if err != nil { NoticeAlert("failed to decompress split tunnel routes: %s", common.ContextError(err)) useCachedRoutes = true } } if !useCachedRoutes { etag := response.Header.Get("ETag") if etag != "" { err := SetSplitTunnelRoutes(tunnel.serverContext.clientRegion, etag, routesData) if err != nil { NoticeAlert("failed to cache split tunnel routes: %s", common.ContextError(err)) // Proceed with fetched data, even when we can't cache it } } } if useCachedRoutes { routesData, err = GetSplitTunnelRoutesData(tunnel.serverContext.clientRegion) if err != nil { return nil, common.ContextError(err) } if routesData == nil { return nil, common.ContextError(errors.New("no cached routes")) } } return routesData, nil }