Procházet zdrojové kódy

Merge pull request #555 from rod-hynes/master

Add ActiveAuthorizationIDs traffic rules filter
Rod Hynes před 5 roky
rodič
revize
73b20fe2d2

+ 37 - 13
psiphon/server/server_test.go

@@ -21,6 +21,7 @@ package server
 
 import (
 	"context"
+	"encoding/base64"
 	"encoding/json"
 	"errors"
 	"flag"
@@ -565,16 +566,18 @@ func runServer(t *testing.T, runConfig *runServerConfig) {
 		Keys: []*accesscontrol.VerificationKey{accessControlVerificationKey},
 	}
 
-	var authorizationID [32]byte
+	var seedAuthorizationID [32]byte
 
-	clientAuthorization, _, err := accesscontrol.IssueAuthorization(
+	clientAuthorization, authorizationID, err := accesscontrol.IssueAuthorization(
 		accessControlSigningKey,
-		authorizationID[:],
+		seedAuthorizationID[:],
 		time.Now().Add(1*time.Hour))
 	if err != nil {
 		t.Fatalf("error issuing authorization: %s", err)
 	}
 
+	authorizationIDStr := base64.StdEncoding.EncodeToString(authorizationID)
+
 	// Enable tactics when the test protocol is meek. Both the client and the
 	// server will be configured to support tactics. The client config will be
 	// set with a nonfunctional config so that the tactics request must
@@ -648,8 +651,14 @@ func runServer(t *testing.T, runConfig *runServerConfig) {
 	// requests.
 	trafficRulesFilename := filepath.Join(testDataDirName, "traffic_rules.json")
 	paveTrafficRulesFile(
-		t, trafficRulesFilename, propagationChannelID, accessType,
-		runConfig.requireAuthorization, runConfig.denyTrafficRules, livenessTestSize)
+		t,
+		trafficRulesFilename,
+		propagationChannelID,
+		accessType,
+		authorizationIDStr,
+		runConfig.requireAuthorization,
+		runConfig.denyTrafficRules,
+		livenessTestSize)
 
 	var tacticsConfigFilename string
 
@@ -658,8 +667,11 @@ func runServer(t *testing.T, runConfig *runServerConfig) {
 	if doServerTactics {
 		tacticsConfigFilename = filepath.Join(testDataDirName, "tactics_config.json")
 		paveTacticsConfigFile(
-			t, tacticsConfigFilename,
-			tacticsRequestPublicKey, tacticsRequestPrivateKey, tacticsRequestObfuscatedKey,
+			t,
+			tacticsConfigFilename,
+			tacticsRequestPublicKey,
+			tacticsRequestPrivateKey,
+			tacticsRequestObfuscatedKey,
 			runConfig.tunnelProtocol,
 			propagationChannelID,
 			livenessTestSize)
@@ -784,8 +796,13 @@ func runServer(t *testing.T, runConfig *runServerConfig) {
 		propagationChannelID = paveOSLConfigFile(t, oslConfigFilename)
 
 		paveTrafficRulesFile(
-			t, trafficRulesFilename, propagationChannelID, accessType,
-			runConfig.requireAuthorization, runConfig.denyTrafficRules,
+			t,
+			trafficRulesFilename,
+			propagationChannelID,
+			accessType,
+			authorizationIDStr,
+			runConfig.requireAuthorization,
+			runConfig.denyTrafficRules,
 			livenessTestSize)
 
 		p, _ := os.FindProcess(os.Getpid())
@@ -1696,8 +1713,13 @@ func pavePsinetDatabaseFile(
 }
 
 func paveTrafficRulesFile(
-	t *testing.T, trafficRulesFilename, propagationChannelID, accessType string,
-	requireAuthorization, deny bool,
+	t *testing.T,
+	trafficRulesFilename string,
+	propagationChannelID string,
+	accessType string,
+	authorizationID string,
+	requireAuthorization bool,
+	deny bool,
 	livenessTestSize int) {
 
 	// Test both default and fast lookups
@@ -1721,12 +1743,14 @@ func paveTrafficRulesFile(
 	}
 
 	authorizationFilterFormat := `,
-                    "AuthorizedAccessTypes" : ["%s"]
+                    "AuthorizedAccessTypes" : ["%s"],
+                    "ActiveAuthorizationIDs" : ["%s"]
 	`
 
 	authorizationFilter := ""
 	if requireAuthorization {
-		authorizationFilter = fmt.Sprintf(authorizationFilterFormat, accessType)
+		authorizationFilter = fmt.Sprintf(
+			authorizationFilterFormat, accessType, authorizationID)
 	}
 
 	// Supports two traffic rule test cases:

+ 54 - 11
psiphon/server/trafficRules.go

@@ -152,14 +152,21 @@ type TrafficRulesFilter struct {
 	// AuthorizedAccessTypes is ignored when AuthorizationsRevoked is true.
 	AuthorizedAccessTypes []string
 
+	// ActiveAuthorizationIDs specifies a list of authorization IDs, at least
+	// one of which the client must have presented an active authorization
+	// for and which must not be revoked.
+	// ActiveAuthorizationIDs is ignored when AuthorizationsRevoked is true.
+	ActiveAuthorizationIDs []string
+
 	// AuthorizationsRevoked indicates whether the client's authorizations
 	// must have been revoked. When true, authorizations must have been
 	// revoked. When omitted or false, this field is ignored.
 	AuthorizationsRevoked bool
 
-	regionLookup map[string]bool
-	ispLookup    map[string]bool
-	cityLookup   map[string]bool
+	regionLookup                map[string]bool
+	ispLookup                   map[string]bool
+	cityLookup                  map[string]bool
+	activeAuthorizationIDLookup map[string]bool
 }
 
 // TrafficRules specify the limits placed on client traffic.
@@ -451,6 +458,13 @@ func (set *TrafficRulesSet) initLookups() {
 				filter.cityLookup[city] = true
 			}
 		}
+
+		if len(filter.ActiveAuthorizationIDs) >= stringLookupThreshold {
+			filter.activeAuthorizationIDLookup = make(map[string]bool)
+			for _, ID := range filter.ActiveAuthorizationIDs {
+				filter.activeAuthorizationIDLookup[ID] = true
+			}
+		}
 	}
 
 	initTrafficRulesLookups(&set.DefaultRules)
@@ -646,17 +660,46 @@ func (set *TrafficRulesSet) GetTrafficRules(
 				continue
 			}
 
-		} else if len(filteredRules.Filter.AuthorizedAccessTypes) > 0 {
-			if !state.completed {
-				continue
-			}
+		} else {
+			if len(filteredRules.Filter.ActiveAuthorizationIDs) > 0 {
+				if !state.completed {
+					continue
+				}
+
+				if state.authorizationsRevoked {
+					continue
+				}
+
+				if filteredRules.Filter.activeAuthorizationIDLookup != nil {
+					found := false
+					for _, ID := range state.activeAuthorizationIDs {
+						if filteredRules.Filter.activeAuthorizationIDLookup[ID] {
+							found = true
+							break
+						}
+					}
+					if !found {
+						continue
+					}
+				} else {
+					if !common.ContainsAny(filteredRules.Filter.ActiveAuthorizationIDs, state.activeAuthorizationIDs) {
+						continue
+					}
+				}
 
-			if state.authorizationsRevoked {
-				continue
 			}
+			if len(filteredRules.Filter.AuthorizedAccessTypes) > 0 {
+				if !state.completed {
+					continue
+				}
 
-			if !common.ContainsAny(filteredRules.Filter.AuthorizedAccessTypes, state.authorizedAccessTypes) {
-				continue
+				if state.authorizationsRevoked {
+					continue
+				}
+
+				if !common.ContainsAny(filteredRules.Filter.AuthorizedAccessTypes, state.authorizedAccessTypes) {
+					continue
+				}
 			}
 		}
 

+ 2 - 0
psiphon/server/tunnelServer.go

@@ -1228,6 +1228,7 @@ type handshakeState struct {
 	completed               bool
 	apiProtocol             string
 	apiParams               common.APIParameters
+	activeAuthorizationIDs  []string
 	authorizedAccessTypes   []string
 	authorizationsRevoked   bool
 	expectDomainBytes       bool
@@ -2641,6 +2642,7 @@ func (sshClient *sshClient) setHandshakeState(
 
 		// Make the authorizedAccessTypes available for traffic rules filtering.
 
+		sshClient.handshakeState.activeAuthorizationIDs = authorizationIDs
 		sshClient.handshakeState.authorizedAccessTypes = authorizedAccessTypes
 
 		// On exit, sshClient.runTunnel will call releaseAuthorizations, which