Merge pull request 'Generate Torrc and Integration test for Launching Tor with Generated Torrc' (#9) from authenticator into master
the build was successful
Details
the build was successful
Details
This commit is contained in:
commit
6f2a158504
|
@ -35,6 +35,15 @@ pipeline:
|
||||||
- ./tmp/tor -f ./testing/torrc
|
- ./tmp/tor -f ./testing/torrc
|
||||||
- sleep 15
|
- sleep 15
|
||||||
- sh testing/tests.sh
|
- sh testing/tests.sh
|
||||||
|
- pkill -9 tor
|
||||||
|
integration-tests:
|
||||||
|
when:
|
||||||
|
repo: openprivacy/connectivity
|
||||||
|
branch: master
|
||||||
|
event: [ push, pull_request ]
|
||||||
|
image: golang
|
||||||
|
commands:
|
||||||
|
- go test -race -v ./testing/launch_tor_integration_test.go
|
||||||
notify-email:
|
notify-email:
|
||||||
image: drillster/drone-email
|
image: drillster/drone-email
|
||||||
host: build.openprivacy.ca
|
host: build.openprivacy.ca
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
||||||
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLaunchTor(t *testing.T) {
|
||||||
|
log.SetLevel(log.LevelDebug)
|
||||||
|
// Create the tor data directory if it doesn't already exist..
|
||||||
|
os.MkdirAll("../tmp/data/tor", 0700)
|
||||||
|
err := tor.GenerateTorrc("examplehashedpassword", "../tmp/data/tor/torrc")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create torrc file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current working director, clean the paths to remove relative references
|
||||||
|
wd, _ := os.Getwd()
|
||||||
|
t.Logf("Launching bundled tor at %v", path.Clean(wd+"/../tmp/tor"))
|
||||||
|
acn, err := tor.NewTorACNWithAuth(path.Clean(wd+"/../tmp/data"), path.Clean(wd+"/../tmp/tor"), 9051, tor.HashedPasswordAuthenticator{Password: "examplehashedpassword"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("tor failed to start: %v", err)
|
||||||
|
}
|
||||||
|
acn.WaitTillBootstrapped()
|
||||||
|
t.Log("we have bootstrapped!")
|
||||||
|
acn.Close()
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package tor
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"git.openprivacy.ca/openprivacy/connectivity"
|
"git.openprivacy.ca/openprivacy/connectivity"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
"github.com/cretz/bine/control"
|
"github.com/cretz/bine/control"
|
||||||
|
@ -269,7 +270,6 @@ func startTor(appDirectory string, bundledTorPath string, controlPort int, authe
|
||||||
controlport, err := dialControlPort(tp.controlPort)
|
controlport, err := dialControlPort(tp.controlPort)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO: configurable auth
|
|
||||||
err := authenticator.Authenticate(controlport)
|
err := authenticator.Authenticate(controlport)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Debugln("connected to control port")
|
log.Debugln("connected to control port")
|
||||||
|
@ -282,10 +282,17 @@ func startTor(appDirectory string, bundledTorPath string, controlPort int, authe
|
||||||
controlport.Close()
|
controlport.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.Debugf("launching system tor\n")
|
||||||
|
|
||||||
|
// check if the torrc file is present where expected
|
||||||
|
if _, err := os.Stat(path.Join(dataDir, "torrc")); os.IsNotExist(err) {
|
||||||
|
log.Debugf("torrc file does not exist at %v", path.Join(dataDir, "torrc"))
|
||||||
|
return nil, fmt.Errorf("torrc file does not exist at %v", path.Join(dataDir, "torrc"))
|
||||||
|
}
|
||||||
|
|
||||||
// if not, try running system tor
|
// if not, try running system tor
|
||||||
if checkCmdlineTorVersion("tor") {
|
if checkCmdlineTorVersion("tor") {
|
||||||
t, err := tor.Start(nil, &tor.StartConf{EnableNetwork: true, DataDir: dataDir, DebugWriter: nil, ProcessCreator: newHideCmd("tor")})
|
t, err := tor.Start(nil, &tor.StartConf{EnableNetwork: true, DataDir: dataDir, TorrcFile: path.Join(dataDir, "torrc"), DebugWriter: nil, ProcessCreator: newHideCmd("tor")})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tp.t = t
|
tp.t = t
|
||||||
return tp, nil
|
return tp, nil
|
||||||
|
@ -293,10 +300,11 @@ func startTor(appDirectory string, bundledTorPath string, controlPort int, authe
|
||||||
log.Debugf("Error connecting to self-run system tor: %v\n", err)
|
log.Debugf("Error connecting to self-run system tor: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debugf("launching bundled tor\n")
|
||||||
// try running bundledTor
|
// try running bundledTor
|
||||||
if bundledTorPath != "" && checkCmdlineTorVersion(bundledTorPath) {
|
if bundledTorPath != "" && checkCmdlineTorVersion(bundledTorPath) {
|
||||||
log.Debugln("using bundled tor '" + bundledTorPath + "'")
|
log.Debugln("using bundled tor '" + bundledTorPath + "'")
|
||||||
t, err := tor.Start(nil, &tor.StartConf{EnableNetwork: true, DataDir: dataDir, ExePath: bundledTorPath, DebugWriter: nil, ProcessCreator: newHideCmd(bundledTorPath)})
|
t, err := tor.Start(nil, &tor.StartConf{EnableNetwork: true, DataDir: dataDir, TorrcFile: path.Join(dataDir, "torrc"), ExePath: bundledTorPath, DebugWriter: nil, ProcessCreator: newHideCmd(bundledTorPath)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("Error running bundled tor: %v\n", err)
|
log.Debugf("Error running bundled tor: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
package tor
|
package tor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha1"
|
||||||
"encoding/base32"
|
"encoding/base32"
|
||||||
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/cretz/bine/control"
|
"github.com/cretz/bine/control"
|
||||||
"golang.org/x/crypto/ed25519"
|
"golang.org/x/crypto/ed25519"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -60,17 +66,17 @@ type Authenticator interface {
|
||||||
// HashedPasswordAuthenticator authenticates to a Tor control port using a hashed password.
|
// HashedPasswordAuthenticator authenticates to a Tor control port using a hashed password.
|
||||||
// Note: This method is vulnerable to replay attacks by the host system (but so is cookie auth)
|
// Note: This method is vulnerable to replay attacks by the host system (but so is cookie auth)
|
||||||
type HashedPasswordAuthenticator struct {
|
type HashedPasswordAuthenticator struct {
|
||||||
password string
|
Password string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authenticate uses the given hashed password to authenticate to the control port
|
// Authenticate uses the given hashed password to authenticate to the control port
|
||||||
func (h HashedPasswordAuthenticator) Authenticate(controlport *control.Conn) error {
|
func (h HashedPasswordAuthenticator) Authenticate(controlport *control.Conn) error {
|
||||||
return controlport.Authenticate(h.password)
|
return controlport.Authenticate(h.Password)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHashedPasswordAuthenticator creates a new hashed password authenticator
|
// NewHashedPasswordAuthenticator creates a new hashed password authenticator
|
||||||
func NewHashedPasswordAuthenticator(password string) Authenticator {
|
func NewHashedPasswordAuthenticator(password string) Authenticator {
|
||||||
return HashedPasswordAuthenticator{password: password}
|
return HashedPasswordAuthenticator{Password: password}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NullAuthenticator exists to force always authenticating to a system tor.
|
// NullAuthenticator exists to force always authenticating to a system tor.
|
||||||
|
@ -81,3 +87,39 @@ type NullAuthenticator struct {
|
||||||
func (n NullAuthenticator) Authenticate(controlport *control.Conn) error {
|
func (n NullAuthenticator) Authenticate(controlport *control.Conn) error {
|
||||||
return errors.New("null authenticator provided, this control port is unsafe, start a new tor process instead")
|
return errors.New("null authenticator provided, this control port is unsafe, start a new tor process instead")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateHashedPassword calculates a hash in the same waay tha tor --hash-password does
|
||||||
|
// this function takes a salt as input which is not great from an api-misuse perspective, but
|
||||||
|
// we make it private.
|
||||||
|
func generateHashedPassword(salt [8]byte, password string) string {
|
||||||
|
c := 96
|
||||||
|
count := (16 + (c & 15)) << ((c >> 4) + 6)
|
||||||
|
tmp := append(salt[:], []byte(password)...)
|
||||||
|
slen := len(tmp)
|
||||||
|
d := sha1.New()
|
||||||
|
for count != 0 {
|
||||||
|
if count > slen {
|
||||||
|
d.Write(tmp)
|
||||||
|
count -= slen
|
||||||
|
} else {
|
||||||
|
d.Write(tmp[:count])
|
||||||
|
count = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hashed := d.Sum([]byte{})
|
||||||
|
return fmt.Sprintf("16:%s%s%s", strings.ToUpper(hex.EncodeToString(salt[:])),
|
||||||
|
strings.ToUpper(hex.EncodeToString([]byte{byte(c)})),
|
||||||
|
strings.ToUpper(hex.EncodeToString(hashed)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateTorrc generates a Tor config file with the control port bieng guarded by a hash of the given
|
||||||
|
// password.
|
||||||
|
func GenerateTorrc(password string, path string) error {
|
||||||
|
var salt [8]byte
|
||||||
|
if _, err := io.ReadFull(rand.Reader, salt[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hashedpassword := generateHashedPassword(salt, password)
|
||||||
|
contents := fmt.Sprintf("SOCKSPort 9050\nControlPort 9051\nHashedControlPassword %s", hashedpassword)
|
||||||
|
return ioutil.WriteFile(path, []byte(contents), 0600)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
package tor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGenerateHashedPassword(t *testing.T) {
|
||||||
|
// 16:C15305F97789414B601259E3EC5E76B8E55FC56A9F562B713F3D2BA257
|
||||||
|
hp := generateHashedPassword([8]byte{0xC1, 0x53, 0x05, 0xF9, 0x77, 0x89, 0x41, 0x4B}, "examplehashedpassword")
|
||||||
|
if hp != "16:C15305F97789414B601259E3EC5E76B8E55FC56A9F562B713F3D2BA257" {
|
||||||
|
t.Fatalf("hashed passwords do not match. Expected %s, got %s", "16:C15305F97789414B601259E3EC5E76B8E55FC56A9F562B713F3D2BA257", hp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateTorrc(t *testing.T) {
|
||||||
|
path := "./torrc.test"
|
||||||
|
err := GenerateTorrc("examplehashedpassword", path)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Torrc file could not be written")
|
||||||
|
}
|
||||||
|
os.Remove(path)
|
||||||
|
}
|
Loading…
Reference in New Issue