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

Send sponsor ID in the SSH password payload

Rod Hynes 6 месяцев назад
Родитель
Сommit
85ca0c8802
4 измененных файлов с 25 добавлено и 1 удалено
  1. 1 0
      psiphon/common/protocol/protocol.go
  2. 7 1
      psiphon/server/api.go
  3. 16 0
      psiphon/server/tunnelServer.go
  4. 1 0
      psiphon/tunnel.go

+ 1 - 0
psiphon/common/protocol/protocol.go

@@ -819,6 +819,7 @@ type SSHPasswordPayload struct {
 	SessionId          string   `json:"SessionId"`
 	SshPassword        string   `json:"SshPassword"`
 	ClientCapabilities []string `json:"ClientCapabilities"`
+	SponsorID          string   `json:"SponsorId"`
 }
 
 type MeekCookieData struct {

+ 7 - 1
psiphon/server/api.go

@@ -45,6 +45,8 @@ const (
 	CLIENT_PLATFORM_ANDROID = "Android"
 	CLIENT_PLATFORM_WINDOWS = "Windows"
 	CLIENT_PLATFORM_IOS     = "iOS"
+
+	SPONSOR_ID_LENGTH = 16
 )
 
 // sshAPIRequestHandler routes Psiphon API requests transported as
@@ -1101,7 +1103,7 @@ const (
 // requests and log events.
 var baseParams = []requestParamSpec{
 	{"propagation_channel_id", isHexDigits, 0},
-	{"sponsor_id", isHexDigits, 0},
+	{"sponsor_id", isSponsorID, 0},
 	{"client_version", isIntString, requestParamLogStringAsInt},
 	{"client_platform", isClientPlatform, 0},
 	{"client_features", isAnyString, requestParamOptional | requestParamArray},
@@ -1750,6 +1752,10 @@ func isMobileClientPlatform(clientPlatform string) bool {
 
 // Input validators follow the legacy validations rules in psi_web.
 
+func isSponsorID(config *Config, value string) bool {
+	return len(value) == SPONSOR_ID_LENGTH && isHexDigits(config, value)
+}
+
 func isHexDigits(_ *Config, value string) bool {
 	// Allows both uppercase in addition to lowercase, for legacy support.
 	return -1 == strings.IndexFunc(value, func(c rune) bool {

+ 16 - 0
psiphon/server/tunnelServer.go

@@ -1927,6 +1927,7 @@ type sshClient struct {
 	sessionID                            string
 	isFirstTunnelInSession               bool
 	supportsServerRequests               bool
+	sponsorID                            string
 	handshakeState                       handshakeState
 	udpgwChannelHandler                  *udpgwPortForwardMultiplexer
 	totalUdpgwChannelCount               int
@@ -2887,6 +2888,13 @@ func (sshClient *sshClient) passwordCallback(conn ssh.ConnMetadata, password []b
 	supportsServerRequests := common.Contains(
 		sshPasswordPayload.ClientCapabilities, protocol.CLIENT_CAPABILITY_SERVER_REQUESTS)
 
+	// This optional, early sponsor ID will be logged with server_tunnel if
+	// the tunnel doesn't reach handshakeState.completed.
+	sponsorID := sshPasswordPayload.SponsorID
+	if sponsorID != "" && !isSponsorID(sshClient.sshServer.support.Config, sponsorID) {
+		return nil, errors.Tracef("invalid sponsor ID")
+	}
+
 	sshClient.Lock()
 
 	// After this point, these values are read-only as they are read
@@ -2894,6 +2902,7 @@ func (sshClient *sshClient) passwordCallback(conn ssh.ConnMetadata, password []b
 	sshClient.sessionID = sessionID
 	sshClient.isFirstTunnelInSession = isFirstTunnelInSession
 	sshClient.supportsServerRequests = supportsServerRequests
+	sshClient.sponsorID = sponsorID
 
 	sshClient.Unlock()
 
@@ -3642,6 +3651,13 @@ func (sshClient *sshClient) logTunnel(additionalMetrics []LogFields) {
 
 	logFields["handshake_completed"] = sshClient.handshakeState.completed
 
+	// Use the handshake sponsor ID unless the handshake did not complete.
+	//
+	// TODO: check that the handshake sponsor ID matches the early sponsor ID?
+	if !sshClient.handshakeState.completed {
+		logFields["sponsor_id"] = sshClient.sponsorID
+	}
+
 	logFields["is_first_tunnel_in_session"] = sshClient.isFirstTunnelInSession
 
 	if sshClient.preHandshakeRandomStreamMetrics.count > 0 {

+ 1 - 0
psiphon/tunnel.go

@@ -1105,6 +1105,7 @@ func dialTunnel(
 		SessionId:          config.SessionID,
 		SshPassword:        dialParams.ServerEntry.SshPassword,
 		ClientCapabilities: []string{protocol.CLIENT_CAPABILITY_SERVER_REQUESTS},
+		SponsorID:          config.GetSponsorID(),
 	}
 
 	payload, err := json.Marshal(sshPasswordPayload)