github.com/decred/dcrlnd@v0.7.6/tor/controller.go (about) 1 package tor 2 3 import ( 4 "bytes" 5 "crypto/hmac" 6 "crypto/rand" 7 "crypto/sha256" 8 "encoding/hex" 9 "errors" 10 "fmt" 11 "io/ioutil" 12 "net/textproto" 13 "strconv" 14 "strings" 15 "sync/atomic" 16 ) 17 18 const ( 19 // success is the Tor Control response code representing a successful 20 // request. 21 success = 250 22 23 // invalidNumOfArguments is the Tor Control response code representing 24 // there being an invalid number of arguments. 25 invalidNumOfArguments = 512 26 27 // serviceIDNotRecognized is the Tor Control response code representing 28 // the specified ServiceID is not recognized. 29 serviceIDNotRecognized = 552 30 31 // nonceLen is the length of a nonce generated by either the controller 32 // or the Tor server 33 nonceLen = 32 34 35 // cookieLen is the length of the authentication cookie. 36 cookieLen = 32 37 38 // ProtocolInfoVersion is the `protocolinfo` version currently supported 39 // by the Tor server. 40 ProtocolInfoVersion = 1 41 42 // MinTorVersion is the minimum supported version that the Tor server 43 // must be running on. This is needed in order to create v3 onion 44 // services through Tor's control port. 45 MinTorVersion = "0.3.3.6" 46 47 // authSafeCookie is the name of the SAFECOOKIE authentication method. 48 authSafeCookie = "SAFECOOKIE" 49 50 // authHashedPassword is the name of the HASHEDPASSWORD authentication 51 // method. 52 authHashedPassword = "HASHEDPASSWORD" 53 54 // authNull is the name of the NULL authentication method. 55 authNull = "NULL" 56 ) 57 58 var ( 59 // serverKey is the key used when computing the HMAC-SHA256 of a message 60 // from the server. 61 serverKey = []byte("Tor safe cookie authentication " + 62 "server-to-controller hash") 63 64 // controllerKey is the key used when computing the HMAC-SHA256 of a 65 // message from the controller. 66 controllerKey = []byte("Tor safe cookie authentication " + 67 "controller-to-server hash") 68 69 // errCodeNotMatch is used when an expected response code is not 70 // returned. 71 errCodeNotMatch = errors.New("unexpected code") 72 73 // errTCNotStarted is used when we require the tor controller to be 74 // started while it's not. 75 errTCNotStarted = errors.New("tor controller must be started") 76 77 // errTCNotStarted is used when we require the tor controller to be 78 // not stopped while it is. 79 errTCStopped = errors.New("tor controller must not be stopped") 80 ) 81 82 // Controller is an implementation of the Tor Control protocol. This is used in 83 // order to communicate with a Tor server. Its only supported method of 84 // authentication is the SAFECOOKIE method. 85 // 86 // NOTE: The connection to the Tor server must be authenticated before 87 // proceeding to send commands. Otherwise, the connection will be closed. 88 // 89 // TODO: 90 // - if adding support for more commands, extend this with a command queue? 91 // - place under sub-package? 92 // - support async replies from the server 93 type Controller struct { 94 // started is used atomically in order to prevent multiple calls to 95 // Start. 96 started int32 97 98 // stopped is used atomically in order to prevent multiple calls to 99 // Stop. 100 stopped int32 101 102 // conn is the underlying connection between the controller and the 103 // Tor server. It provides read and write methods to simplify the 104 // text-based messages within the connection. 105 conn *textproto.Conn 106 107 // controlAddr is the host:port the Tor server is listening locally for 108 // controller connections on. 109 controlAddr string 110 111 // password, if non-empty, signals that the controller should attempt to 112 // authenticate itself with the backing Tor daemon through the 113 // HASHEDPASSWORD authentication method with this value. 114 password string 115 116 // version is the current version of the Tor server. 117 version string 118 119 // targetIPAddress is the IP address which we tell the Tor server to use 120 // to connect to the LND node. This is required when the Tor server 121 // runs on another host, otherwise the service will not be reachable. 122 targetIPAddress string 123 124 // activeServiceID is the Onion ServiceID created by ADD_ONION. 125 activeServiceID string 126 } 127 128 // NewController returns a new Tor controller that will be able to interact with 129 // a Tor server. 130 func NewController(controlAddr string, targetIPAddress string, 131 password string) *Controller { 132 133 return &Controller{ 134 controlAddr: controlAddr, 135 targetIPAddress: targetIPAddress, 136 password: password, 137 } 138 } 139 140 // Start establishes and authenticates the connection between the controller 141 // and a Tor server. Once done, the controller will be able to send commands 142 // and expect responses. 143 func (c *Controller) Start() error { 144 if !atomic.CompareAndSwapInt32(&c.started, 0, 1) { 145 return nil 146 } 147 148 log.Info("Starting tor controller") 149 150 conn, err := textproto.Dial("tcp", c.controlAddr) 151 if err != nil { 152 return fmt.Errorf("unable to connect to Tor server: %v", err) 153 } 154 155 c.conn = conn 156 157 return c.authenticate() 158 } 159 160 // Stop closes the connection between the controller and the Tor server. 161 func (c *Controller) Stop() error { 162 if !atomic.CompareAndSwapInt32(&c.stopped, 0, 1) { 163 return nil 164 } 165 166 log.Info("Stopping tor controller") 167 168 // Remove the onion service. 169 if err := c.DelOnion(c.activeServiceID); err != nil { 170 log.Errorf("DEL_ONION got error: %v", err) 171 return err 172 } 173 174 // Reset service ID. 175 c.activeServiceID = "" 176 177 return c.conn.Close() 178 } 179 180 // Reconnect makes a new socket connection between the tor controller and 181 // daemon. It will attempt to close the old connection, make a new connection 182 // and authenticate, and finally reset the activeServiceID that the controller 183 // is aware of. 184 // 185 // NOTE: Any old onion services will be removed once this function is called. 186 // In the case of a Tor daemon restart, previously created onion services will 187 // no longer be there. If the function is called without a Tor daemon restart, 188 // because the control connection is reset, all the onion services belonging to 189 // the old connection will be removed. 190 func (c *Controller) Reconnect() error { 191 // Require the tor controller to be running when we want to reconnect. 192 // This means the started flag must be 1 and the stopped flag must be 193 // 0. 194 if c.started != 1 { 195 return errTCNotStarted 196 } 197 if c.stopped != 0 { 198 return errTCStopped 199 } 200 201 log.Info("Re-connectting tor controller") 202 203 // If we have an old connection, try to close it. We might receive an 204 // error if the connection has already been closed by Tor daemon(ie, 205 // daemon restarted), so we ignore the error here. 206 if c.conn != nil { 207 if err := c.conn.Close(); err != nil { 208 log.Debugf("closing old conn got err: %v", err) 209 } 210 } 211 212 // Make a new connection and authenticate. 213 conn, err := textproto.Dial("tcp", c.controlAddr) 214 if err != nil { 215 return fmt.Errorf("unable to connect to Tor server: %w", err) 216 } 217 218 c.conn = conn 219 220 // Authenticate the connection between the controller and Tor daemon. 221 if err := c.authenticate(); err != nil { 222 return err 223 } 224 225 // Reset the activeServiceID. This value would only be set if a 226 // previous onion service was created. Because the old connection has 227 // been closed at this point, the old onion service is no longer 228 // active. 229 c.activeServiceID = "" 230 231 return nil 232 } 233 234 // sendCommand sends a command to the Tor server and returns its response, as a 235 // single space-delimited string, and code. 236 func (c *Controller) sendCommand(command string) (int, string, error) { 237 id, err := c.conn.Cmd(command) 238 if err != nil { 239 return 0, "", err 240 } 241 242 // Make sure our reader only process the response returned from the 243 // above command. 244 c.conn.StartResponse(id) 245 defer c.conn.EndResponse(id) 246 247 code, reply, err := c.readResponse(success) 248 if err != nil { 249 log.Debugf("sendCommand:%s got err:%v, reply:%v", 250 command, err, reply) 251 return code, reply, err 252 } 253 254 return code, reply, nil 255 } 256 257 // readResponse reads the replies from Tor to the controller. The reply has the 258 // following format, 259 // 260 // Reply = SyncReply / AsyncReply 261 // SyncReply = *(MidReplyLine / DataReplyLine) EndReplyLine 262 // AsyncReply = *(MidReplyLine / DataReplyLine) EndReplyLine 263 // 264 // MidReplyLine = StatusCode "-" ReplyLine 265 // DataReplyLine = StatusCode "+" ReplyLine CmdData 266 // EndReplyLine = StatusCode SP ReplyLine 267 // ReplyLine = [ReplyText] CRLF 268 // ReplyText = XXXX 269 // StatusCode = 3DIGIT 270 // 271 // Unless specified otherwise, multiple lines in a single reply from Tor daemon 272 // to the controller are guaranteed to share the same status code. Read more on 273 // this topic: 274 // 275 // https://gitweb.torproject.org/torspec.git/tree/control-spec.txt#n158 276 // 277 // NOTE: this code is influenced by https://github.com/Yawning/bulb. 278 func (c *Controller) readResponse(expected int) (int, string, error) { 279 // Clean the buffer inside the conn. This is needed when we encountered 280 // an error while reading the response, the remaining lines need to be 281 // cleaned before next read. 282 defer func() { 283 if _, err := c.conn.R.Discard(c.conn.R.Buffered()); err != nil { 284 log.Errorf("clean read buffer failed: %v", err) 285 } 286 }() 287 288 reply, code := "", 0 289 hasMoreLines := true 290 291 for hasMoreLines { 292 line, err := c.conn.Reader.ReadLine() 293 if err != nil { 294 return 0, reply, err 295 } 296 log.Tracef("Reading line: %v", line) 297 298 // Line being shortter than 4 is not allowed. 299 if len(line) < 4 { 300 err = textproto.ProtocolError("short line: " + line) 301 return 0, reply, err 302 } 303 304 // Parse the status code. 305 code, err = strconv.Atoi(line[0:3]) 306 if err != nil { 307 return code, reply, err 308 } 309 310 switch line[3] { 311 // EndReplyLine = StatusCode SP ReplyLine. 312 // Example: 250 OK 313 // This is the end of the response, so we mark hasMoreLines to 314 // be false to exit the loop. 315 case ' ': 316 reply += line[4:] 317 hasMoreLines = false 318 319 // MidReplyLine = StatusCode "-" ReplyLine. 320 // Example: 250-version=... 321 // This is a continued response, so we keep reading the next 322 // line. 323 case '-': 324 reply += line[4:] 325 326 // DataReplyLine = StatusCode "+" ReplyLine CmdData. 327 // Example: 250+config-text= 328 // line1 329 // line2 330 // more lines... 331 // . 332 // This is a data response, meaning the following multiple 333 // lines are the actual data, and a dot(.) in the end means the 334 // end of the data response. The response will be formatted as, 335 // key=line1,line2,... 336 // The above example will then be, 337 // config-text=line1,line2,... 338 case '+': 339 // Add the key(config-text=) 340 reply += line[4:] 341 342 // Add the values. 343 resp, err := c.conn.Reader.ReadDotLines() 344 if err != nil { 345 return code, reply, err 346 } 347 reply += strings.Join(resp, ",") 348 349 // Invalid line separator found. 350 default: 351 err = textproto.ProtocolError("invalid line: " + line) 352 return code, reply, err 353 } 354 355 // We check the code here so that the error message is parsed 356 // from the line. 357 if code != expected { 358 return code, reply, errCodeNotMatch 359 } 360 361 // Separate each line using "\n". 362 if hasMoreLines { 363 reply += "\n" 364 } 365 } 366 367 log.Tracef("Parsed reply: %v", reply) 368 return code, reply, nil 369 } 370 371 // parseTorReply parses the reply from the Tor server after receiving a command 372 // from a controller. This will parse the relevant reply parameters into a map 373 // of keys and values. 374 func parseTorReply(reply string) map[string]string { 375 params := make(map[string]string) 376 377 // Replies can either span single or multiple lines, so we'll default 378 // to stripping whitespace and newlines in order to retrieve the 379 // individual contents of it. The -1 indicates that we want this to span 380 // across all instances of a newline. 381 contents := strings.Split(strings.Replace(reply, "\n", " ", -1), " ") 382 for _, content := range contents { 383 // Each parameter within the reply should be of the form 384 // "KEY=VALUE". If the parameter doesn't contain "=", then we 385 // can assume it does not provide any other relevant information 386 // already known. 387 keyValue := strings.SplitN(content, "=", 2) 388 if len(keyValue) != 2 { 389 continue 390 } 391 392 key := keyValue[0] 393 value := keyValue[1] 394 params[key] = value 395 } 396 397 return params 398 } 399 400 // authenticate authenticates the connection between the controller and the 401 // Tor server using either of the following supported authentication methods 402 // depending on its configuration: SAFECOOKIE, HASHEDPASSWORD, and NULL. 403 func (c *Controller) authenticate() error { 404 protocolInfo, err := c.protocolInfo() 405 if err != nil { 406 return err 407 } 408 409 log.Debugf("received protocol info: %v", protocolInfo) 410 411 // With the version retrieved, we'll cache it now in case it needs to be 412 // used later on. 413 c.version = protocolInfo.version() 414 415 switch { 416 // If a password was provided, then we should attempt to use the 417 // HASHEDPASSWORD authentication method. 418 case c.password != "": 419 if !protocolInfo.supportsAuthMethod(authHashedPassword) { 420 return fmt.Errorf("%v authentication method not "+ 421 "supported", authHashedPassword) 422 } 423 424 return c.authenticateViaHashedPassword() 425 426 // Otherwise, attempt to authentication via the SAFECOOKIE method as it 427 // provides the most security. 428 case protocolInfo.supportsAuthMethod(authSafeCookie): 429 return c.authenticateViaSafeCookie(protocolInfo) 430 431 // Fallback to the NULL method if any others aren't supported. 432 case protocolInfo.supportsAuthMethod(authNull): 433 return c.authenticateViaNull() 434 435 // No supported authentication methods, fail. 436 default: 437 return errors.New("the Tor server must be configured with " + 438 "NULL, SAFECOOKIE, or HASHEDPASSWORD authentication") 439 } 440 } 441 442 // authenticateViaNull authenticates the controller with the Tor server using 443 // the NULL authentication method. 444 func (c *Controller) authenticateViaNull() error { 445 _, _, err := c.sendCommand("AUTHENTICATE") 446 return err 447 } 448 449 // authenticateViaHashedPassword authenticates the controller with the Tor 450 // server using the HASHEDPASSWORD authentication method. 451 func (c *Controller) authenticateViaHashedPassword() error { 452 cmd := fmt.Sprintf("AUTHENTICATE \"%s\"", c.password) 453 _, _, err := c.sendCommand(cmd) 454 return err 455 } 456 457 // authenticateViaSafeCookie authenticates the controller with the Tor server 458 // using the SAFECOOKIE authentication method. 459 func (c *Controller) authenticateViaSafeCookie(info protocolInfo) error { 460 // Before proceeding to authenticate the connection, we'll retrieve 461 // the authentication cookie of the Tor server. This will be used 462 // throughout the authentication routine. We do this before as once the 463 // authentication routine has begun, it is not possible to retrieve it 464 // mid-way. 465 cookie, err := c.getAuthCookie(info) 466 if err != nil { 467 return fmt.Errorf("unable to retrieve authentication cookie: "+ 468 "%v", err) 469 } 470 471 // Authenticating using the SAFECOOKIE authentication method is a two 472 // step process. We'll kick off the authentication routine by sending 473 // the AUTHCHALLENGE command followed by a hex-encoded 32-byte nonce. 474 clientNonce := make([]byte, nonceLen) 475 if _, err := rand.Read(clientNonce); err != nil { 476 return fmt.Errorf("unable to generate client nonce: %v", err) 477 } 478 479 cmd := fmt.Sprintf("AUTHCHALLENGE SAFECOOKIE %x", clientNonce) 480 _, reply, err := c.sendCommand(cmd) 481 if err != nil { 482 return err 483 } 484 485 // If successful, the reply from the server should be of the following 486 // format: 487 // 488 // "250 AUTHCHALLENGE" 489 // SP "SERVERHASH=" ServerHash 490 // SP "SERVERNONCE=" ServerNonce 491 // CRLF 492 // 493 // We're interested in retrieving the SERVERHASH and SERVERNONCE 494 // parameters, so we'll parse our reply to do so. 495 replyParams := parseTorReply(reply) 496 497 // Once retrieved, we'll ensure these values are of proper length when 498 // decoded. 499 serverHash, ok := replyParams["SERVERHASH"] 500 if !ok { 501 return errors.New("server hash not found in reply") 502 } 503 decodedServerHash, err := hex.DecodeString(serverHash) 504 if err != nil { 505 return fmt.Errorf("unable to decode server hash: %v", err) 506 } 507 if len(decodedServerHash) != sha256.Size { 508 return errors.New("invalid server hash length") 509 } 510 511 serverNonce, ok := replyParams["SERVERNONCE"] 512 if !ok { 513 return errors.New("server nonce not found in reply") 514 } 515 decodedServerNonce, err := hex.DecodeString(serverNonce) 516 if err != nil { 517 return fmt.Errorf("unable to decode server nonce: %v", err) 518 } 519 if len(decodedServerNonce) != nonceLen { 520 return errors.New("invalid server nonce length") 521 } 522 523 // The server hash above was constructed by computing the HMAC-SHA256 524 // of the message composed of the cookie, client nonce, and server 525 // nonce. We'll redo this computation ourselves to ensure the integrity 526 // and authentication of the message. 527 hmacMessage := bytes.Join( 528 [][]byte{cookie, clientNonce, decodedServerNonce}, []byte{}, 529 ) 530 computedServerHash := computeHMAC256(serverKey, hmacMessage) 531 if !hmac.Equal(computedServerHash, decodedServerHash) { 532 return fmt.Errorf("expected server hash %x, got %x", 533 decodedServerHash, computedServerHash) 534 } 535 536 // If the MAC check was successful, we'll proceed with the last step of 537 // the authentication routine. We'll now send the AUTHENTICATE command 538 // followed by a hex-encoded client hash constructed by computing the 539 // HMAC-SHA256 of the same message, but this time using the controller's 540 // key. 541 clientHash := computeHMAC256(controllerKey, hmacMessage) 542 if len(clientHash) != sha256.Size { 543 return errors.New("invalid client hash length") 544 } 545 546 cmd = fmt.Sprintf("AUTHENTICATE %x", clientHash) 547 if _, _, err := c.sendCommand(cmd); err != nil { 548 return err 549 } 550 551 return nil 552 } 553 554 // getAuthCookie retrieves the authentication cookie in bytes from the Tor 555 // server. Cookie authentication must be enabled for this to work. 556 func (c *Controller) getAuthCookie(info protocolInfo) ([]byte, error) { 557 // Retrieve the cookie file path from the PROTOCOLINFO reply. 558 cookieFilePath, ok := info["COOKIEFILE"] 559 if !ok { 560 return nil, errors.New("COOKIEFILE not found in PROTOCOLINFO " + 561 "reply") 562 } 563 cookieFilePath = strings.Trim(cookieFilePath, "\"") 564 565 // Read the cookie from the file and ensure it has the correct length. 566 cookie, err := ioutil.ReadFile(cookieFilePath) 567 if err != nil { 568 return nil, err 569 } 570 571 if len(cookie) != cookieLen { 572 return nil, errors.New("invalid authentication cookie length") 573 } 574 575 return cookie, nil 576 } 577 578 // computeHMAC256 computes the HMAC-SHA256 of a key and message. 579 func computeHMAC256(key, message []byte) []byte { 580 mac := hmac.New(sha256.New, key) 581 mac.Write(message) 582 return mac.Sum(nil) 583 } 584 585 // supportsV3 is a helper function that parses the current version of the Tor 586 // server and determines whether it supports creationg v3 onion services through 587 // Tor's control port. The version string should be of the format: 588 // 589 // major.minor.revision.build 590 func supportsV3(version string) error { 591 // We'll split the minimum Tor version that's supported and the given 592 // version in order to individually compare each number. 593 parts := strings.Split(version, ".") 594 if len(parts) != 4 { 595 return errors.New("version string is not of the format " + 596 "major.minor.revision.build") 597 } 598 599 // It's possible that the build number (the last part of the version 600 // string) includes a pre-release string, e.g. rc, beta, etc., so we'll 601 // parse that as well. 602 build := strings.Split(parts[len(parts)-1], "-") 603 parts[len(parts)-1] = build[0] 604 605 // Ensure that each part of the version string corresponds to a number. 606 for _, part := range parts { 607 if _, err := strconv.Atoi(part); err != nil { 608 return err 609 } 610 } 611 612 // Once we've determined we have a proper version string of the format 613 // major.minor.revision.build, we can just do a string comparison to 614 // determine if it satisfies the minimum version supported. 615 if version < MinTorVersion { 616 return fmt.Errorf("version %v below minimum version supported "+ 617 "%v", version, MinTorVersion) 618 } 619 620 return nil 621 } 622 623 // protocolInfo is encompasses the details of a response to a PROTOCOLINFO 624 // command. 625 type protocolInfo map[string]string 626 627 // version returns the Tor version as reported by the server. 628 func (i protocolInfo) version() string { 629 version := i["Tor"] 630 return strings.Trim(version, "\"") 631 } 632 633 // supportsAuthMethod determines whether the Tor server supports the given 634 // authentication method. 635 func (i protocolInfo) supportsAuthMethod(method string) bool { 636 methods, ok := i["METHODS"] 637 if !ok { 638 return false 639 } 640 return strings.Contains(methods, method) 641 } 642 643 // protocolInfo sends a "PROTOCOLINFO" command to the Tor server and returns its 644 // response. 645 func (c *Controller) protocolInfo() (protocolInfo, error) { 646 cmd := fmt.Sprintf("PROTOCOLINFO %d", ProtocolInfoVersion) 647 _, reply, err := c.sendCommand(cmd) 648 if err != nil { 649 return nil, err 650 } 651 652 return protocolInfo(parseTorReply(reply)), nil 653 }