github.com/astaguna/popon-core@v0.0.0-20231019235610-96e42d76a5ff/psiphon/dialParameters.go (about) 1 /* 2 * Copyright (c) 2018, Psiphon Inc. 3 * All rights reserved. 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package psiphon 21 22 import ( 23 "bytes" 24 "context" 25 "crypto/md5" 26 "encoding/binary" 27 "net" 28 "net/http" 29 "strconv" 30 "strings" 31 "sync/atomic" 32 "time" 33 34 "github.com/astaguna/popon-core/psiphon/common" 35 "github.com/astaguna/popon-core/psiphon/common/errors" 36 "github.com/astaguna/popon-core/psiphon/common/fragmentor" 37 "github.com/astaguna/popon-core/psiphon/common/obfuscator" 38 "github.com/astaguna/popon-core/psiphon/common/parameters" 39 "github.com/astaguna/popon-core/psiphon/common/prng" 40 "github.com/astaguna/popon-core/psiphon/common/protocol" 41 "github.com/astaguna/popon-core/psiphon/common/regen" 42 "github.com/astaguna/popon-core/psiphon/common/resolver" 43 "github.com/astaguna/popon-core/psiphon/common/transforms" 44 "github.com/astaguna/popon-core/psiphon/common/values" 45 "golang.org/x/net/bpf" 46 ) 47 48 // DialParameters represents a selected protocol and all the related selected 49 // protocol attributes, many chosen at random, for a tunnel dial attempt. 50 // 51 // DialParameters is used: 52 // - to configure dialers 53 // - as a persistent record to store successful dial parameters for replay 54 // - to report dial stats in notices and Psiphon API calls. 55 // 56 // MeekResolvedIPAddress is set asynchronously, as it is not known until the 57 // dial process has begun. The atomic.Value will contain a string, initialized 58 // to "", and set to the resolved IP address once that part of the dial 59 // process has completed. 60 // 61 // DialParameters is not safe for concurrent use. 62 type DialParameters struct { 63 ServerEntry *protocol.ServerEntry `json:"-"` 64 NetworkID string `json:"-"` 65 IsReplay bool `json:"-"` 66 CandidateNumber int `json:"-"` 67 EstablishedTunnelsCount int `json:"-"` 68 69 IsExchanged bool 70 71 LastUsedTimestamp time.Time 72 LastUsedConfigStateHash []byte 73 LastUsedServerEntryHash []byte 74 75 NetworkLatencyMultiplier float64 76 77 TunnelProtocol string 78 79 DirectDialAddress string 80 DialPortNumber string 81 UpstreamProxyType string `json:"-"` 82 UpstreamProxyCustomHeaderNames []string `json:"-"` 83 84 BPFProgramName string 85 BPFProgramInstructions []bpf.RawInstruction 86 87 SelectedSSHClientVersion bool 88 SSHClientVersion string 89 SSHKEXSeed *prng.Seed 90 91 ObfuscatorPaddingSeed *prng.Seed 92 OSSHObfuscatorSeedTransformerParameters *transforms.ObfuscatorSeedTransformerParameters 93 94 OSSHPrefixSpec *obfuscator.OSSHPrefixSpec 95 OSSHPrefixSplitConfig *obfuscator.OSSHPrefixSplitConfig 96 97 FragmentorSeed *prng.Seed 98 99 FrontingProviderID string 100 101 MeekFrontingDialAddress string 102 MeekFrontingHost string 103 MeekDialAddress string 104 MeekTransformedHostName bool 105 MeekSNIServerName string 106 MeekVerifyServerName string 107 MeekVerifyPins []string 108 MeekHostHeader string 109 MeekObfuscatorPaddingSeed *prng.Seed 110 MeekTLSPaddingSize int 111 MeekResolvedIPAddress atomic.Value `json:"-"` 112 113 TLSOSSHTransformedSNIServerName bool 114 TLSOSSHSNIServerName string 115 TLSOSSHObfuscatorPaddingSeed *prng.Seed 116 117 SelectedUserAgent bool 118 UserAgent string 119 120 SelectedTLSProfile bool 121 TLSProfile string 122 NoDefaultTLSSessionID bool 123 TLSVersion string 124 RandomizedTLSProfileSeed *prng.Seed 125 126 QUICVersion string 127 QUICDialSNIAddress string 128 QUICClientHelloSeed *prng.Seed 129 ObfuscatedQUICPaddingSeed *prng.Seed 130 ObfuscatedQUICNonceTransformerParameters *transforms.ObfuscatorSeedTransformerParameters 131 QUICDisablePathMTUDiscovery bool 132 133 ConjureCachedRegistrationTTL time.Duration 134 ConjureAPIRegistration bool 135 ConjureAPIRegistrarBidirectionalURL string 136 ConjureAPIRegistrarDelay time.Duration 137 ConjureDecoyRegistration bool 138 ConjureDecoyRegistrarDelay time.Duration 139 ConjureDecoyRegistrarWidth int 140 ConjureTransport string 141 142 LivenessTestSeed *prng.Seed 143 144 APIRequestPaddingSeed *prng.Seed 145 146 HoldOffTunnelDuration time.Duration 147 148 DialConnMetrics common.MetricsSource `json:"-"` 149 DialConnNoticeMetrics common.NoticeMetricsSource `json:"-"` 150 ObfuscatedSSHConnMetrics common.MetricsSource `json:"-"` 151 152 DialDuration time.Duration `json:"-"` 153 154 resolver *resolver.Resolver `json:"-"` 155 ResolveParameters *resolver.ResolveParameters 156 157 HTTPTransformerParameters *transforms.HTTPTransformerParameters 158 159 dialConfig *DialConfig `json:"-"` 160 meekConfig *MeekConfig `json:"-"` 161 } 162 163 // MakeDialParameters creates a new DialParameters for the candidate server 164 // entry, including selecting a protocol and all the various protocol 165 // attributes. The input selectProtocol is used to comply with any active 166 // protocol selection constraints. 167 // 168 // When stored dial parameters are available and may be used, 169 // MakeDialParameters may replay previous dial parameters in an effort to 170 // leverage "known working" values instead of always chosing at random from a 171 // large space. 172 // 173 // MakeDialParameters will return nil/nil in cases where the candidate server 174 // entry should be skipped. 175 // 176 // To support replay, the caller must call DialParameters.Succeeded when a 177 // successful tunnel is established with the returned DialParameters; and must 178 // call DialParameters.Failed when a tunnel dial or activation fails, except 179 // when establishment is cancelled. 180 func MakeDialParameters( 181 config *Config, 182 upstreamProxyErrorCallback func(error), 183 canReplay func(serverEntry *protocol.ServerEntry, replayProtocol string) bool, 184 selectProtocol func(serverEntry *protocol.ServerEntry) (string, bool), 185 serverEntry *protocol.ServerEntry, 186 isTactics bool, 187 candidateNumber int, 188 establishedTunnelsCount int) (*DialParameters, error) { 189 190 networkID := config.GetNetworkID() 191 192 p := config.GetParameters().Get() 193 194 ttl := p.Duration(parameters.ReplayDialParametersTTL) 195 replayIgnoreChangedConfigState := p.Bool(parameters.ReplayIgnoreChangedConfigState) 196 replayBPF := p.Bool(parameters.ReplayBPF) 197 replaySSH := p.Bool(parameters.ReplaySSH) 198 replayObfuscatorPadding := p.Bool(parameters.ReplayObfuscatorPadding) 199 replayFragmentor := p.Bool(parameters.ReplayFragmentor) 200 replayTLSProfile := p.Bool(parameters.ReplayTLSProfile) 201 replayFronting := p.Bool(parameters.ReplayFronting) 202 replayHostname := p.Bool(parameters.ReplayHostname) 203 replayQUICVersion := p.Bool(parameters.ReplayQUICVersion) 204 replayObfuscatedQUIC := p.Bool(parameters.ReplayObfuscatedQUIC) 205 replayObfuscatedQUICNonceTransformer := p.Bool(parameters.ReplayObfuscatedQUICNonceTransformer) 206 replayConjureRegistration := p.Bool(parameters.ReplayConjureRegistration) 207 replayConjureTransport := p.Bool(parameters.ReplayConjureTransport) 208 replayLivenessTest := p.Bool(parameters.ReplayLivenessTest) 209 replayUserAgent := p.Bool(parameters.ReplayUserAgent) 210 replayAPIRequestPadding := p.Bool(parameters.ReplayAPIRequestPadding) 211 replayHoldOffTunnel := p.Bool(parameters.ReplayHoldOffTunnel) 212 replayResolveParameters := p.Bool(parameters.ReplayResolveParameters) 213 replayHTTPTransformerParameters := p.Bool(parameters.ReplayHTTPTransformerParameters) 214 replayOSSHSeedTransformerParameters := p.Bool(parameters.ReplayOSSHSeedTransformerParameters) 215 replayOSSHPrefix := p.Bool(parameters.ReplayOSSHPrefix) 216 217 // Check for existing dial parameters for this server/network ID. 218 219 dialParams, err := GetDialParameters( 220 config, serverEntry.IpAddress, networkID) 221 if err != nil { 222 NoticeWarning("GetDialParameters failed: %s", err) 223 dialParams = nil 224 // Proceed, without existing dial parameters. 225 } 226 227 // Check if replay is permitted: 228 // - TTL must be > 0 and existing dial parameters must not have expired 229 // as indicated by LastUsedTimestamp + TTL. 230 // - Config/tactics/server entry values must be unchanged from when 231 // previous dial parameters were established. 232 // - The protocol selection constraints must permit replay, as indicated 233 // by canReplay. 234 // - Must not be using an obsolete TLS profile that is no longer supported. 235 // - Must be using the latest Conjure API URL. 236 // 237 // When existing dial parameters don't meet these conditions, dialParams 238 // is reset to nil and new dial parameters will be generated. 239 240 var currentTimestamp time.Time 241 var configStateHash []byte 242 var serverEntryHash []byte 243 244 // When TTL is 0, replay is disabled; the timestamp remains 0 and the 245 // output DialParameters will not be stored by Success. 246 247 if ttl > 0 { 248 currentTimestamp = time.Now() 249 configStateHash, serverEntryHash = getDialStateHashes(config, p, serverEntry) 250 } 251 252 if dialParams != nil && 253 (ttl <= 0 || 254 dialParams.LastUsedTimestamp.Before(currentTimestamp.Add(-ttl)) || 255 256 // Replay is disabled when the current config state hash -- config 257 // dial parameters and the current tactics tag -- have changed 258 // since the last dial. This prioritizes applying any potential 259 // tactics change over redialing with parameters that may have 260 // changed in tactics. 261 // 262 // Because of this, frequent tactics changes may degrade replay 263 // effectiveness. When ReplayIgnoreChangedConfigState is set, 264 // differences in the config state hash are ignored. 265 // 266 // Limitation: some code which previously assumed that replay 267 // always implied unchanged tactics parameters may now use newer 268 // tactics parameters in replay cases when 269 // ReplayIgnoreChangedConfigState is set. One case is the call 270 // below to fragmentor.NewUpstreamConfig, made when initializing 271 // dialParams.dialConfig. 272 (!replayIgnoreChangedConfigState && !bytes.Equal(dialParams.LastUsedConfigStateHash, configStateHash)) || 273 274 // Replay is disabled when the server entry has changed. 275 !bytes.Equal(dialParams.LastUsedServerEntryHash, serverEntryHash) || 276 277 (dialParams.TLSProfile != "" && 278 !common.Contains(protocol.SupportedTLSProfiles, dialParams.TLSProfile)) || 279 (dialParams.QUICVersion != "" && 280 !common.Contains(protocol.SupportedQUICVersions, dialParams.QUICVersion)) || 281 282 // Legacy clients use ConjureAPIRegistrarURL with 283 // gotapdance.tapdance.APIRegistrar and new clients use 284 // ConjureAPIRegistrarBidirectionalURL with 285 // gotapdance.tapdance.APIRegistrarBidirectional. Updated clients 286 // may have replay dial parameters with the old 287 // ConjureAPIRegistrarURL field, which is now ignored. In this 288 // case, ConjureAPIRegistrarBidirectionalURL will be blank. Reset 289 // this replay. 290 (dialParams.ConjureAPIRegistration && dialParams.ConjureAPIRegistrarBidirectionalURL == "")) { 291 292 // In these cases, existing dial parameters are expired or no longer 293 // match the config state and so are cleared to avoid rechecking them. 294 295 err = DeleteDialParameters(serverEntry.IpAddress, networkID) 296 if err != nil { 297 NoticeWarning("DeleteDialParameters failed: %s", err) 298 } 299 dialParams = nil 300 } 301 302 if dialParams != nil { 303 if config.DisableReplay || 304 !canReplay(serverEntry, dialParams.TunnelProtocol) { 305 306 // In these ephemeral cases, existing dial parameters may still be valid 307 // and used in future establishment phases, and so are retained. 308 309 dialParams = nil 310 } 311 } 312 313 // IsExchanged: 314 // 315 // Dial parameters received via client-to-client exchange are partially 316 // initialized. Only the exchange fields are retained, and all other dial 317 // parameters fields must be initialized. This is not considered or logged as 318 // a replay. The exchange case is identified by the IsExchanged flag. 319 // 320 // When previously stored, IsExchanged dial parameters will have set the same 321 // timestamp and state hash used for regular dial parameters and the same 322 // logic above should invalidate expired or invalid exchanged dial 323 // parameters. 324 // 325 // Limitation: metrics will indicate when an exchanged server entry is used 326 // (source "EXCHANGED") but will not indicate when exchanged dial parameters 327 // are used vs. a redial after discarding dial parameters. 328 329 isReplay := (dialParams != nil) 330 isExchanged := isReplay && dialParams.IsExchanged 331 332 if !isReplay { 333 dialParams = &DialParameters{} 334 } 335 336 // Point to the current resolver to be used in dials. 337 dialParams.resolver = config.GetResolver() 338 if dialParams.resolver == nil { 339 return nil, errors.TraceNew("missing resolver") 340 } 341 342 if isExchanged { 343 // Set isReplay to false to cause all non-exchanged values to be 344 // initialized; this also causes the exchange case to not log as replay. 345 isReplay = false 346 } 347 348 // Set IsExchanged such that full dial parameters are stored and replayed 349 // upon success. 350 dialParams.IsExchanged = false 351 352 dialParams.ServerEntry = serverEntry 353 dialParams.NetworkID = networkID 354 dialParams.IsReplay = isReplay 355 dialParams.CandidateNumber = candidateNumber 356 dialParams.EstablishedTunnelsCount = establishedTunnelsCount 357 358 // Even when replaying, LastUsedTimestamp is updated to extend the TTL of 359 // replayed dial parameters which will be updated in the datastore upon 360 // success. 361 362 dialParams.LastUsedTimestamp = currentTimestamp 363 dialParams.LastUsedConfigStateHash = configStateHash 364 dialParams.LastUsedServerEntryHash = serverEntryHash 365 366 // Initialize dial parameters. 367 // 368 // When not replaying, all required parameters are initialized. When 369 // replaying, existing parameters are retained, subject to the replay-X 370 // tactics flags. 371 372 // Select a network latency multiplier for this dial. This allows clients to 373 // explore and discover timeout values appropriate for the current network. 374 // The selection applies per tunnel, to avoid delaying all establishment 375 // candidates due to excessive timeouts. The random selection is bounded by a 376 // min/max set in tactics and an exponential distribution is used so as to 377 // heavily favor values close to the min, which should be set to the 378 // singleton NetworkLatencyMultiplier tactics value. 379 // 380 // For NetworkLatencyMultiplierLambda close to 2.0, values near min are 381 // very approximately 10x more likely to be selected than values near 382 // max, while for NetworkLatencyMultiplierLambda close to 0.1, the 383 // distribution is close to uniform. 384 // 385 // Not all existing, persisted DialParameters will have a custom 386 // NetworkLatencyMultiplier value. Its zero value will cause the singleton 387 // NetworkLatencyMultiplier tactics value to be used instead, which is 388 // consistent with the pre-custom multiplier behavior in the older client 389 // version which persisted that DialParameters. 390 391 networkLatencyMultiplierMin := p.Float(parameters.NetworkLatencyMultiplierMin) 392 networkLatencyMultiplierMax := p.Float(parameters.NetworkLatencyMultiplierMax) 393 394 if !isReplay || 395 // Was selected... 396 (dialParams.NetworkLatencyMultiplier != 0.0 && 397 // But is now outside tactics range... 398 (dialParams.NetworkLatencyMultiplier < networkLatencyMultiplierMin || 399 dialParams.NetworkLatencyMultiplier > networkLatencyMultiplierMax)) { 400 401 dialParams.NetworkLatencyMultiplier = prng.ExpFloat64Range( 402 networkLatencyMultiplierMin, 403 networkLatencyMultiplierMax, 404 p.Float(parameters.NetworkLatencyMultiplierLambda)) 405 } 406 407 // After this point, any tactics parameters that apply the network latency 408 // multiplier will use this selected value. 409 p = config.GetParameters().GetCustom(dialParams.NetworkLatencyMultiplier) 410 411 if !isReplay && !isExchanged { 412 413 // TODO: should there be a pre-check of selectProtocol before incurring 414 // overhead of unmarshaling dial parameters? In may be that a server entry 415 // is fully incapable of satisfying the current protocol selection 416 // constraints. 417 418 selectedProtocol, ok := selectProtocol(serverEntry) 419 if !ok { 420 return nil, nil 421 } 422 423 dialParams.TunnelProtocol = selectedProtocol 424 } 425 426 // Skip this candidate when the clients tactics restrict usage of the 427 // fronting provider ID. See the corresponding server-side enforcement 428 // comments in server.TacticsListener.accept. 429 if protocol.TunnelProtocolUsesFrontedMeek(dialParams.TunnelProtocol) && 430 common.Contains( 431 p.Strings(parameters.RestrictFrontingProviderIDs), 432 dialParams.ServerEntry.FrontingProviderID) { 433 if p.WeightedCoinFlip( 434 parameters.RestrictFrontingProviderIDsClientProbability) { 435 436 // When skipping, return nil/nil as no error should be logged. 437 // NoticeSkipServerEntry emits each skip reason, regardless 438 // of server entry, at most once per session. 439 440 NoticeSkipServerEntry( 441 "restricted fronting provider ID: %s", 442 dialParams.ServerEntry.FrontingProviderID) 443 444 return nil, nil 445 } 446 } 447 448 if config.UseUpstreamProxy() { 449 450 // When UpstreamProxy is configured, ServerEntry.GetSupportedProtocols, when 451 // called via selectProtocol, will filter out protocols such that will not 452 // select a protocol incompatible with UpstreamProxy. This additional check 453 // will catch cases where selectProtocol does not apply this filter. 454 if !protocol.TunnelProtocolSupportsUpstreamProxy(dialParams.TunnelProtocol) { 455 456 NoticeSkipServerEntry( 457 "protocol does not support upstream proxy: %s", 458 dialParams.TunnelProtocol) 459 460 return nil, nil 461 } 462 463 // Skip this candidate when the server entry is not to be used with an 464 // upstream proxy. By not exposing servers from sources that are 465 // relatively hard to enumerate, this mechanism mitigates the risk of 466 // a malicious upstream proxy enumerating Psiphon servers. Populate 467 // the allowed sources with fronted servers to provide greater 468 // blocking resistence for clients using upstream proxy clients that 469 // are subject to blocking. 470 source := dialParams.ServerEntry.LocalSource 471 if !protocol.AllowServerEntrySourceWithUpstreamProxy(source) && 472 !p.Bool(parameters.UpstreamProxyAllowAllServerEntrySources) { 473 474 NoticeSkipServerEntry( 475 "server entry source disallowed with upstream proxy: %s", 476 source) 477 478 return nil, nil 479 } 480 } 481 482 if (!isReplay || !replayBPF) && 483 ClientBPFEnabled() && 484 protocol.TunnelProtocolUsesTCP(dialParams.TunnelProtocol) { 485 486 if p.WeightedCoinFlip(parameters.BPFClientTCPProbability) { 487 dialParams.BPFProgramName = "" 488 dialParams.BPFProgramInstructions = nil 489 ok, name, rawInstructions := p.BPFProgram(parameters.BPFClientTCPProgram) 490 if ok { 491 dialParams.BPFProgramName = name 492 dialParams.BPFProgramInstructions = rawInstructions 493 } 494 } 495 } 496 497 if !isReplay || !replaySSH { 498 dialParams.SelectedSSHClientVersion = true 499 dialParams.SSHClientVersion = values.GetSSHClientVersion() 500 dialParams.SSHKEXSeed, err = prng.NewSeed() 501 if err != nil { 502 return nil, errors.Trace(err) 503 } 504 } 505 506 if !isReplay || !replayObfuscatorPadding { 507 dialParams.ObfuscatorPaddingSeed, err = prng.NewSeed() 508 if err != nil { 509 return nil, errors.Trace(err) 510 } 511 512 if protocol.TunnelProtocolUsesMeek(dialParams.TunnelProtocol) { 513 dialParams.MeekObfuscatorPaddingSeed, err = prng.NewSeed() 514 if err != nil { 515 return nil, errors.Trace(err) 516 } 517 } else if protocol.TunnelProtocolUsesTLSOSSH(dialParams.TunnelProtocol) { 518 dialParams.TLSOSSHObfuscatorPaddingSeed, err = prng.NewSeed() 519 if err != nil { 520 return nil, errors.Trace(err) 521 } 522 } 523 } 524 525 if !isReplay || !replayFragmentor { 526 dialParams.FragmentorSeed, err = prng.NewSeed() 527 if err != nil { 528 return nil, errors.Trace(err) 529 } 530 } 531 532 if (!isReplay || !replayConjureRegistration) && 533 protocol.TunnelProtocolUsesConjure(dialParams.TunnelProtocol) { 534 535 dialParams.ConjureCachedRegistrationTTL = p.Duration(parameters.ConjureCachedRegistrationTTL) 536 537 apiURL := p.String(parameters.ConjureAPIRegistrarBidirectionalURL) 538 decoyWidth := p.Int(parameters.ConjureDecoyRegistrarWidth) 539 540 dialParams.ConjureAPIRegistration = apiURL != "" 541 dialParams.ConjureDecoyRegistration = decoyWidth != 0 542 543 // We select only one of API or decoy registration. When both are enabled, 544 // ConjureDecoyRegistrarProbability determines the probability of using 545 // decoy registration. 546 // 547 // In general, we disable retries in gotapdance and rely on Psiphon 548 // establishment to try/retry different registration schemes. This allows us 549 // to control the proportion of registration types attempted. And, in good 550 // network conditions, individual candidates are most likely to be cancelled 551 // before they exhaust their retry options. 552 553 if dialParams.ConjureAPIRegistration && dialParams.ConjureDecoyRegistration { 554 if p.WeightedCoinFlip(parameters.ConjureDecoyRegistrarProbability) { 555 dialParams.ConjureAPIRegistration = false 556 } 557 } 558 559 if dialParams.ConjureAPIRegistration { 560 561 // While Conjure API registration uses MeekConn and specifies common meek 562 // parameters, the meek address and SNI configuration is implemented in this 563 // code block and not in common code blocks below. The exception is TLS 564 // configuration. 565 // 566 // Accordingly, replayFronting/replayHostname have no effect on Conjure API 567 // registration replay. 568 569 dialParams.ConjureAPIRegistrarBidirectionalURL = apiURL 570 571 frontingSpecs := p.FrontingSpecs(parameters.ConjureAPIRegistrarFrontingSpecs) 572 dialParams.FrontingProviderID, 573 dialParams.MeekFrontingDialAddress, 574 dialParams.MeekSNIServerName, 575 dialParams.MeekVerifyServerName, 576 dialParams.MeekVerifyPins, 577 dialParams.MeekFrontingHost, 578 err = frontingSpecs.SelectParameters() 579 if err != nil { 580 return nil, errors.Trace(err) 581 } 582 583 if config.DisableSystemRootCAs && 584 (len(dialParams.MeekVerifyPins) == 0 || dialParams.MeekVerifyServerName == "") { 585 return nil, errors.TraceNew("TLS certificates must be verified in Conjure API registration") 586 } 587 588 dialParams.MeekDialAddress = net.JoinHostPort(dialParams.MeekFrontingDialAddress, "443") 589 dialParams.MeekHostHeader = dialParams.MeekFrontingHost 590 591 // For a FrontingSpec, an SNI value of "" indicates to disable/omit SNI, so 592 // never transform in that case. 593 if dialParams.MeekSNIServerName != "" { 594 if p.WeightedCoinFlip(parameters.TransformHostNameProbability) { 595 dialParams.MeekSNIServerName = selectHostName(dialParams.TunnelProtocol, p) 596 dialParams.MeekTransformedHostName = true 597 } 598 } 599 600 // The minimum delay value is determined by the Conjure station, which 601 // performs an asynchronous "liveness test" against the selected phantom 602 // IPs. The min/max range allows us to introduce some jitter so that we 603 // don't present a trivial inter-flow fingerprint: CDN connection, fixed 604 // delay, phantom dial. 605 606 minDelay := p.Duration(parameters.ConjureAPIRegistrarMinDelay) 607 maxDelay := p.Duration(parameters.ConjureAPIRegistrarMaxDelay) 608 dialParams.ConjureAPIRegistrarDelay = prng.Period(minDelay, maxDelay) 609 610 } else if dialParams.ConjureDecoyRegistration { 611 612 dialParams.ConjureDecoyRegistrarWidth = decoyWidth 613 minDelay := p.Duration(parameters.ConjureDecoyRegistrarMinDelay) 614 maxDelay := p.Duration(parameters.ConjureDecoyRegistrarMaxDelay) 615 dialParams.ConjureAPIRegistrarDelay = prng.Period(minDelay, maxDelay) 616 617 } else { 618 619 return nil, errors.TraceNew("no Conjure registrar configured") 620 } 621 } 622 623 if (!isReplay || !replayConjureTransport) && 624 protocol.TunnelProtocolUsesConjure(dialParams.TunnelProtocol) { 625 626 dialParams.ConjureTransport = protocol.CONJURE_TRANSPORT_MIN_OSSH 627 if p.WeightedCoinFlip( 628 parameters.ConjureTransportObfs4Probability) { 629 dialParams.ConjureTransport = protocol.CONJURE_TRANSPORT_OBFS4_OSSH 630 } 631 } 632 633 usingTLS := protocol.TunnelProtocolUsesMeekHTTPS(dialParams.TunnelProtocol) || 634 protocol.TunnelProtocolUsesTLSOSSH(dialParams.TunnelProtocol) || 635 dialParams.ConjureAPIRegistration 636 637 if (!isReplay || !replayTLSProfile) && usingTLS { 638 639 dialParams.SelectedTLSProfile = true 640 641 requireTLS12SessionTickets := protocol.TunnelProtocolRequiresTLS12SessionTickets( 642 dialParams.TunnelProtocol) 643 644 requireTLS13Support := protocol.TunnelProtocolRequiresTLS13Support(dialParams.TunnelProtocol) 645 646 isFronted := protocol.TunnelProtocolUsesFrontedMeek(dialParams.TunnelProtocol) || 647 dialParams.ConjureAPIRegistration 648 649 dialParams.TLSProfile, dialParams.TLSVersion, dialParams.RandomizedTLSProfileSeed, err = SelectTLSProfile( 650 requireTLS12SessionTickets, requireTLS13Support, isFronted, serverEntry.FrontingProviderID, p) 651 if err != nil { 652 return nil, errors.Trace(err) 653 } 654 655 if dialParams.TLSProfile == "" && (requireTLS12SessionTickets || requireTLS13Support) { 656 return nil, errors.TraceNew("required TLS profile not found") 657 } 658 659 dialParams.NoDefaultTLSSessionID = p.WeightedCoinFlip( 660 parameters.NoDefaultTLSSessionIDProbability) 661 } 662 663 if (!isReplay || !replayFronting) && 664 protocol.TunnelProtocolUsesFrontedMeek(dialParams.TunnelProtocol) { 665 666 dialParams.FrontingProviderID = serverEntry.FrontingProviderID 667 668 dialParams.MeekFrontingDialAddress, dialParams.MeekFrontingHost, err = 669 selectFrontingParameters(serverEntry) 670 if err != nil { 671 return nil, errors.Trace(err) 672 } 673 } 674 675 if !isReplay || !replayHostname { 676 677 // Any MeekHostHeader selections made here will be overridden below, 678 // as required, for fronting cases. 679 680 if protocol.TunnelProtocolUsesMeekHTTPS(dialParams.TunnelProtocol) || 681 protocol.TunnelProtocolUsesFrontedMeekQUIC(dialParams.TunnelProtocol) { 682 683 dialParams.MeekSNIServerName = "" 684 hostname := "" 685 if p.WeightedCoinFlip(parameters.TransformHostNameProbability) { 686 dialParams.MeekSNIServerName = selectHostName(dialParams.TunnelProtocol, p) 687 hostname = dialParams.MeekSNIServerName 688 dialParams.MeekTransformedHostName = true 689 } else { 690 691 // Always select a hostname for the Host header in this case. 692 // Unlike HTTP, the Host header isn't plaintext on the wire, 693 // and so there's no anti-fingerprint benefit from presenting 694 // the server IP address in the Host header. Omitting the 695 // server IP here can prevent exposing it in certain 696 // scenarios where the traffic is rerouted and arrives at a 697 // different HTTPS server. 698 699 hostname = selectHostName(dialParams.TunnelProtocol, p) 700 } 701 if serverEntry.MeekServerPort == 443 { 702 dialParams.MeekHostHeader = hostname 703 } else { 704 dialParams.MeekHostHeader = net.JoinHostPort( 705 hostname, strconv.Itoa(serverEntry.MeekServerPort)) 706 } 707 708 } else if protocol.TunnelProtocolUsesTLSOSSH(dialParams.TunnelProtocol) { 709 710 dialParams.TLSOSSHSNIServerName = "" 711 if p.WeightedCoinFlip(parameters.TransformHostNameProbability) { 712 dialParams.TLSOSSHSNIServerName = selectHostName(dialParams.TunnelProtocol, p) 713 dialParams.TLSOSSHTransformedSNIServerName = true 714 } 715 716 } else if protocol.TunnelProtocolUsesMeekHTTP(dialParams.TunnelProtocol) { 717 718 dialParams.MeekHostHeader = "" 719 hostname := serverEntry.IpAddress 720 if p.WeightedCoinFlip(parameters.TransformHostNameProbability) { 721 hostname = selectHostName(dialParams.TunnelProtocol, p) 722 dialParams.MeekTransformedHostName = true 723 } 724 if serverEntry.MeekServerPort == 80 { 725 dialParams.MeekHostHeader = hostname 726 } else { 727 dialParams.MeekHostHeader = net.JoinHostPort( 728 hostname, strconv.Itoa(serverEntry.MeekServerPort)) 729 } 730 } else if protocol.TunnelProtocolUsesQUIC(dialParams.TunnelProtocol) { 731 dialParams.QUICDialSNIAddress = selectHostName(dialParams.TunnelProtocol, p) 732 } 733 } 734 735 if (!isReplay || !replayQUICVersion) && 736 protocol.TunnelProtocolUsesQUIC(dialParams.TunnelProtocol) { 737 738 isFronted := protocol.TunnelProtocolUsesFrontedMeekQUIC(dialParams.TunnelProtocol) 739 dialParams.QUICVersion = selectQUICVersion(isFronted, serverEntry, p) 740 741 // Due to potential tactics configurations, it may be that no QUIC 742 // version is selected. Abort immediately, with no error, as in the 743 // selectProtocol case. quic.Dial and quic.NewQUICTransporter will 744 // check for a missing QUIC version, but at that later stage an 745 // unnecessary failed_tunnel can be logged in this scenario. 746 if dialParams.QUICVersion == "" { 747 return nil, nil 748 } 749 750 if protocol.QUICVersionHasRandomizedClientHello(dialParams.QUICVersion) { 751 dialParams.QUICClientHelloSeed, err = prng.NewSeed() 752 if err != nil { 753 return nil, errors.Trace(err) 754 } 755 } 756 757 dialParams.QUICDisablePathMTUDiscovery = 758 protocol.QUICVersionUsesPathMTUDiscovery(dialParams.QUICVersion) && 759 p.WeightedCoinFlip(parameters.QUICDisableClientPathMTUDiscoveryProbability) 760 } 761 762 if (!isReplay || !replayObfuscatedQUIC) && 763 protocol.QUICVersionIsObfuscated(dialParams.QUICVersion) { 764 765 dialParams.ObfuscatedQUICPaddingSeed, err = prng.NewSeed() 766 if err != nil { 767 return nil, errors.Trace(err) 768 } 769 } 770 771 if protocol.QUICVersionIsObfuscated(dialParams.QUICVersion) { 772 773 if serverEntry.DisableObfuscatedQUICTransforms { 774 775 dialParams.ObfuscatedQUICNonceTransformerParameters = nil 776 777 } else if !isReplay || !replayObfuscatedQUICNonceTransformer { 778 779 params, err := makeSeedTransformerParameters( 780 p, 781 parameters.ObfuscatedQUICNonceTransformProbability, 782 parameters.ObfuscatedQUICNonceTransformSpecs, 783 parameters.ObfuscatedQUICNonceTransformScopedSpecNames) 784 if err != nil { 785 return nil, errors.Trace(err) 786 } 787 788 if params.TransformSpec != nil { 789 dialParams.ObfuscatedQUICNonceTransformerParameters = params 790 } else { 791 dialParams.ObfuscatedQUICNonceTransformerParameters = nil 792 } 793 } 794 } 795 796 if !isReplay || !replayLivenessTest { 797 798 // TODO: initialize only when LivenessTestMaxUp/DownstreamBytes > 0? 799 dialParams.LivenessTestSeed, err = prng.NewSeed() 800 if err != nil { 801 return nil, errors.Trace(err) 802 } 803 } 804 805 if !isReplay || !replayAPIRequestPadding { 806 dialParams.APIRequestPaddingSeed, err = prng.NewSeed() 807 if err != nil { 808 return nil, errors.Trace(err) 809 } 810 } 811 812 // Initialize dialParams.ResolveParameters for dials that will resolve 813 // domain names, which currently includes fronted meek and Conjure API 814 // registration, where the dial address is not an IP address. 815 // 816 // dialParams.ResolveParameters must be nil when the dial address is an IP 817 // address to ensure that no DNS dial parameters are reported in metrics 818 // or diagnostics when when no domain is resolved. 819 820 useResolver := (protocol.TunnelProtocolUsesFrontedMeek(dialParams.TunnelProtocol) || 821 dialParams.ConjureAPIRegistration) && 822 net.ParseIP(dialParams.MeekFrontingDialAddress) == nil 823 824 if (!isReplay || !replayResolveParameters) && useResolver { 825 826 dialParams.ResolveParameters, err = dialParams.resolver.MakeResolveParameters( 827 p, dialParams.FrontingProviderID) 828 if err != nil { 829 return nil, errors.Trace(err) 830 } 831 } 832 833 if !isReplay || !replayHoldOffTunnel { 834 835 if common.Contains( 836 p.TunnelProtocols(parameters.HoldOffTunnelProtocols), dialParams.TunnelProtocol) || 837 838 (protocol.TunnelProtocolUsesFrontedMeek(dialParams.TunnelProtocol) && 839 common.Contains( 840 p.Strings(parameters.HoldOffTunnelFrontingProviderIDs), 841 dialParams.FrontingProviderID)) { 842 843 if p.WeightedCoinFlip(parameters.HoldOffTunnelProbability) { 844 845 dialParams.HoldOffTunnelDuration = prng.Period( 846 p.Duration(parameters.HoldOffTunnelMinDuration), 847 p.Duration(parameters.HoldOffTunnelMaxDuration)) 848 } 849 } 850 851 } 852 853 // OSSH prefix and seed transform are applied only to the OSSH tunnel protocol, 854 // and not to any other protocol layered over OSSH. 855 if dialParams.TunnelProtocol == protocol.TUNNEL_PROTOCOL_OBFUSCATED_SSH { 856 857 if serverEntry.DisableOSSHTransforms { 858 859 dialParams.OSSHObfuscatorSeedTransformerParameters = nil 860 861 } else if !isReplay || !replayOSSHSeedTransformerParameters { 862 863 params, err := makeSeedTransformerParameters( 864 p, 865 parameters.OSSHObfuscatorSeedTransformProbability, 866 parameters.OSSHObfuscatorSeedTransformSpecs, 867 parameters.OSSHObfuscatorSeedTransformScopedSpecNames) 868 if err != nil { 869 return nil, errors.Trace(err) 870 } 871 872 if params.TransformSpec != nil { 873 dialParams.OSSHObfuscatorSeedTransformerParameters = params 874 } else { 875 dialParams.OSSHObfuscatorSeedTransformerParameters = nil 876 } 877 } 878 879 if serverEntry.DisableOSSHPrefix { 880 dialParams.OSSHPrefixSpec = nil 881 dialParams.OSSHPrefixSplitConfig = nil 882 883 } else if !isReplay || !replayOSSHPrefix { 884 885 dialPortNumber, err := serverEntry.GetDialPortNumber(dialParams.TunnelProtocol) 886 if err != nil { 887 return nil, errors.Trace(err) 888 } 889 prefixSpec, err := makeOSSHPrefixSpecParameters(p, strconv.Itoa(dialPortNumber)) 890 if err != nil { 891 return nil, errors.Trace(err) 892 } 893 894 splitConfig, err := makeOSSHPrefixSplitConfig(p) 895 if err != nil { 896 return nil, errors.Trace(err) 897 } 898 899 if prefixSpec.Spec != nil { 900 dialParams.OSSHPrefixSpec = prefixSpec 901 dialParams.OSSHPrefixSplitConfig = splitConfig 902 } else { 903 dialParams.OSSHPrefixSpec = nil 904 dialParams.OSSHPrefixSplitConfig = nil 905 } 906 } 907 908 // OSSHPrefix supersedes OSSHObfuscatorSeedTransform. 909 // This ensures both tactics are not used simultaneously, 910 // until OSSHObfuscatorSeedTransform is removed. 911 if dialParams.OSSHPrefixSpec != nil { 912 dialParams.OSSHObfuscatorSeedTransformerParameters = nil 913 } 914 915 } 916 917 if protocol.TunnelProtocolUsesMeekHTTP(dialParams.TunnelProtocol) { 918 919 if serverEntry.DisableHTTPTransforms { 920 921 dialParams.HTTPTransformerParameters = nil 922 923 } else if !isReplay || !replayHTTPTransformerParameters { 924 925 isFronted := protocol.TunnelProtocolUsesFrontedMeek(dialParams.TunnelProtocol) 926 927 params, err := makeHTTPTransformerParameters(config.GetParameters().Get(), serverEntry.FrontingProviderID, isFronted) 928 if err != nil { 929 return nil, errors.Trace(err) 930 } 931 932 if params.ProtocolTransformSpec != nil { 933 dialParams.HTTPTransformerParameters = params 934 } else { 935 dialParams.HTTPTransformerParameters = nil 936 } 937 } 938 } 939 940 // Set dial address fields. This portion of configuration is 941 // deterministic, given the parameters established or replayed so far. 942 943 dialPortNumber, err := serverEntry.GetDialPortNumber(dialParams.TunnelProtocol) 944 if err != nil { 945 return nil, errors.Trace(err) 946 } 947 948 dialParams.DialPortNumber = strconv.Itoa(dialPortNumber) 949 950 switch dialParams.TunnelProtocol { 951 952 case protocol.TUNNEL_PROTOCOL_SSH, 953 protocol.TUNNEL_PROTOCOL_OBFUSCATED_SSH, 954 protocol.TUNNEL_PROTOCOL_TAPDANCE_OBFUSCATED_SSH, 955 protocol.TUNNEL_PROTOCOL_CONJURE_OBFUSCATED_SSH, 956 protocol.TUNNEL_PROTOCOL_QUIC_OBFUSCATED_SSH, 957 protocol.TUNNEL_PROTOCOL_TLS_OBFUSCATED_SSH: 958 959 dialParams.DirectDialAddress = net.JoinHostPort(serverEntry.IpAddress, dialParams.DialPortNumber) 960 961 case protocol.TUNNEL_PROTOCOL_FRONTED_MEEK, 962 protocol.TUNNEL_PROTOCOL_FRONTED_MEEK_QUIC_OBFUSCATED_SSH: 963 964 dialParams.MeekDialAddress = net.JoinHostPort(dialParams.MeekFrontingDialAddress, dialParams.DialPortNumber) 965 dialParams.MeekHostHeader = dialParams.MeekFrontingHost 966 if serverEntry.MeekFrontingDisableSNI { 967 dialParams.MeekSNIServerName = "" 968 // When SNI is omitted, the transformed host name is not used. 969 dialParams.MeekTransformedHostName = false 970 } else if !dialParams.MeekTransformedHostName { 971 dialParams.MeekSNIServerName = dialParams.MeekFrontingDialAddress 972 } 973 974 case protocol.TUNNEL_PROTOCOL_FRONTED_MEEK_HTTP: 975 976 dialParams.MeekDialAddress = net.JoinHostPort(dialParams.MeekFrontingDialAddress, dialParams.DialPortNumber) 977 dialParams.MeekHostHeader = dialParams.MeekFrontingHost 978 // For FRONTED HTTP, the Host header cannot be transformed. 979 dialParams.MeekTransformedHostName = false 980 981 case protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK: 982 983 dialParams.MeekDialAddress = net.JoinHostPort(serverEntry.IpAddress, dialParams.DialPortNumber) 984 985 case protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS, 986 protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET: 987 988 dialParams.MeekDialAddress = net.JoinHostPort(serverEntry.IpAddress, dialParams.DialPortNumber) 989 if !dialParams.MeekTransformedHostName { 990 // Note: IP address in SNI field will be omitted. 991 dialParams.MeekSNIServerName = serverEntry.IpAddress 992 } 993 994 default: 995 return nil, errors.Tracef( 996 "unknown tunnel protocol: %s", dialParams.TunnelProtocol) 997 } 998 999 if protocol.TunnelProtocolUsesMeek(dialParams.TunnelProtocol) { 1000 1001 host, _, _ := net.SplitHostPort(dialParams.MeekDialAddress) 1002 1003 if p.Bool(parameters.MeekDialDomainsOnly) { 1004 if net.ParseIP(host) != nil { 1005 // No error, as this is a "not supported" case. 1006 return nil, nil 1007 } 1008 } 1009 1010 // The underlying TLS implementation will automatically omit SNI for 1011 // IP address server name values; we have this explicit check here so 1012 // we record the correct value for stats. 1013 if net.ParseIP(dialParams.MeekSNIServerName) != nil { 1014 dialParams.MeekSNIServerName = "" 1015 } 1016 } 1017 1018 // Initialize/replay User-Agent header for HTTP upstream proxy and meek protocols. 1019 1020 if config.UseUpstreamProxy() { 1021 // Note: UpstreamProxyURL will be validated in the dial 1022 proxyURL, err := common.SafeParseURL(config.UpstreamProxyURL) 1023 if err == nil { 1024 dialParams.UpstreamProxyType = proxyURL.Scheme 1025 } 1026 } 1027 1028 dialCustomHeaders := makeDialCustomHeaders(config, p) 1029 1030 if protocol.TunnelProtocolUsesMeek(dialParams.TunnelProtocol) || 1031 dialParams.UpstreamProxyType == "http" || 1032 dialParams.ConjureAPIRegistration { 1033 1034 if !isReplay || !replayUserAgent { 1035 dialParams.SelectedUserAgent, dialParams.UserAgent = selectUserAgentIfUnset(p, dialCustomHeaders) 1036 } 1037 1038 if dialParams.SelectedUserAgent { 1039 dialCustomHeaders.Set("User-Agent", dialParams.UserAgent) 1040 } 1041 1042 } 1043 1044 // UpstreamProxyCustomHeaderNames is a reported metric. Just the names and 1045 // not the values are reported, in case the values are identifying. 1046 1047 if len(config.CustomHeaders) > 0 { 1048 dialParams.UpstreamProxyCustomHeaderNames = make([]string, 0) 1049 for name := range dialCustomHeaders { 1050 if name == "User-Agent" && dialParams.SelectedUserAgent { 1051 continue 1052 } 1053 dialParams.UpstreamProxyCustomHeaderNames = append(dialParams.UpstreamProxyCustomHeaderNames, name) 1054 } 1055 } 1056 1057 // Initialize Dial/MeekConfigs to be passed to the corresponding dialers. 1058 1059 // Custom ResolveParameters are set only when useResolver is true, but 1060 // DialConfig.ResolveIP is required and wired up unconditionally. Any 1061 // misconfigured or miscoded domain dial cases will use default 1062 // ResolveParameters. 1063 // 1064 // ResolveIP will use the networkID obtained above, as it will be used 1065 // almost immediately, instead of incurring the overhead of calling 1066 // GetNetworkID again. 1067 resolveIP := func(ctx context.Context, hostname string) ([]net.IP, error) { 1068 IPs, err := dialParams.resolver.ResolveIP( 1069 ctx, 1070 networkID, 1071 dialParams.ResolveParameters, 1072 hostname) 1073 if err != nil { 1074 return nil, errors.Trace(err) 1075 } 1076 return IPs, nil 1077 } 1078 1079 // Fragmentor configuration. 1080 // Note: fragmentorConfig is nil if fragmentor is disabled for prefixed OSSH. 1081 // 1082 // Limitation: when replaying and with ReplayIgnoreChangedConfigState set, 1083 // fragmentor.NewUpstreamConfig may select a config using newer tactics 1084 // parameters. 1085 fragmentorConfig := fragmentor.NewUpstreamConfig(p, dialParams.TunnelProtocol, dialParams.FragmentorSeed) 1086 if !p.Bool(parameters.OSSHPrefixEnableFragmentor) && dialParams.OSSHPrefixSpec != nil { 1087 fragmentorConfig = nil 1088 } 1089 1090 dialParams.dialConfig = &DialConfig{ 1091 DiagnosticID: serverEntry.GetDiagnosticID(), 1092 UpstreamProxyURL: config.UpstreamProxyURL, 1093 CustomHeaders: dialCustomHeaders, 1094 BPFProgramInstructions: dialParams.BPFProgramInstructions, 1095 DeviceBinder: config.deviceBinder, 1096 IPv6Synthesizer: config.IPv6Synthesizer, 1097 ResolveIP: resolveIP, 1098 TrustedCACertificatesFilename: config.TrustedCACertificatesFilename, 1099 FragmentorConfig: fragmentorConfig, 1100 UpstreamProxyErrorCallback: upstreamProxyErrorCallback, 1101 } 1102 1103 // Unconditionally initialize MeekResolvedIPAddress, so a valid string can 1104 // always be read. 1105 dialParams.MeekResolvedIPAddress.Store("") 1106 1107 if protocol.TunnelProtocolUsesMeek(dialParams.TunnelProtocol) || 1108 dialParams.ConjureAPIRegistration { 1109 1110 // For tactics requests, AddPsiphonFrontingHeader is set when set for 1111 // the related tunnel protocol. E.g., FRONTED-OSSH-MEEK for 1112 // FRONTED-MEEK-TACTICS. AddPsiphonFrontingHeader is not replayed. 1113 addPsiphonFrontingHeader := false 1114 if dialParams.FrontingProviderID != "" { 1115 addPsiphonFrontingHeader = common.Contains( 1116 p.LabeledTunnelProtocols( 1117 parameters.AddFrontingProviderPsiphonFrontingHeader, dialParams.FrontingProviderID), 1118 dialParams.TunnelProtocol) 1119 } 1120 1121 dialParams.meekConfig = &MeekConfig{ 1122 DiagnosticID: serverEntry.GetDiagnosticID(), 1123 Parameters: config.GetParameters(), 1124 DialAddress: dialParams.MeekDialAddress, 1125 UseQUIC: protocol.TunnelProtocolUsesFrontedMeekQUIC(dialParams.TunnelProtocol), 1126 QUICVersion: dialParams.QUICVersion, 1127 QUICClientHelloSeed: dialParams.QUICClientHelloSeed, 1128 QUICDisablePathMTUDiscovery: dialParams.QUICDisablePathMTUDiscovery, 1129 UseHTTPS: usingTLS, 1130 TLSProfile: dialParams.TLSProfile, 1131 LegacyPassthrough: serverEntry.ProtocolUsesLegacyPassthrough(dialParams.TunnelProtocol), 1132 NoDefaultTLSSessionID: dialParams.NoDefaultTLSSessionID, 1133 RandomizedTLSProfileSeed: dialParams.RandomizedTLSProfileSeed, 1134 UseObfuscatedSessionTickets: dialParams.TunnelProtocol == protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET, 1135 SNIServerName: dialParams.MeekSNIServerName, 1136 AddPsiphonFrontingHeader: addPsiphonFrontingHeader, 1137 VerifyServerName: dialParams.MeekVerifyServerName, 1138 VerifyPins: dialParams.MeekVerifyPins, 1139 DisableSystemRootCAs: config.DisableSystemRootCAs, 1140 HostHeader: dialParams.MeekHostHeader, 1141 TransformedHostName: dialParams.MeekTransformedHostName, 1142 ClientTunnelProtocol: dialParams.TunnelProtocol, 1143 MeekCookieEncryptionPublicKey: serverEntry.MeekCookieEncryptionPublicKey, 1144 MeekObfuscatedKey: serverEntry.MeekObfuscatedKey, 1145 MeekObfuscatorPaddingSeed: dialParams.MeekObfuscatorPaddingSeed, 1146 NetworkLatencyMultiplier: dialParams.NetworkLatencyMultiplier, 1147 HTTPTransformerParameters: dialParams.HTTPTransformerParameters, 1148 } 1149 1150 // Use an asynchronous callback to record the resolved IP address when 1151 // dialing a domain name. Note that DialMeek doesn't immediately 1152 // establish any HTTP connections, so the resolved IP address won't be 1153 // reported in all cases until after SSH traffic is relayed or a 1154 // endpoint request is made over the meek connection. 1155 dialParams.dialConfig.ResolvedIPCallback = func(IPAddress string) { 1156 dialParams.MeekResolvedIPAddress.Store(IPAddress) 1157 } 1158 1159 if isTactics { 1160 dialParams.meekConfig.Mode = MeekModeObfuscatedRoundTrip 1161 } else if dialParams.ConjureAPIRegistration { 1162 dialParams.meekConfig.Mode = MeekModePlaintextRoundTrip 1163 } else { 1164 dialParams.meekConfig.Mode = MeekModeRelay 1165 } 1166 } 1167 1168 return dialParams, nil 1169 } 1170 1171 func (dialParams *DialParameters) GetDialConfig() *DialConfig { 1172 return dialParams.dialConfig 1173 } 1174 1175 func (dialParams *DialParameters) GetTLSOSSHConfig(config *Config) *TLSTunnelConfig { 1176 1177 return &TLSTunnelConfig{ 1178 CustomTLSConfig: &CustomTLSConfig{ 1179 Parameters: config.GetParameters(), 1180 DialAddr: dialParams.DirectDialAddress, 1181 SNIServerName: dialParams.TLSOSSHSNIServerName, 1182 SkipVerify: true, 1183 VerifyServerName: "", 1184 VerifyPins: nil, 1185 TLSProfile: dialParams.TLSProfile, 1186 NoDefaultTLSSessionID: &dialParams.NoDefaultTLSSessionID, 1187 RandomizedTLSProfileSeed: dialParams.RandomizedTLSProfileSeed, 1188 }, 1189 // Obfuscated session tickets are not used because TLS-OSSH uses TLS 1.3. 1190 UseObfuscatedSessionTickets: false, 1191 // Meek obfuscated key used to allow clients with legacy unfronted 1192 // meek-https server entries, that have the passthrough capability, to 1193 // connect with TLS-OSSH to the servers corresponding to those server 1194 // entries, which now support TLS-OSSH by demultiplexing meek-https and 1195 // TLS-OSSH over the meek-https port. 1196 ObfuscatedKey: dialParams.ServerEntry.MeekObfuscatedKey, 1197 ObfuscatorPaddingSeed: dialParams.TLSOSSHObfuscatorPaddingSeed, 1198 } 1199 } 1200 1201 func (dialParams *DialParameters) GetMeekConfig() *MeekConfig { 1202 return dialParams.meekConfig 1203 } 1204 1205 // GetNetworkType returns a network type name, suitable for metrics, which is 1206 // derived from the network ID. 1207 func (dialParams *DialParameters) GetNetworkType() string { 1208 1209 // Unlike the logic in loggingNetworkIDGetter.GetNetworkID, we don't take the 1210 // arbitrary text before the first "-" since some platforms without network 1211 // detection support stub in random values to enable tactics. Instead we 1212 // check for and use the common network type prefixes currently used in 1213 // NetworkIDGetter implementations. 1214 1215 if strings.HasPrefix(dialParams.NetworkID, "VPN") { 1216 return "VPN" 1217 } 1218 if strings.HasPrefix(dialParams.NetworkID, "WIFI") { 1219 return "WIFI" 1220 } 1221 if strings.HasPrefix(dialParams.NetworkID, "MOBILE") { 1222 return "MOBILE" 1223 } 1224 return "UNKNOWN" 1225 } 1226 1227 func (dialParams *DialParameters) Succeeded() { 1228 1229 // When TTL is 0, don't store dial parameters. 1230 if dialParams.LastUsedTimestamp.IsZero() { 1231 return 1232 } 1233 1234 NoticeInfo("Set dial parameters for %s", dialParams.ServerEntry.GetDiagnosticID()) 1235 err := SetDialParameters(dialParams.ServerEntry.IpAddress, dialParams.NetworkID, dialParams) 1236 if err != nil { 1237 NoticeWarning("SetDialParameters failed: %s", err) 1238 } 1239 } 1240 1241 func (dialParams *DialParameters) Failed(config *Config) { 1242 1243 // When a tunnel fails, and the dial is a replay, clear the stored dial 1244 // parameters which are now presumed to be blocked, impaired or otherwise 1245 // no longer effective. 1246 // 1247 // It may be the case that a dial is not using stored dial parameters 1248 // (!IsReplay), and in this case we retain those dial parameters since they 1249 // were not exercised and may still be effective. 1250 // 1251 // Failed tunnel dial parameters may be retained with a configurable 1252 // probability; this is intended to help mitigate false positive failures due 1253 // to, e.g., temporary network disruptions or server load limiting. 1254 1255 if dialParams.IsReplay && 1256 !config.GetParameters().Get().WeightedCoinFlip( 1257 parameters.ReplayRetainFailedProbability) { 1258 1259 NoticeInfo("Delete dial parameters for %s", dialParams.ServerEntry.GetDiagnosticID()) 1260 err := DeleteDialParameters(dialParams.ServerEntry.IpAddress, dialParams.NetworkID) 1261 if err != nil { 1262 NoticeWarning("DeleteDialParameters failed: %s", err) 1263 } 1264 } 1265 } 1266 1267 func (dialParams *DialParameters) GetTLSVersionForMetrics() string { 1268 return getTLSVersionForMetrics(dialParams.TLSVersion, dialParams.NoDefaultTLSSessionID) 1269 } 1270 1271 func getTLSVersionForMetrics(tlsVersion string, noDefaultTLSSessionID bool) string { 1272 version := tlsVersion 1273 if noDefaultTLSSessionID { 1274 version += "-no_def_id" 1275 } 1276 return version 1277 } 1278 1279 // ExchangedDialParameters represents the subset of DialParameters that is 1280 // shared in a client-to-client exchange of server connection info. 1281 // 1282 // The purpose of client-to-client exchange if for one user that can connect 1283 // to help another user that cannot connect by sharing their connected 1284 // configuration, including the server entry and dial parameters. 1285 // 1286 // There are two concerns regarding which dial parameter fields are safe to 1287 // exchange: 1288 // 1289 // - Unlike signed server entries, there's no independent trust anchor 1290 // that can certify that the exchange data is valid. 1291 // 1292 // - While users should only perform the exchange with trusted peers, 1293 // the user's trust in their peer may be misplaced. 1294 // 1295 // This presents the possibility of attack such as the peer sending dial 1296 // parameters that could be used to trace/monitor/flag the importer; or 1297 // sending dial parameters, including dial address and SNI, to cause the peer 1298 // to appear to connect to a banned service. 1299 // 1300 // To mitigate these risks, only a subset of dial parameters are exchanged. 1301 // When exchanged dial parameters and imported and used, all unexchanged 1302 // parameters are generated locally. At this time, only the tunnel protocol is 1303 // exchanged. We consider tunnel protocol selection one of the key connection 1304 // success factors. 1305 // 1306 // In addition, the exchange peers may not be on the same network with the 1307 // same blocking and circumvention characteristics, which is another reason 1308 // to limit exchanged dial parameter values to broadly applicable fields. 1309 // 1310 // Unlike the exchanged (and otherwise acquired) server entry, 1311 // ExchangedDialParameters does not use the ServerEntry_Fields_ representation 1312 // which allows older clients to receive and store new, unknown fields. Such a 1313 // facility is less useful in this case, since exchanged dial parameters and 1314 // used immediately and have a short lifespan. 1315 // 1316 // TODO: exchange more dial parameters, such as TLS profile, QUIC version, etc. 1317 type ExchangedDialParameters struct { 1318 TunnelProtocol string 1319 } 1320 1321 // NewExchangedDialParameters creates a new ExchangedDialParameters from a 1322 // DialParameters, including only the exchanged values. 1323 // NewExchangedDialParameters assumes the input DialParameters has been 1324 // initialized and populated by MakeDialParameters. 1325 func NewExchangedDialParameters(dialParams *DialParameters) *ExchangedDialParameters { 1326 return &ExchangedDialParameters{ 1327 TunnelProtocol: dialParams.TunnelProtocol, 1328 } 1329 } 1330 1331 // Validate checks that the ExchangedDialParameters contains only valid values 1332 // and is compatible with the specified server entry. 1333 func (dialParams *ExchangedDialParameters) Validate(serverEntry *protocol.ServerEntry) error { 1334 if !common.Contains(protocol.SupportedTunnelProtocols, dialParams.TunnelProtocol) { 1335 return errors.Tracef("unknown tunnel protocol: %s", dialParams.TunnelProtocol) 1336 } 1337 if !serverEntry.SupportsProtocol(dialParams.TunnelProtocol) { 1338 return errors.Tracef("unsupported tunnel protocol: %s", dialParams.TunnelProtocol) 1339 } 1340 return nil 1341 } 1342 1343 // MakeDialParameters creates a new, partially intitialized DialParameters 1344 // from the values in ExchangedDialParameters. The returned DialParameters 1345 // must not be used directly for dialing. It is intended to be stored, and 1346 // then later fully initialized by MakeDialParameters. 1347 func (dialParams *ExchangedDialParameters) MakeDialParameters( 1348 config *Config, 1349 p parameters.ParametersAccessor, 1350 serverEntry *protocol.ServerEntry) *DialParameters { 1351 1352 configStateHash, serverEntryHash := getDialStateHashes(config, p, serverEntry) 1353 1354 return &DialParameters{ 1355 IsExchanged: true, 1356 LastUsedTimestamp: time.Now(), 1357 LastUsedConfigStateHash: configStateHash, 1358 LastUsedServerEntryHash: serverEntryHash, 1359 TunnelProtocol: dialParams.TunnelProtocol, 1360 } 1361 } 1362 1363 // getDialStateHashes returns two hashes: the config state hash reflects the 1364 // config dial parameters and tactics tag used for a dial; and the server 1365 // entry hash relects the server entry used for a dial. 1366 // 1367 // These hashes change if the input values change in a way that invalidates 1368 // any stored dial parameters. 1369 func getDialStateHashes( 1370 config *Config, 1371 p parameters.ParametersAccessor, 1372 serverEntry *protocol.ServerEntry) ([]byte, []byte) { 1373 1374 // MD5 hash is used solely as a data checksum and not for any security 1375 // purpose. 1376 hash := md5.New() 1377 1378 // Add a hash of relevant dial parameter config fields. Config fields 1379 // that change due to user preference changes, such as selected egress 1380 // region, are not to be included in config.dialParametersHash. 1381 // 1382 // Limitation: the config hash may change even when tactics will override the 1383 // changed config field. 1384 hash.Write(config.dialParametersHash) 1385 1386 // Add the active tactics tag. 1387 hash.Write([]byte(p.Tag())) 1388 1389 clientStateHash := hash.Sum(nil) 1390 1391 hash = md5.New() 1392 1393 // Add the server entry version and local timestamp, both of which should 1394 // change when the server entry contents change and/or a new local copy is 1395 // imported. 1396 // TODO: marshal entire server entry? 1397 var serverEntryConfigurationVersion [8]byte 1398 binary.BigEndian.PutUint64( 1399 serverEntryConfigurationVersion[:], 1400 uint64(serverEntry.ConfigurationVersion)) 1401 hash.Write(serverEntryConfigurationVersion[:]) 1402 hash.Write([]byte(serverEntry.LocalTimestamp)) 1403 1404 serverEntryHash := hash.Sum(nil) 1405 1406 return clientStateHash, serverEntryHash 1407 } 1408 1409 func selectFrontingParameters( 1410 serverEntry *protocol.ServerEntry) (string, string, error) { 1411 1412 frontingDialHost := "" 1413 frontingHost := "" 1414 1415 if len(serverEntry.MeekFrontingAddressesRegex) > 0 { 1416 1417 // Generate a front address based on the regex. 1418 1419 var err error 1420 frontingDialHost, err = regen.GenerateString(serverEntry.MeekFrontingAddressesRegex) 1421 if err != nil { 1422 return "", "", errors.Trace(err) 1423 } 1424 1425 } else { 1426 1427 // Randomly select, for this connection attempt, one front address for 1428 // fronting-capable servers. 1429 1430 if len(serverEntry.MeekFrontingAddresses) == 0 { 1431 return "", "", errors.TraceNew("MeekFrontingAddresses is empty") 1432 } 1433 1434 index := prng.Intn(len(serverEntry.MeekFrontingAddresses)) 1435 frontingDialHost = serverEntry.MeekFrontingAddresses[index] 1436 } 1437 1438 if len(serverEntry.MeekFrontingHosts) > 0 { 1439 1440 index := prng.Intn(len(serverEntry.MeekFrontingHosts)) 1441 frontingHost = serverEntry.MeekFrontingHosts[index] 1442 1443 } else { 1444 1445 // Backwards compatibility case 1446 frontingHost = serverEntry.MeekFrontingHost 1447 } 1448 1449 return frontingDialHost, frontingHost, nil 1450 } 1451 1452 func selectQUICVersion( 1453 isFronted bool, 1454 serverEntry *protocol.ServerEntry, 1455 p parameters.ParametersAccessor) string { 1456 1457 limitQUICVersions := p.QUICVersions(parameters.LimitQUICVersions) 1458 1459 var disableQUICVersions protocol.QUICVersions 1460 1461 if isFronted { 1462 if serverEntry.FrontingProviderID == "" { 1463 // Legacy server entry case 1464 disableQUICVersions = protocol.QUICVersions{ 1465 protocol.QUIC_VERSION_V1, 1466 protocol.QUIC_VERSION_RANDOMIZED_V1, 1467 protocol.QUIC_VERSION_OBFUSCATED_V1, 1468 protocol.QUIC_VERSION_DECOY_V1, 1469 } 1470 } else { 1471 disableQUICVersions = p.LabeledQUICVersions( 1472 parameters.DisableFrontingProviderQUICVersions, 1473 serverEntry.FrontingProviderID) 1474 } 1475 } 1476 1477 quicVersions := make([]string, 0) 1478 1479 // Don't use gQUIC versions when the server entry specifies QUICv1-only. 1480 supportedQUICVersions := protocol.SupportedQUICVersions 1481 if serverEntry.SupportsOnlyQUICv1() { 1482 supportedQUICVersions = protocol.SupportedQUICv1Versions 1483 } 1484 1485 for _, quicVersion := range supportedQUICVersions { 1486 1487 if len(limitQUICVersions) > 0 && 1488 !common.Contains(limitQUICVersions, quicVersion) { 1489 continue 1490 } 1491 1492 // Both tactics and the server entry can specify LimitQUICVersions. In 1493 // tactics, the parameter is intended to direct certain clients to 1494 // use a successful protocol variant. In the server entry, the 1495 // parameter may be used to direct all clients to send 1496 // consistent-looking protocol variants to a particular server; e.g., 1497 // only regular QUIC, or only obfuscated QUIC. 1498 // 1499 // The isFronted/QUICVersionIsObfuscated logic predates 1500 // ServerEntry.LimitQUICVersions; ServerEntry.LimitQUICVersions could 1501 // now be used to achieve a similar outcome. 1502 if len(serverEntry.LimitQUICVersions) > 0 && 1503 !common.Contains(serverEntry.LimitQUICVersions, quicVersion) { 1504 continue 1505 } 1506 1507 if isFronted && 1508 protocol.QUICVersionIsObfuscated(quicVersion) { 1509 continue 1510 } 1511 1512 if common.Contains(disableQUICVersions, quicVersion) { 1513 continue 1514 } 1515 1516 quicVersions = append(quicVersions, quicVersion) 1517 } 1518 1519 if len(quicVersions) == 0 { 1520 return "" 1521 } 1522 1523 choice := prng.Intn(len(quicVersions)) 1524 1525 return quicVersions[choice] 1526 } 1527 1528 // selectUserAgentIfUnset selects a User-Agent header if one is not set. 1529 func selectUserAgentIfUnset( 1530 p parameters.ParametersAccessor, headers http.Header) (bool, string) { 1531 1532 if _, ok := headers["User-Agent"]; !ok { 1533 1534 userAgent := "" 1535 if p.WeightedCoinFlip(parameters.PickUserAgentProbability) { 1536 userAgent = values.GetUserAgent() 1537 } 1538 1539 return true, userAgent 1540 } 1541 1542 return false, "" 1543 } 1544 1545 func makeDialCustomHeaders( 1546 config *Config, 1547 p parameters.ParametersAccessor) http.Header { 1548 1549 dialCustomHeaders := make(http.Header) 1550 if config.CustomHeaders != nil { 1551 for k, v := range config.CustomHeaders { 1552 dialCustomHeaders[k] = make([]string, len(v)) 1553 copy(dialCustomHeaders[k], v) 1554 } 1555 } 1556 1557 additionalCustomHeaders := p.HTTPHeaders(parameters.AdditionalCustomHeaders) 1558 for k, v := range additionalCustomHeaders { 1559 dialCustomHeaders[k] = make([]string, len(v)) 1560 copy(dialCustomHeaders[k], v) 1561 } 1562 return dialCustomHeaders 1563 } 1564 1565 func selectHostName( 1566 tunnelProtocol string, p parameters.ParametersAccessor) string { 1567 1568 limitProtocols := p.TunnelProtocols(parameters.CustomHostNameLimitProtocols) 1569 if len(limitProtocols) > 0 && !common.Contains(limitProtocols, tunnelProtocol) { 1570 return values.GetHostName() 1571 } 1572 1573 if !p.WeightedCoinFlip(parameters.CustomHostNameProbability) { 1574 return values.GetHostName() 1575 } 1576 1577 regexStrings := p.RegexStrings(parameters.CustomHostNameRegexes) 1578 if len(regexStrings) == 0 { 1579 return values.GetHostName() 1580 } 1581 1582 choice := prng.Intn(len(regexStrings)) 1583 hostName, err := regen.GenerateString(regexStrings[choice]) 1584 if err != nil { 1585 NoticeWarning("selectHostName: regen.Generate failed: %v", errors.Trace(err)) 1586 return values.GetHostName() 1587 } 1588 1589 return hostName 1590 } 1591 1592 // makeHTTPTransformerParameters generates HTTPTransformerParameters using the 1593 // input tactics parameters and optional frontingProviderID context. 1594 func makeHTTPTransformerParameters(p parameters.ParametersAccessor, 1595 frontingProviderID string, isFronted bool) (*transforms.HTTPTransformerParameters, error) { 1596 1597 params := transforms.HTTPTransformerParameters{} 1598 1599 // Select an HTTP transform. If the request is fronted, HTTP request 1600 // transforms are "scoped" by fronting provider ID. Otherwise, a transform 1601 // from the default scope (transforms.SCOPE_ANY == "") is selected. 1602 1603 var specsKey string 1604 var scopedSpecsNamesKey string 1605 1606 useTransform := false 1607 scope := transforms.SCOPE_ANY 1608 1609 if isFronted { 1610 if p.WeightedCoinFlip(parameters.FrontedHTTPProtocolTransformProbability) { 1611 useTransform = true 1612 scope = frontingProviderID 1613 specsKey = parameters.FrontedHTTPProtocolTransformSpecs 1614 scopedSpecsNamesKey = parameters.FrontedHTTPProtocolTransformScopedSpecNames 1615 } 1616 } else { 1617 // unfronted 1618 if p.WeightedCoinFlip(parameters.DirectHTTPProtocolTransformProbability) { 1619 useTransform = true 1620 specsKey = parameters.DirectHTTPProtocolTransformSpecs 1621 scopedSpecsNamesKey = parameters.DirectHTTPProtocolTransformScopedSpecNames 1622 } 1623 } 1624 1625 if useTransform { 1626 1627 specs := p.ProtocolTransformSpecs( 1628 specsKey) 1629 scopedSpecNames := p.ProtocolTransformScopedSpecNames( 1630 scopedSpecsNamesKey) 1631 1632 name, spec := specs.Select(scope, scopedSpecNames) 1633 1634 if spec != nil { 1635 params.ProtocolTransformName = name 1636 params.ProtocolTransformSpec = spec 1637 var err error 1638 // transform seed generated 1639 params.ProtocolTransformSeed, err = prng.NewSeed() 1640 if err != nil { 1641 return nil, errors.Trace(err) 1642 } 1643 } 1644 } 1645 1646 return ¶ms, nil 1647 } 1648 1649 // makeSeedTransformerParameters generates ObfuscatorSeedTransformerParameters 1650 // using the input tactics parameters. 1651 func makeSeedTransformerParameters(p parameters.ParametersAccessor, 1652 probabilityFieldName, specsKey, scopedSpecsKey string) (*transforms.ObfuscatorSeedTransformerParameters, error) { 1653 1654 if !p.WeightedCoinFlip(probabilityFieldName) { 1655 return &transforms.ObfuscatorSeedTransformerParameters{}, nil 1656 } 1657 1658 seed, err := prng.NewSeed() 1659 if err != nil { 1660 return nil, errors.Trace(err) 1661 } 1662 1663 specs := p.ProtocolTransformSpecs(specsKey) 1664 scopedSpecNames := p.ProtocolTransformScopedSpecNames(scopedSpecsKey) 1665 1666 name, spec := specs.Select(transforms.SCOPE_ANY, scopedSpecNames) 1667 1668 if spec == nil { 1669 return &transforms.ObfuscatorSeedTransformerParameters{}, nil 1670 } else { 1671 return &transforms.ObfuscatorSeedTransformerParameters{ 1672 TransformName: name, 1673 TransformSpec: spec, 1674 TransformSeed: seed, 1675 }, nil 1676 } 1677 } 1678 1679 func makeOSSHPrefixSpecParameters( 1680 p parameters.ParametersAccessor, 1681 dialPortNumber string) (*obfuscator.OSSHPrefixSpec, error) { 1682 1683 if !p.WeightedCoinFlip(parameters.OSSHPrefixProbability) { 1684 return &obfuscator.OSSHPrefixSpec{}, nil 1685 } 1686 1687 specs := p.ProtocolTransformSpecs(parameters.OSSHPrefixSpecs) 1688 scopedSpecNames := p.ProtocolTransformScopedSpecNames(parameters.OSSHPrefixScopedSpecNames) 1689 1690 name, spec := specs.Select(dialPortNumber, scopedSpecNames) 1691 1692 if spec == nil { 1693 return &obfuscator.OSSHPrefixSpec{}, nil 1694 } else { 1695 seed, err := prng.NewSeed() 1696 if err != nil { 1697 return nil, errors.Trace(err) 1698 } 1699 return &obfuscator.OSSHPrefixSpec{ 1700 Name: name, 1701 Spec: spec, 1702 Seed: seed, 1703 }, nil 1704 } 1705 } 1706 1707 func makeOSSHPrefixSplitConfig(p parameters.ParametersAccessor) (*obfuscator.OSSHPrefixSplitConfig, error) { 1708 1709 minDelay := p.Duration(parameters.OSSHPrefixSplitMinDelay) 1710 maxDelay := p.Duration(parameters.OSSHPrefixSplitMaxDelay) 1711 1712 seed, err := prng.NewSeed() 1713 if err != nil { 1714 return nil, errors.Trace(err) 1715 } 1716 1717 return &obfuscator.OSSHPrefixSplitConfig{ 1718 Seed: seed, 1719 MinDelay: minDelay, 1720 MaxDelay: maxDelay, 1721 }, nil 1722 }