github.com/Psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/protocol/serverEntry.go (about) 1 /* 2 * Copyright (c) 2015, 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 protocol 21 22 import ( 23 "bufio" 24 "bytes" 25 "crypto/ed25519" 26 "crypto/hmac" 27 "crypto/rand" 28 "crypto/sha256" 29 "encoding/base64" 30 "encoding/hex" 31 "encoding/json" 32 "fmt" 33 "io" 34 "net" 35 "strings" 36 "time" 37 38 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common" 39 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors" 40 ) 41 42 // ServerEntry represents a Psiphon server. It contains information 43 // about how to establish a tunnel connection to the server through 44 // several protocols. Server entries are JSON records downloaded from 45 // various sources. 46 type ServerEntry struct { 47 Tag string `json:"tag"` 48 IpAddress string `json:"ipAddress"` 49 WebServerPort string `json:"webServerPort"` // not an int 50 WebServerSecret string `json:"webServerSecret"` 51 WebServerCertificate string `json:"webServerCertificate"` 52 SshPort int `json:"sshPort"` 53 SshUsername string `json:"sshUsername"` 54 SshPassword string `json:"sshPassword"` 55 SshHostKey string `json:"sshHostKey"` 56 SshObfuscatedPort int `json:"sshObfuscatedPort"` 57 SshObfuscatedQUICPort int `json:"sshObfuscatedQUICPort"` 58 LimitQUICVersions []string `json:"limitQUICVersions"` 59 SshObfuscatedTapDancePort int `json:"sshObfuscatedTapdancePort"` 60 SshObfuscatedConjurePort int `json:"sshObfuscatedConjurePort"` 61 SshObfuscatedKey string `json:"sshObfuscatedKey"` 62 Capabilities []string `json:"capabilities"` 63 Region string `json:"region"` 64 FrontingProviderID string `json:"frontingProviderID"` 65 MeekServerPort int `json:"meekServerPort"` 66 MeekCookieEncryptionPublicKey string `json:"meekCookieEncryptionPublicKey"` 67 MeekObfuscatedKey string `json:"meekObfuscatedKey"` 68 MeekFrontingHost string `json:"meekFrontingHost"` 69 MeekFrontingHosts []string `json:"meekFrontingHosts"` 70 MeekFrontingDomain string `json:"meekFrontingDomain"` 71 MeekFrontingAddresses []string `json:"meekFrontingAddresses"` 72 MeekFrontingAddressesRegex string `json:"meekFrontingAddressesRegex"` 73 MeekFrontingDisableSNI bool `json:"meekFrontingDisableSNI"` 74 TacticsRequestPublicKey string `json:"tacticsRequestPublicKey"` 75 TacticsRequestObfuscatedKey string `json:"tacticsRequestObfuscatedKey"` 76 ConfigurationVersion int `json:"configurationVersion"` 77 Signature string `json:"signature"` 78 79 // These local fields are not expected to be present in downloaded server 80 // entries. They are added by the client to record and report stats about 81 // how and when server entries are obtained. 82 // All local fields should be included the list of fields in RemoveUnsignedFields. 83 LocalSource string `json:"localSource,omitempty"` 84 LocalTimestamp string `json:"localTimestamp,omitempty"` 85 IsLocalDerivedTag bool `json:"isLocalDerivedTag,omitempty"` 86 } 87 88 // ServerEntryFields is an alternate representation of ServerEntry which 89 // enables future compatibility when unmarshaling and persisting new server 90 // entries which may contain new, unrecognized fields not in the ServerEntry 91 // type for a particular client version. 92 // 93 // When new JSON server entries with new fields are unmarshaled to ServerEntry 94 // types, unrecognized fields are discarded. When unmarshaled to 95 // ServerEntryFields, unrecognized fields are retained and may be persisted 96 // and available when the client is upgraded and unmarshals to an updated 97 // ServerEntry type. 98 type ServerEntryFields map[string]interface{} 99 100 // GetServerEntry converts a ServerEntryFields into a ServerEntry. 101 func (fields ServerEntryFields) GetServerEntry() (*ServerEntry, error) { 102 103 marshaledServerEntry, err := json.Marshal(fields) 104 if err != nil { 105 return nil, errors.Trace(err) 106 } 107 108 var serverEntry *ServerEntry 109 err = json.Unmarshal(marshaledServerEntry, &serverEntry) 110 if err != nil { 111 return nil, errors.Trace(err) 112 } 113 114 return serverEntry, nil 115 } 116 117 func (fields ServerEntryFields) GetTag() string { 118 tag, ok := fields["tag"] 119 if !ok { 120 return "" 121 } 122 tagStr, ok := tag.(string) 123 if !ok { 124 return "" 125 } 126 return tagStr 127 } 128 129 // SetTag sets a local, derived server entry tag. A tag is an identifier used 130 // in server entry pruning and potentially other use cases. An explict tag, 131 // set by the Psiphon Network, may be present in a server entry that is 132 // imported; otherwise, the client will set a derived tag. The tag should be 133 // generated using GenerateServerEntryTag. When SetTag finds a explicit tag, 134 // the new, derived tag is ignored. The isLocalTag local field is set to 135 // distinguish explict and derived tags and is used in signature verification 136 // to determine if the tag field is part of the signature. 137 func (fields ServerEntryFields) SetTag(tag string) { 138 139 // Don't replace explicit tag 140 if tag, ok := fields["tag"]; ok { 141 tagStr, ok := tag.(string) 142 if ok && tagStr != "" { 143 isLocalDerivedTag, ok := fields["isLocalDerivedTag"] 144 if !ok { 145 return 146 } 147 isLocalDerivedTagBool, ok := isLocalDerivedTag.(bool) 148 if ok && !isLocalDerivedTagBool { 149 return 150 } 151 } 152 } 153 154 fields["tag"] = tag 155 156 // Mark this tag as local 157 fields["isLocalDerivedTag"] = true 158 } 159 160 func (fields ServerEntryFields) GetDiagnosticID() string { 161 tag, ok := fields["tag"] 162 if !ok { 163 return "" 164 } 165 tagStr, ok := tag.(string) 166 if !ok { 167 return "" 168 } 169 return TagToDiagnosticID(tagStr) 170 } 171 172 func (fields ServerEntryFields) GetIPAddress() string { 173 ipAddress, ok := fields["ipAddress"] 174 if !ok { 175 return "" 176 } 177 ipAddressStr, ok := ipAddress.(string) 178 if !ok { 179 return "" 180 } 181 return ipAddressStr 182 } 183 184 func (fields ServerEntryFields) GetWebServerPort() string { 185 webServerPort, ok := fields["webServerPort"] 186 if !ok { 187 return "" 188 } 189 webServerPortStr, ok := webServerPort.(string) 190 if !ok { 191 return "" 192 } 193 return webServerPortStr 194 } 195 196 func (fields ServerEntryFields) GetWebServerSecret() string { 197 webServerSecret, ok := fields["webServerSecret"] 198 if !ok { 199 return "" 200 } 201 webServerSecretStr, ok := webServerSecret.(string) 202 if !ok { 203 return "" 204 } 205 return webServerSecretStr 206 } 207 208 func (fields ServerEntryFields) GetWebServerCertificate() string { 209 webServerCertificate, ok := fields["webServerCertificate"] 210 if !ok { 211 return "" 212 } 213 webServerCertificateStr, ok := webServerCertificate.(string) 214 if !ok { 215 return "" 216 } 217 return webServerCertificateStr 218 } 219 220 func (fields ServerEntryFields) GetConfigurationVersion() int { 221 configurationVersion, ok := fields["configurationVersion"] 222 if !ok { 223 return 0 224 } 225 configurationVersionFloat, ok := configurationVersion.(float64) 226 if !ok { 227 return 0 228 } 229 return int(configurationVersionFloat) 230 } 231 232 func (fields ServerEntryFields) GetLocalSource() string { 233 localSource, ok := fields["localSource"] 234 if !ok { 235 return "" 236 } 237 localSourceStr, ok := localSource.(string) 238 if !ok { 239 return "" 240 } 241 return localSourceStr 242 } 243 244 func (fields ServerEntryFields) SetLocalSource(source string) { 245 fields["localSource"] = source 246 } 247 248 func (fields ServerEntryFields) GetLocalTimestamp() string { 249 localTimestamp, ok := fields["localTimestamp"] 250 if !ok { 251 return "" 252 } 253 localTimestampStr, ok := localTimestamp.(string) 254 if !ok { 255 return "" 256 } 257 return localTimestampStr 258 } 259 260 func (fields ServerEntryFields) SetLocalTimestamp(timestamp string) { 261 fields["localTimestamp"] = timestamp 262 } 263 264 func (fields ServerEntryFields) HasSignature() bool { 265 signature, ok := fields["signature"] 266 if !ok { 267 return false 268 } 269 signatureStr, ok := signature.(string) 270 if !ok { 271 return false 272 } 273 return signatureStr != "" 274 } 275 276 const signaturePublicKeyDigestSize = 8 277 278 // AddSignature signs a server entry and attaches a new field containing the 279 // signature. Any existing "signature" field will be replaced. 280 // 281 // The signature incudes a public key ID that is derived from a digest of the 282 // public key value. This ID is intended for future use when multiple signing 283 // keys may be deployed. 284 func (fields ServerEntryFields) AddSignature(publicKey, privateKey string) error { 285 286 // Make a copy so that removing unsigned fields will have no side effects 287 copyFields := make(ServerEntryFields) 288 for k, v := range fields { 289 copyFields[k] = v 290 } 291 292 copyFields.RemoveUnsignedFields() 293 294 delete(copyFields, "signature") 295 296 // Best practise would be to sign the JSON encoded server entry bytes and 297 // append the signature to those bytes. However, due to backwards 298 // compatibility requirements, we must retain the outer server entry encoding 299 // as-is and insert the signature. 300 // 301 // Limitation: since the verifyier must remarshal its server entry before 302 // verifying, the JSON produced there must be a byte-for-byte match to the 303 // JSON signed here. The precise output of the JSON encoder that is used, 304 // "encoding/json", with default formatting, as of Go 1.11.5, is therefore 305 // part of the signature protocol. 306 // 307 // TODO: use a standard, canonical encoding, such as JCS: 308 // https://tools.ietf.org/id/draft-rundgren-json-canonicalization-scheme-05.html 309 310 marshaledFields, err := json.Marshal(copyFields) 311 if err != nil { 312 return errors.Trace(err) 313 } 314 315 decodedPublicKey, err := base64.StdEncoding.DecodeString(publicKey) 316 if err != nil { 317 return errors.Trace(err) 318 } 319 320 publicKeyDigest := sha256.Sum256(decodedPublicKey) 321 publicKeyID := publicKeyDigest[:signaturePublicKeyDigestSize] 322 323 decodedPrivateKey, err := base64.StdEncoding.DecodeString(privateKey) 324 if err != nil { 325 return errors.Trace(err) 326 } 327 328 signature := ed25519.Sign(decodedPrivateKey, marshaledFields) 329 330 fields["signature"] = base64.StdEncoding.EncodeToString( 331 append(publicKeyID, signature...)) 332 333 return nil 334 } 335 336 // VerifySignature verifies the signature set by AddSignature. 337 // 338 // VerifySignature must be called before using any server entry that is 339 // imported from an untrusted source, such as client-to-client exchange. 340 func (fields ServerEntryFields) VerifySignature(publicKey string) error { 341 342 if publicKey == "" { 343 return errors.TraceNew("missing public key") 344 } 345 346 // Make a copy so that removing unsigned fields will have no side effects 347 copyFields := make(ServerEntryFields) 348 for k, v := range fields { 349 copyFields[k] = v 350 } 351 352 signatureField, ok := copyFields["signature"] 353 if !ok { 354 return errors.TraceNew("missing signature field") 355 } 356 357 signatureFieldStr, ok := signatureField.(string) 358 if !ok { 359 return errors.TraceNew("invalid signature field") 360 } 361 362 decodedSignatureField, err := base64.StdEncoding.DecodeString(signatureFieldStr) 363 if err != nil { 364 return errors.Trace(err) 365 } 366 367 if len(decodedSignatureField) < signaturePublicKeyDigestSize { 368 return errors.TraceNew("invalid signature field length") 369 } 370 371 publicKeyID := decodedSignatureField[:signaturePublicKeyDigestSize] 372 signature := decodedSignatureField[signaturePublicKeyDigestSize:] 373 374 if len(signature) != ed25519.SignatureSize { 375 return errors.TraceNew("invalid signature length") 376 } 377 378 decodedPublicKey, err := base64.StdEncoding.DecodeString(publicKey) 379 if err != nil { 380 return errors.Trace(err) 381 } 382 383 publicKeyDigest := sha256.Sum256(decodedPublicKey) 384 expectedPublicKeyID := publicKeyDigest[:signaturePublicKeyDigestSize] 385 386 if !bytes.Equal(expectedPublicKeyID, publicKeyID) { 387 return errors.TraceNew("unexpected public key ID") 388 } 389 390 copyFields.RemoveUnsignedFields() 391 392 delete(copyFields, "signature") 393 394 marshaledFields, err := json.Marshal(copyFields) 395 if err != nil { 396 return errors.Trace(err) 397 } 398 399 if !ed25519.Verify(decodedPublicKey, marshaledFields, signature) { 400 return errors.TraceNew("invalid signature") 401 } 402 403 return nil 404 } 405 406 // RemoveUnsignedFields prepares a server entry for signing or signature 407 // verification by removing unsigned fields. The JSON marshalling of the 408 // remaining fields is the data that is signed. 409 func (fields ServerEntryFields) RemoveUnsignedFields() { 410 delete(fields, "localSource") 411 delete(fields, "localTimestamp") 412 413 // Only non-local, explicit tags are part of the signature 414 isLocalDerivedTag := fields["isLocalDerivedTag"] 415 isLocalDerivedTagBool, ok := isLocalDerivedTag.(bool) 416 if ok && isLocalDerivedTagBool { 417 delete(fields, "tag") 418 } 419 delete(fields, "isLocalDerivedTag") 420 } 421 422 // NewServerEntrySignatureKeyPair creates an ed25519 key pair for use in 423 // server entry signing and verification. 424 func NewServerEntrySignatureKeyPair() (string, string, error) { 425 426 publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader) 427 if err != nil { 428 return "", "", errors.Trace(err) 429 } 430 431 return base64.StdEncoding.EncodeToString(publicKey), 432 base64.StdEncoding.EncodeToString(privateKey), 433 nil 434 } 435 436 // GetCapability returns the server capability corresponding 437 // to the tunnel protocol. 438 func GetCapability(protocol string) string { 439 return strings.TrimSuffix(protocol, "-OSSH") 440 } 441 442 // GetTacticsCapability returns the server tactics capability 443 // corresponding to the tunnel protocol. 444 func GetTacticsCapability(protocol string) string { 445 return GetCapability(protocol) + "-TACTICS" 446 } 447 448 // hasCapability indicates if the server entry has the specified capability. 449 // 450 // Any internal "PASSTHROUGH-v2 or "PASSTHROUGH" componant in the server 451 // entry's capabilities is ignored. These PASSTHROUGH components are used to 452 // mask protocols which are running the passthrough mechanisms from older 453 // clients which do not implement the passthrough messages. Older clients 454 // will treat these capabilities as unknown protocols and skip them. 455 // 456 // Any "QUICv1" capability is treated as "QUIC". "QUICv1" is used to mask the 457 // QUIC-OSSH capability from older clients to ensure that older clients do 458 // not send gQUIC packets to second generation QUICv1-only QUIC-OSSH servers. 459 // New clients must check SupportsOnlyQUICv1 before selecting a QUIC version; 460 // for "QUICv1", this ensures that new clients also do not select gQUIC to 461 // QUICv1-only servers. 462 func (serverEntry *ServerEntry) hasCapability(requiredCapability string) bool { 463 for _, capability := range serverEntry.Capabilities { 464 465 capability = strings.ReplaceAll(capability, "-PASSTHROUGH-v2", "") 466 capability = strings.ReplaceAll(capability, "-PASSTHROUGH", "") 467 468 quicCapability := GetCapability(TUNNEL_PROTOCOL_QUIC_OBFUSCATED_SSH) 469 if capability == quicCapability+"v1" { 470 capability = quicCapability 471 } 472 473 if capability == requiredCapability { 474 return true 475 } 476 } 477 return false 478 } 479 480 // SupportsProtocol returns true if and only if the ServerEntry has 481 // the necessary capability to support the specified tunnel protocol. 482 func (serverEntry *ServerEntry) SupportsProtocol(protocol string) bool { 483 requiredCapability := GetCapability(protocol) 484 return serverEntry.hasCapability(requiredCapability) 485 } 486 487 // ProtocolUsesLegacyPassthrough indicates whether the ServerEntry supports 488 // the specified protocol using legacy passthrough messages. 489 // 490 // There is no corresponding check for v2 passthrough, as clients send v2 491 // passthrough messages unconditionally, by default, for passthrough 492 // protocols. 493 func (serverEntry *ServerEntry) ProtocolUsesLegacyPassthrough(protocol string) bool { 494 legacyCapability := GetCapability(protocol) + "-PASSTHROUGH" 495 for _, capability := range serverEntry.Capabilities { 496 if capability == legacyCapability { 497 return true 498 } 499 } 500 return false 501 } 502 503 // SupportsOnlyQUICv1 indicates that the QUIC-OSSH server supports only QUICv1 504 // and gQUIC versions should not be selected, as they will fail to connect 505 // while sending atypical traffic to the server. 506 func (serverEntry *ServerEntry) SupportsOnlyQUICv1() bool { 507 quicCapability := GetCapability(TUNNEL_PROTOCOL_QUIC_OBFUSCATED_SSH) 508 return common.Contains(serverEntry.Capabilities, quicCapability+"v1") && 509 !common.Contains(serverEntry.Capabilities, quicCapability) 510 } 511 512 // ConditionallyEnabledComponents defines an interface which can be queried to 513 // determine which conditionally compiled protocol components are present. 514 type ConditionallyEnabledComponents interface { 515 QUICEnabled() bool 516 RefractionNetworkingEnabled() bool 517 } 518 519 // TunnelProtocolPortLists is a map from tunnel protocol names (or "All") to a 520 // list of port number ranges. 521 type TunnelProtocolPortLists map[string]*common.PortList 522 523 // GetSupportedProtocols returns a list of tunnel protocols supported by the 524 // ServerEntry's capabilities and allowed by various constraints. 525 func (serverEntry *ServerEntry) GetSupportedProtocols( 526 conditionallyEnabled ConditionallyEnabledComponents, 527 useUpstreamProxy bool, 528 limitTunnelProtocols TunnelProtocols, 529 limitTunnelDialPortNumbers TunnelProtocolPortLists, 530 limitQUICVersions QUICVersions, 531 excludeIntensive bool) TunnelProtocols { 532 533 supportedProtocols := make(TunnelProtocols, 0) 534 535 for _, tunnelProtocol := range SupportedTunnelProtocols { 536 537 if useUpstreamProxy && !TunnelProtocolSupportsUpstreamProxy(tunnelProtocol) { 538 continue 539 } 540 541 if len(limitTunnelProtocols) > 0 { 542 if !common.Contains(limitTunnelProtocols, tunnelProtocol) { 543 continue 544 } 545 } else { 546 if common.Contains(DefaultDisabledTunnelProtocols, tunnelProtocol) { 547 continue 548 } 549 } 550 551 if excludeIntensive && TunnelProtocolIsResourceIntensive(tunnelProtocol) { 552 continue 553 } 554 555 if (TunnelProtocolUsesQUIC(tunnelProtocol) && !conditionallyEnabled.QUICEnabled()) || 556 (TunnelProtocolUsesRefractionNetworking(tunnelProtocol) && 557 !conditionallyEnabled.RefractionNetworkingEnabled()) { 558 continue 559 } 560 561 if !serverEntry.SupportsProtocol(tunnelProtocol) { 562 continue 563 } 564 565 // If the server is limiting QUIC versions, at least one must be 566 // supported. And if tactics is also limiting QUIC versions, there 567 // must be a common version in both limit lists for this server entry 568 // to support QUIC-OSSH. 569 // 570 // Limitation: to avoid additional complexity, we do not consider 571 // DisableFrontingProviderQUICVersion here, as fronting providers are 572 // expected to support QUICv1 and gQUIC is expected to become 573 // obsolete in general. 574 575 if TunnelProtocolUsesQUIC(tunnelProtocol) && len(serverEntry.LimitQUICVersions) > 0 { 576 if !common.ContainsAny(serverEntry.LimitQUICVersions, SupportedQUICVersions) { 577 continue 578 } 579 if len(limitQUICVersions) > 0 && 580 !common.ContainsAny(serverEntry.LimitQUICVersions, limitQUICVersions) { 581 continue 582 } 583 } 584 585 dialPortNumber, err := serverEntry.GetDialPortNumber(tunnelProtocol) 586 if err != nil { 587 continue 588 } 589 590 if len(limitTunnelDialPortNumbers) > 0 { 591 if portList, ok := limitTunnelDialPortNumbers[tunnelProtocol]; ok { 592 if !portList.Lookup(dialPortNumber) { 593 continue 594 } 595 } else if portList, ok := limitTunnelDialPortNumbers[TUNNEL_PROTOCOLS_ALL]; ok { 596 if !portList.Lookup(dialPortNumber) { 597 continue 598 } 599 } 600 } 601 602 supportedProtocols = append(supportedProtocols, tunnelProtocol) 603 604 } 605 return supportedProtocols 606 } 607 608 func (serverEntry *ServerEntry) GetDialPortNumber(tunnelProtocol string) (int, error) { 609 610 if !serverEntry.SupportsProtocol(tunnelProtocol) { 611 return 0, errors.TraceNew("protocol not supported") 612 } 613 614 switch tunnelProtocol { 615 616 case TUNNEL_PROTOCOL_SSH: 617 return serverEntry.SshPort, nil 618 619 case TUNNEL_PROTOCOL_OBFUSCATED_SSH: 620 return serverEntry.SshObfuscatedPort, nil 621 622 case TUNNEL_PROTOCOL_TAPDANCE_OBFUSCATED_SSH: 623 return serverEntry.SshObfuscatedTapDancePort, nil 624 625 case TUNNEL_PROTOCOL_CONJURE_OBFUSCATED_SSH: 626 return serverEntry.SshObfuscatedConjurePort, nil 627 628 case TUNNEL_PROTOCOL_QUIC_OBFUSCATED_SSH: 629 return serverEntry.SshObfuscatedQUICPort, nil 630 631 case TUNNEL_PROTOCOL_FRONTED_MEEK, 632 TUNNEL_PROTOCOL_FRONTED_MEEK_QUIC_OBFUSCATED_SSH: 633 return 443, nil 634 635 case TUNNEL_PROTOCOL_FRONTED_MEEK_HTTP: 636 return 80, nil 637 638 case TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS, 639 TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET, 640 TUNNEL_PROTOCOL_UNFRONTED_MEEK: 641 return serverEntry.MeekServerPort, nil 642 } 643 644 return 0, errors.TraceNew("unknown protocol") 645 } 646 647 // GetSupportedTacticsProtocols returns a list of tunnel protocols, 648 // supported by the ServerEntry's capabilities, that may be used 649 // for tactics requests. 650 func (serverEntry *ServerEntry) GetSupportedTacticsProtocols() []string { 651 652 supportedProtocols := make([]string, 0) 653 654 for _, protocol := range SupportedTunnelProtocols { 655 656 if !TunnelProtocolUsesMeek(protocol) { 657 continue 658 } 659 660 requiredCapability := GetTacticsCapability(protocol) 661 if !serverEntry.hasCapability(requiredCapability) { 662 continue 663 } 664 665 supportedProtocols = append(supportedProtocols, protocol) 666 } 667 668 return supportedProtocols 669 } 670 671 // SupportsSSHAPIRequests returns true when the server supports 672 // SSH API requests. 673 func (serverEntry *ServerEntry) SupportsSSHAPIRequests() bool { 674 return serverEntry.hasCapability(CAPABILITY_SSH_API_REQUESTS) 675 } 676 677 func (serverEntry *ServerEntry) GetUntunneledWebRequestPorts() []string { 678 ports := make([]string, 0) 679 if serverEntry.hasCapability(CAPABILITY_UNTUNNELED_WEB_API_REQUESTS) { 680 // Server-side configuration quirk: there's a port forward from 681 // port 443 to the web server, which we can try, except on servers 682 // running FRONTED_MEEK, which listens on port 443. 683 if !serverEntry.SupportsProtocol(TUNNEL_PROTOCOL_FRONTED_MEEK) { 684 ports = append(ports, "443") 685 } 686 ports = append(ports, serverEntry.WebServerPort) 687 } 688 return ports 689 } 690 691 func (serverEntry *ServerEntry) HasSignature() bool { 692 return serverEntry.Signature != "" 693 } 694 695 func (serverEntry *ServerEntry) GetDiagnosticID() string { 696 return TagToDiagnosticID(serverEntry.Tag) 697 } 698 699 // GenerateServerEntryTag creates a server entry tag value that is 700 // cryptographically derived from the IP address and web server secret in a 701 // way that is difficult to reverse the IP address value from the tag or 702 // compute the tag without having the web server secret, a 256-bit random 703 // value which is unique per server, in addition to the IP address. A database 704 // consisting only of server entry tags should be resistent to an attack that 705 // attempts to reverse all the server IPs, even given a small IP space (IPv4), 706 // or some subset of the web server secrets. 707 func GenerateServerEntryTag(ipAddress, webServerSecret string) string { 708 h := hmac.New(sha256.New, []byte(webServerSecret)) 709 h.Write([]byte(ipAddress)) 710 return base64.StdEncoding.EncodeToString(h.Sum(nil)) 711 } 712 713 // TagToDiagnosticID returns a prefix of the server entry tag that should be 714 // sufficient to uniquely identify servers in diagnostics, while also being 715 // more human readable than emitting the full tag. The tag is used as the base 716 // of the diagnostic ID as it doesn't leak the server IP address in diagnostic 717 // output. 718 func TagToDiagnosticID(tag string) string { 719 if len(tag) < 8 { 720 return "<unknown>" 721 } 722 return tag[:8] 723 } 724 725 // EncodeServerEntry returns a string containing the encoding of 726 // a ServerEntry following Psiphon conventions. 727 func EncodeServerEntry(serverEntry *ServerEntry) (string, error) { 728 encodedServerEntry, err := encodeServerEntry( 729 serverEntry.IpAddress, 730 serverEntry.WebServerPort, 731 serverEntry.WebServerSecret, 732 serverEntry.WebServerCertificate, 733 serverEntry) 734 if err != nil { 735 return "", errors.Trace(err) 736 } 737 return encodedServerEntry, nil 738 } 739 740 // EncodeServerEntryFields returns a string containing the encoding of 741 // ServerEntryFields following Psiphon conventions. 742 func EncodeServerEntryFields(serverEntryFields ServerEntryFields) (string, error) { 743 encodedServerEntry, err := encodeServerEntry( 744 serverEntryFields.GetIPAddress(), 745 serverEntryFields.GetWebServerPort(), 746 serverEntryFields.GetWebServerSecret(), 747 serverEntryFields.GetWebServerCertificate(), 748 serverEntryFields) 749 if err != nil { 750 return "", errors.Trace(err) 751 } 752 return encodedServerEntry, nil 753 } 754 755 func encodeServerEntry( 756 prefixIPAddress string, 757 prefixWebServerPort string, 758 prefixWebServerSecret string, 759 prefixWebServerCertificate string, 760 serverEntry interface{}) (string, error) { 761 762 serverEntryJSON, err := json.Marshal(serverEntry) 763 if err != nil { 764 return "", errors.Trace(err) 765 } 766 767 // Legacy clients use a space-delimited fields prefix, and all clients expect 768 // to at least parse the prefix in order to skip over it. 769 // 770 // When the server entry has no web API server certificate, the entire prefix 771 // can be compacted down to single character placeholders. Clients that can 772 // use the ssh API always prefer it over the web API and won't use the prefix 773 // values. 774 if len(prefixWebServerCertificate) == 0 { 775 prefixIPAddress = "0" 776 prefixWebServerPort = "0" 777 prefixWebServerSecret = "0" 778 prefixWebServerCertificate = "0" 779 } 780 781 return hex.EncodeToString([]byte(fmt.Sprintf( 782 "%s %s %s %s %s", 783 prefixIPAddress, 784 prefixWebServerPort, 785 prefixWebServerSecret, 786 prefixWebServerCertificate, 787 serverEntryJSON))), nil 788 } 789 790 // DecodeServerEntry extracts a server entry from the encoding 791 // used by remote server lists and Psiphon server handshake requests. 792 // 793 // The resulting ServerEntry.LocalSource is populated with serverEntrySource, 794 // which should be one of SERVER_ENTRY_SOURCE_EMBEDDED, SERVER_ENTRY_SOURCE_REMOTE, 795 // SERVER_ENTRY_SOURCE_DISCOVERY, SERVER_ENTRY_SOURCE_TARGET, 796 // SERVER_ENTRY_SOURCE_OBFUSCATED. 797 // ServerEntry.LocalTimestamp is populated with the provided timestamp, which 798 // should be a RFC 3339 formatted string. These local fields are stored with the 799 // server entry and reported to the server as stats (a coarse granularity timestamp 800 // is reported). 801 func DecodeServerEntry( 802 encodedServerEntry, timestamp, serverEntrySource string) (*ServerEntry, error) { 803 804 serverEntry := new(ServerEntry) 805 err := decodeServerEntry(encodedServerEntry, timestamp, serverEntrySource, serverEntry) 806 if err != nil { 807 return nil, errors.Trace(err) 808 } 809 810 // NOTE: if the source JSON happens to have values in these fields, they get clobbered. 811 serverEntry.LocalSource = serverEntrySource 812 serverEntry.LocalTimestamp = timestamp 813 814 return serverEntry, nil 815 } 816 817 // DecodeServerEntryFields extracts an encoded server entry into a 818 // ServerEntryFields type, much like DecodeServerEntry. Unrecognized fields 819 // not in ServerEntry are retained in the ServerEntryFields. 820 // 821 // LocalSource/LocalTimestamp map entries are set only when the corresponding 822 // inputs are non-blank. 823 func DecodeServerEntryFields( 824 encodedServerEntry, timestamp, serverEntrySource string) (ServerEntryFields, error) { 825 826 serverEntryFields := make(ServerEntryFields) 827 err := decodeServerEntry(encodedServerEntry, timestamp, serverEntrySource, &serverEntryFields) 828 if err != nil { 829 return nil, errors.Trace(err) 830 } 831 832 // NOTE: if the source JSON happens to have values in these fields, they get clobbered. 833 if serverEntrySource != "" { 834 serverEntryFields.SetLocalSource(serverEntrySource) 835 } 836 if timestamp != "" { 837 serverEntryFields.SetLocalTimestamp(timestamp) 838 } 839 840 return serverEntryFields, nil 841 } 842 843 func decodeServerEntry( 844 encodedServerEntry, timestamp, serverEntrySource string, 845 target interface{}) error { 846 847 hexDecodedServerEntry, err := hex.DecodeString(encodedServerEntry) 848 if err != nil { 849 return errors.Trace(err) 850 } 851 852 // Skip past legacy format (4 space delimited fields) and just parse the JSON config 853 fields := bytes.SplitN(hexDecodedServerEntry, []byte(" "), 5) 854 if len(fields) != 5 { 855 return errors.TraceNew("invalid encoded server entry") 856 } 857 858 err = json.Unmarshal(fields[4], target) 859 if err != nil { 860 return errors.Trace(err) 861 } 862 863 return nil 864 } 865 866 // ValidateServerEntryFields checks for malformed server entries. 867 func ValidateServerEntryFields(serverEntryFields ServerEntryFields) error { 868 869 // Checks for a valid ipAddress. This is important since the IP 870 // address is the key used to store/lookup the server entry. 871 872 ipAddress := serverEntryFields.GetIPAddress() 873 if net.ParseIP(ipAddress) == nil { 874 return errors.Tracef("server entry has invalid ipAddress: %s", ipAddress) 875 } 876 877 // TODO: validate more fields? 878 879 // Ensure locally initialized fields have been set. 880 881 source := serverEntryFields.GetLocalSource() 882 if !common.Contains( 883 SupportedServerEntrySources, source) { 884 return errors.Tracef("server entry has invalid source: %s", source) 885 } 886 887 timestamp := serverEntryFields.GetLocalTimestamp() 888 _, err := time.Parse(time.RFC3339, timestamp) 889 if err != nil { 890 return errors.Tracef("server entry has invalid timestamp: %s", err) 891 } 892 893 return nil 894 } 895 896 // DecodeServerEntryList extracts server entries from the list encoding 897 // used by remote server lists and Psiphon server handshake requests. 898 // Each server entry is validated and invalid entries are skipped. 899 // See DecodeServerEntry for note on serverEntrySource/timestamp. 900 func DecodeServerEntryList( 901 encodedServerEntryList, timestamp, 902 serverEntrySource string) ([]ServerEntryFields, error) { 903 904 serverEntries := make([]ServerEntryFields, 0) 905 for _, encodedServerEntry := range strings.Split(encodedServerEntryList, "\n") { 906 if len(encodedServerEntry) == 0 { 907 continue 908 } 909 910 // TODO: skip this entry and continue if can't decode? 911 serverEntryFields, err := DecodeServerEntryFields(encodedServerEntry, timestamp, serverEntrySource) 912 if err != nil { 913 return nil, errors.Trace(err) 914 } 915 916 if ValidateServerEntryFields(serverEntryFields) != nil { 917 // Skip this entry and continue with the next one 918 // TODO: invoke a logging callback 919 continue 920 } 921 922 serverEntries = append(serverEntries, serverEntryFields) 923 } 924 return serverEntries, nil 925 } 926 927 // StreamingServerEntryDecoder performs the DecodeServerEntryList 928 // operation, loading only one server entry into memory at a time. 929 type StreamingServerEntryDecoder struct { 930 scanner *bufio.Scanner 931 timestamp string 932 serverEntrySource string 933 } 934 935 // NewStreamingServerEntryDecoder creates a new StreamingServerEntryDecoder. 936 func NewStreamingServerEntryDecoder( 937 encodedServerEntryListReader io.Reader, 938 timestamp, serverEntrySource string) *StreamingServerEntryDecoder { 939 940 return &StreamingServerEntryDecoder{ 941 scanner: bufio.NewScanner(encodedServerEntryListReader), 942 timestamp: timestamp, 943 serverEntrySource: serverEntrySource, 944 } 945 } 946 947 // Next reads and decodes, and validates the next server entry from the 948 // input stream, returning a nil server entry when the stream is complete. 949 // 950 // Limitations: 951 // - Each encoded server entry line cannot exceed bufio.MaxScanTokenSize, 952 // the default buffer size which this decoder uses. This is 64K. 953 // - DecodeServerEntry is called on each encoded server entry line, which 954 // will allocate memory to hex decode and JSON deserialze the server 955 // entry. As this is not presently reusing a fixed buffer, each call 956 // will allocate additional memory; garbage collection is necessary to 957 // reclaim that memory for reuse for the next server entry. 958 func (decoder *StreamingServerEntryDecoder) Next() (ServerEntryFields, error) { 959 960 for { 961 if !decoder.scanner.Scan() { 962 return nil, errors.Trace(decoder.scanner.Err()) 963 } 964 965 // TODO: use scanner.Bytes which doesn't allocate, instead of scanner.Text 966 967 // TODO: skip this entry and continue if can't decode? 968 serverEntryFields, err := DecodeServerEntryFields( 969 decoder.scanner.Text(), decoder.timestamp, decoder.serverEntrySource) 970 if err != nil { 971 return nil, errors.Trace(err) 972 } 973 974 if ValidateServerEntryFields(serverEntryFields) != nil { 975 // Skip this entry and continue with the next one 976 // TODO: invoke a logging callback 977 continue 978 } 979 980 return serverEntryFields, nil 981 } 982 }