check current onion descriptors on old versions of tor to see if they're out-of-sync

This commit is contained in:
erinn 2018-10-09 12:56:25 -07:00
parent 9c1c5ef98a
commit 6258396dc7
2 changed files with 89 additions and 4 deletions

View File

@ -14,7 +14,11 @@ import (
"fmt"
"strings"
"errors"
"github.com/yawning/bulb/utils/pkcs1"
"golang.org/x/net/proxy"
"log"
"strconv"
)
// OnionInfo is the result of the AddOnion command.
@ -39,6 +43,7 @@ type OnionPortSpec struct {
// NewOnionConfig is a configuration for NewOnion command.
type NewOnionConfig struct {
Onion string
PortSpecs []OnionPortSpec
PrivateKey crypto.PrivateKey
DiscardPK bool
@ -49,7 +54,7 @@ type NewOnionConfig struct {
// NewOnion issues an ADD_ONION command using configuration config and
// returns the parsed response.
func (c *Conn) NewOnion(config *NewOnionConfig) (*OnionInfo, error) {
func (c *Conn) NewOnion(config *NewOnionConfig, dontCheckDescriptor bool) (*OnionInfo, error) {
const keyTypeRSA = "RSA1024"
var err error
@ -109,9 +114,41 @@ func (c *Conn) NewOnion(config *NewOnionConfig) (*OnionInfo, error) {
flagsStr = " Flags="
flagsStr += strings.Join(flags, ",")
}
request := fmt.Sprintf("ADD_ONION %s:%s%s%s", hsKeyType, hsKeyStr, portStr, flagsStr)
resp, err := c.Request(request)
if err != nil && fmt.Sprintf("%v", err) != "550 Unspecified Tor error: Onion address collision" {
if err == nil && !dontCheckDescriptor {
pi, _ := c.ProtocolInfo()
// 0.3.5.1
torversion := strings.Split(pi.TorVersion, ".")
tva, _ := strconv.Atoi(torversion[0])
tvb, _ := strconv.Atoi(torversion[1])
tvc, _ := strconv.Atoi(torversion[2])
if tva == 0 && (tvb < 3 || (tvb == 3 && tvc < 5)) {
// here we need to check if the descriptor upload succeeded
log.Println("running a descriptor check because you are on tor version < 0.3.5.1-alpha. this will take a little while but only happens once per tor process--onion service pair")
HSFetch(config.Onion)
crc, err := c.GetClientRevisionCounter(config.Onion)
if err != nil {
log.Printf("%v\n", err)
}
src, err := c.GetServiceRevisionCounter(config.Onion)
if err != nil {
log.Printf("%v\n", err)
}
if crc > src {
log.Printf("uh oh: your tor process is using a service descriptor revision counter of %d\n", src)
log.Printf("whereas the HSDirs have revision %d. this is a bug in tor < 0.3.5.1-alpha\n", crc)
log.Printf("your have two options to fix it:\n")
log.Printf("- upgrade to tor >= 0.3.5.1-alpha [recommended]\n")
log.Printf("- run this listener %d more times [not a great idea but it'll work]\n", crc-src)
log.Printf("the revision counter is only reset when the tor process restarts, so try not to do that in order to prevent this problem from reoccurring\n")
return nil, errors.New("client descriptor is newer than service descriptor")
}
fmt.Printf("crc: %v src: %v\n", crc, src)
}
} else if fmt.Sprintf("%v", err) != "550 Unspecified Tor error: Onion address collision" {
// (don't worry if there is an address collision -- it's a good thing and means we're using the cached circuits)
return nil, err
}
@ -171,7 +208,7 @@ func (c *Conn) AddOnion(ports []OnionPortSpec, key crypto.PrivateKey, oneshot bo
cfg.PrivateKey = key
}
cfg.DiscardPK = oneshot
return c.NewOnion(cfg)
return c.NewOnion(cfg, false)
}
// DeleteOnion issues a DEL_ONION command and returns the parsed response.
@ -179,3 +216,51 @@ func (c *Conn) DeleteOnion(serviceID string) error {
_, err := c.Request("DEL_ONION %s", serviceID)
return err
}
// run HSFETCH for an onion, in order to put its onion service descriptor into the client cache
// unfortunately we don't actually do this, because HSFETCH doesn't support v3 yet
// instead we make a shortlived connection to it, which has a side effect of putting the descriptor into cache
// see https://trac.torproject.org/projects/tor/ticket/25417 for details
func HSFetch(onion string) error {
torDialer, err := proxy.SOCKS5("tcp", "127.0.0.1:9050", nil, proxy.Direct)
if err != nil {
return err
}
conn, err := torDialer.Dial("tcp", onion+".onion:9878")
if err != nil {
return err
}
conn.Close()
return nil
}
func (c *Conn) GetClientRevisionCounter(onion string) (uint64, error) {
return c.getRevisionCounterHelper(onion, "client")
}
func (c *Conn) GetServiceRevisionCounter(onion string) (uint64, error) {
return c.getRevisionCounterHelper(onion, "service")
}
func (c *Conn) getRevisionCounterHelper(onion string, cachetype string) (uint64, error) {
_, err := c.conn.Cmd("GETINFO hs/%v/desc/id/%v", cachetype, onion)
if err != nil {
return 0, err
}
var rc uint64
reterr := errors.New("could not find revision counter in onion descriptor")
for {
line, err := c.conn.ReadLine()
if err != nil {
return 0, err
}
if strings.HasPrefix(line, "250 OK") {
return rc, reterr
} else if strings.HasPrefix(line, "revision-counter ") {
rc, reterr = strconv.ParseUint(line[17:], 10, 64)
}
}
return rc, reterr
}

View File

@ -114,7 +114,7 @@ func (c *Conn) RecoverListener(config *NewOnionConfig, onion string, vports ...u
}
cfg.PortSpecs = portSpecs
// Create the onion.
oi, err := c.NewOnion(&cfg)
oi, err := c.NewOnion(&cfg, false)
if err != nil {
tcpListener.Close()
return nil, err