Android use changes: dedup peer start code; peer and app track write directory and files, do not use supplied filenames for Save(); rework tormanager to take a path to tor binary

This commit is contained in:
Dan Ballard 2018-07-10 15:42:47 -05:00
parent bcaa85fd5b
commit c289052aae
8 changed files with 112 additions and 84 deletions

View File

@ -36,7 +36,7 @@ pipeline:
when: when:
status: [ success, changed, failure ] status: [ success, changed, failure ]
notify-gogs: notify-gogs:
image: mindstab/drone-gogs image: openpriv/drone-gogs
when: when:
event: pull_request event: pull_request
secrets: [gogs_account_token] secrets: [gogs_account_token]

View File

@ -3,10 +3,10 @@ package app
import ( import (
"cwtch.im/cwtch/connectivity/tor" "cwtch.im/cwtch/connectivity/tor"
"cwtch.im/cwtch/peer" "cwtch.im/cwtch/peer"
"errors"
"fmt" "fmt"
"log" "log"
"os" "os"
"os/user"
"path" "path"
) )
@ -14,54 +14,53 @@ import (
type Application struct { type Application struct {
Peer peer.CwtchPeerInterface Peer peer.CwtchPeerInterface
TorManager *tor.Manager TorManager *tor.Manager
directory string
}
// NewApp creates a new app with some environment awareness and initializes a Tor Manager
func NewApp(appDirectory string, torPath string) (*Application, error) {
log.Printf("NewApp(%v, %v)\n", appDirectory, torPath)
app := &Application{Peer: nil, directory: appDirectory}
os.MkdirAll(path.Join(appDirectory, "tor"), 0700)
err := app.startTor(torPath)
if err != nil {
return nil, err
}
return app, nil
} }
// NewProfile creates a new cwtchPeer with a given name. // NewProfile creates a new cwtchPeer with a given name.
func (app *Application) NewProfile(name string, filename string, password string) error { func (app *Application) NewProfile(name string, password string) error {
profile := peer.NewCwtchPeer(name, password) log.Printf("NewProfile(%v, %v)\n", name, password)
app.Peer = profile if app.Peer != nil {
err := profile.Save(filename) return errors.New("Profile already created")
}
app.Peer = peer.NewCwtchPeer(name, password, path.Join(app.directory, name+".json"))
err := app.Peer.Save()
if err == nil { if err == nil {
err = app.startPeer()
err := app.startTor()
if err != nil {
return err
}
go func() {
err := app.Peer.Listen()
if err != nil {
log.Panic(err)
}
}()
} }
return err return err
} }
func (app *Application) startTor() error { // startTor will create a local torrc if needed
func (app *Application) startTor(torPath string) error {
// Creating a local cwtch tor server config for the user // Creating a local cwtch tor server config for the user
usr, err := user.Current() // creating $app.directory/torrc file
if err != nil {
return err
}
// creating /home/<usr>/.cwtch/torrc file
// SOCKSPort socksPort // SOCKSPort socksPort
// ControlPort controlPort // ControlPort controlPort
torrc := path.Join(usr.HomeDir, ".cwtch", "torrc") torrc := path.Join(app.directory, "tor", "torrc")
if _, err := os.Stat(torrc); os.IsNotExist(err) { if _, err := os.Stat(torrc); os.IsNotExist(err) {
log.Printf("writing torrc to: %v\n", torrc)
os.MkdirAll(path.Join(usr.HomeDir, ".cwtch"), 0700)
file, err := os.Create(torrc) file, err := os.Create(torrc)
if err != nil { if err != nil {
return err return err
} }
fmt.Fprintf(file, "SOCKSPort %d\nControlPort %d\n", 9050, 9051) fmt.Fprintf(file, "SOCKSPort %d\nControlPort %d\nCookieAuthentication 0\nSafeSocks 1\n", 9050, 9051)
file.Close() file.Close()
} }
tm, err := tor.NewTorManager(9050, 9051, torrc) tm, err := tor.NewTorManager(9050, 9051, torPath, torrc)
if err != nil { if err != nil {
return err return err
} }
@ -76,20 +75,18 @@ func (app *Application) SetProfile(filename string, password string) error {
return err return err
} }
app.Peer = profile app.Peer = profile
if err == nil { return app.startPeer()
}
err := app.startTor() func (app *Application) startPeer() error {
if err != nil { go func() {
return err e := app.Peer.Listen()
if e != nil {
log.Panic(e)
} }
go func() { }()
err := app.Peer.Listen()
if err != nil { return nil
log.Panic(err)
}
}()
}
return err
} }
// PeerRequest attempts to setup peer relationship with the given onion address.` // PeerRequest attempts to setup peer relationship with the given onion address.`

View File

@ -10,14 +10,18 @@ import (
"bytes" "bytes"
"golang.org/x/crypto/ssh/terminal" "golang.org/x/crypto/ssh/terminal"
"log"
"os" "os"
"os/exec"
"os/user"
"path"
"syscall" "syscall"
) )
var app app2.Application var app *app2.Application
var suggestions = []prompt.Suggest{ var suggestions = []prompt.Suggest{
{Text: "new-profile", Description: "create a new profile"}, {Text: "new-profile", Description: "create a new profile in ~/.cwtch/$USERNAME.json"},
{Text: "load-profile", Description: "load a new profile"}, {Text: "load-profile", Description: "load a new profile"},
{Text: "quit", Description: "quit cwtch"}, {Text: "quit", Description: "quit cwtch"},
{Text: "info", Description: "show user info"}, {Text: "info", Description: "show user info"},
@ -37,7 +41,7 @@ var suggestions = []prompt.Suggest{
} }
var usages = map[string]string{ var usages = map[string]string{
"new-profile": "new-profile [name] [filename]", "new-profile": "new-profile [name]",
"load-profile": "load-profile [filename]", "load-profile": "load-profile [filename]",
"quit": "", "quit": "",
"servers": "", "servers": "",
@ -163,8 +167,18 @@ func main() {
fmt.Printf("%v\n\n", cwtch) fmt.Printf("%v\n\n", cwtch)
quit := false quit := false
app = app2.Application{}
profilefile := "" torPath, err := exec.LookPath("tor")
if err != nil {
log.Fatal("tor could not be found on this system. Please install it in the system $PATH")
}
usr, err := user.Current()
if err != nil {
log.Fatalf("\nError: could not load current user: %v\n", err)
}
app, err = app2.NewApp(path.Join(usr.HomeDir, ".cwtch"), torPath)
var history []string var history []string
for !quit { for !quit {
profile := "unset" profile := "unset"
@ -181,10 +195,10 @@ func main() {
history = append(history, text) history = append(history, text)
switch commands[0] { switch commands[0] {
case "quit": case "quit":
app.Peer.Save(profilefile) app.Peer.Save()
quit = true quit = true
case "new-profile": case "new-profile":
if len(commands) == 3 { if len(commands) == 2 {
fmt.Print("** WARNING: PASSWORDS CANNOT BE RECOVERED! **\n") fmt.Print("** WARNING: PASSWORDS CANNOT BE RECOVERED! **\n")
password := "" password := ""
@ -209,8 +223,7 @@ func main() {
if failcount >= 3 { if failcount >= 3 {
fmt.Printf("Error creating profile for %v: Your password entries must match!\n", commands[1]) fmt.Printf("Error creating profile for %v: Your password entries must match!\n", commands[1])
} else { } else {
err := app.NewProfile(commands[1], commands[2], password) err := app.NewProfile(commands[1], password)
profilefile = commands[2]
if err == nil { if err == nil {
fmt.Printf("\nNew profile created for %v\n", commands[1]) fmt.Printf("\nNew profile created for %v\n", commands[1])
} else { } else {
@ -224,10 +237,9 @@ func main() {
if len(commands) == 2 { if len(commands) == 2 {
fmt.Print("Enter a password to decrypt the profile: ") fmt.Print("Enter a password to decrypt the profile: ")
bytePassword, err := terminal.ReadPassword(int(syscall.Stdin)) bytePassword, err := terminal.ReadPassword(int(syscall.Stdin))
err = app.SetProfile(commands[1], string(bytePassword)) err = app.SetProfile(commands[1]+".json", string(bytePassword))
if err == nil { if err == nil {
fmt.Printf("\nLoaded profile for %v\n", commands[1]) fmt.Printf("\nLoaded profile for %v\n", commands[1])
profilefile = commands[1]
} else { } else {
fmt.Printf("Error loading profile for %v: %v\n", commands[1], err) fmt.Printf("Error loading profile for %v: %v\n", commands[1], err)
} }
@ -288,7 +300,7 @@ func main() {
if err != nil { if err != nil {
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
} else { } else {
app.Peer.Save(profilefile) app.Peer.Save()
group := app.Peer.GetGroup(groupID) group := app.Peer.GetGroup(groupID)
if group == nil { if group == nil {
fmt.Printf("Error: group does not exist\n") fmt.Printf("Error: group does not exist\n")
@ -315,7 +327,7 @@ func main() {
id, _, err := app.Peer.StartGroup(commands[1]) id, _, err := app.Peer.StartGroup(commands[1])
if err == nil { if err == nil {
fmt.Printf("New Group [%v] created for server %v\n", id, commands[1]) fmt.Printf("New Group [%v] created for server %v\n", id, commands[1])
app.Peer.Save(profilefile) app.Peer.Save()
group := app.Peer.GetGroup(id) group := app.Peer.GetGroup(id)
if group == nil { if group == nil {
fmt.Printf("Error: group does not exist\n") fmt.Printf("Error: group does not exist\n")
@ -378,7 +390,7 @@ func main() {
fmt.Printf("Error reading timeline from group, usage: %s\n", usages["timeline"]) fmt.Printf("Error reading timeline from group, usage: %s\n", usages["timeline"])
} }
case "save": case "save":
app.Peer.Save(profilefile) app.Peer.Save()
case "help": case "help":
for _, command := range suggestions { for _, command := range suggestions {
fmt.Printf("%-18s%-56s%s\n", command.Text, command.Description, usages[command.Text]) fmt.Printf("%-18s%-56s%s\n", command.Text, command.Description, usages[command.Text])
@ -428,10 +440,8 @@ func main() {
} }
} }
} }
if profilefile != "" { if app.Peer != nil {
if app.Peer != nil { app.Peer.Save()
app.Peer.Save(profilefile)
}
} }
} }

View File

@ -8,7 +8,9 @@ import (
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"os"
"os/exec" "os/exec"
"path"
"strings" "strings"
"time" "time"
) )
@ -21,10 +23,11 @@ type Manager struct {
} }
// NewTorManager Instantiates a new connection manager, returns non-nil error if it fails to connect to a tor daemon on the given ports. // NewTorManager Instantiates a new connection manager, returns non-nil error if it fails to connect to a tor daemon on the given ports.
func NewTorManager(socksPort int, controlPort int, torrc string) (*Manager, error) { func NewTorManager(socksPort int, controlPort int, torPath string, torrc string) (*Manager, error) {
torManager := new(Manager) torManager := new(Manager)
torManager.socksPort = socksPort torManager.socksPort = socksPort
torManager.controlPort = controlPort torManager.controlPort = controlPort
err := torManager.TestConnection() err := torManager.TestConnection()
if err == nil { if err == nil {
@ -33,16 +36,30 @@ func NewTorManager(socksPort int, controlPort int, torrc string) (*Manager, erro
} }
// try to start tor // try to start tor
cmd := exec.Command("tor", "-f", torrc)
cmd := exec.Command(torPath, "-f", torrc)
// on Android, home can be set to '/' which is not writeable
if os.Getenv("HOME") == "" {
cmd.Env = append(os.Environ(), fmt.Sprintf("HOME=%s", path.Dir(torrc)))
}
log.Printf("starting local tor proxy") log.Printf("starting local tor proxy")
err = cmd.Start() err = cmd.Start()
if err != nil { if err != nil {
log.Printf("starting tor failed %v", err) log.Printf("starting tor failed %v", err)
return nil, err return nil, err
} }
time.Sleep(time.Second * 5)
torManager.process = cmd torManager.process = cmd
err = torManager.TestConnection()
// for 30 seconds check every 5 if tor is up and working
for i := 0; i < 6; i++ {
time.Sleep(time.Second * 5)
err = torManager.TestConnection()
if err == nil {
break
}
}
return torManager, err return torManager, err
} }

View File

@ -3,21 +3,25 @@ package tor
import ( import (
"fmt" "fmt"
"os" "os"
"os/exec"
"testing" "testing"
) )
func TestTorManager(t *testing.T) { func TestTorManager(t *testing.T) {
tor, err := exec.LookPath("tor")
if err != nil {
t.Errorf("tor not found in PATH")
}
os.Remove("/tmp/torrc") os.Remove("/tmp/torrc")
file, _ := os.Create("/tmp/torrc") file, _ := os.Create("/tmp/torrc")
fmt.Fprintf(file, "SOCKSPort %d\nControlPort %d\nDataDirectory /tmp/tor\n", 10050, 10051) fmt.Fprintf(file, "SOCKSPort %d\nControlPort %d\nDataDirectory /tmp/tor\n", 10050, 10051)
file.Close() file.Close()
tm, err := NewTorManager(10050, 10051, "/tmp/torrc") tm, err := NewTorManager(10050, 10051, tor, "/tmp/torrc")
if err != nil { if err != nil {
t.Errorf("creating a new tor manager failed: %v", err) t.Errorf("creating a new tor manager failed: %v", err)
} else { } else {
tm2, err := NewTorManager(10050, 10051, "/tmp/torrc") tm2, err := NewTorManager(10050, 10051, tor, "/tmp/torrc")
if err != nil { if err != nil {
t.Errorf("creating a new tor manager failed: %v", err) t.Errorf("creating a new tor manager failed: %v", err)
} }

View File

@ -43,7 +43,7 @@ type cwtchPeer struct {
// CwtchPeerInterface provides us with a way of testing systems built on top of cwtch without having to // CwtchPeerInterface provides us with a way of testing systems built on top of cwtch without having to
// directly implement a cwtchPeer. // directly implement a cwtchPeer.
type CwtchPeerInterface interface { type CwtchPeerInterface interface {
Save(string) error Save() error
PeerWithOnion(string) PeerWithOnion(string)
InviteOnionToGroup(string, string) error InviteOnionToGroup(string, string) error
@ -145,8 +145,9 @@ func (cp *cwtchPeer) setup() {
} }
// NewCwtchPeer creates and returns a new cwtchPeer with the given name. // NewCwtchPeer creates and returns a new cwtchPeer with the given name.
func NewCwtchPeer(name string, password string) CwtchPeerInterface { func NewCwtchPeer(name string, password string, profilefile string) CwtchPeerInterface {
cp := new(cwtchPeer) cp := new(cwtchPeer)
cp.profilefile = profilefile
cp.Profile = model.GenerateNewProfile(name) cp.Profile = model.GenerateNewProfile(name)
cp.setup() cp.setup()
key, salt := createKey(password) key, salt := createKey(password)
@ -156,14 +157,13 @@ func NewCwtchPeer(name string, password string) CwtchPeerInterface {
} }
// Save saves the cwtchPeer profile state to a file. // Save saves the cwtchPeer profile state to a file.
func (cp *cwtchPeer) Save(profilefile string) error { func (cp *cwtchPeer) Save() error {
cp.mutex.Lock() cp.mutex.Lock()
encryptedbytes := encryptProfile(cp, cp.key) encryptedbytes := encryptProfile(cp, cp.key)
// the salt for the derived key is appended to the front of the file // the salt for the derived key is appended to the front of the file
encryptedbytes = append(cp.salt[:], encryptedbytes...) encryptedbytes = append(cp.salt[:], encryptedbytes...)
err := ioutil.WriteFile(profilefile, encryptedbytes, 0600) err := ioutil.WriteFile(cp.profilefile, encryptedbytes, 0600)
cp.profilefile = profilefile
cp.mutex.Unlock() cp.mutex.Unlock()
return err return err
} }
@ -171,7 +171,6 @@ func (cp *cwtchPeer) Save(profilefile string) error {
// LoadCwtchPeer loads an existing cwtchPeer from a file. // LoadCwtchPeer loads an existing cwtchPeer from a file.
func LoadCwtchPeer(profilefile string, password string) (CwtchPeerInterface, error) { func LoadCwtchPeer(profilefile string, password string) (CwtchPeerInterface, error) {
encryptedbytes, err := ioutil.ReadFile(profilefile) encryptedbytes, err := ioutil.ReadFile(profilefile)
if err == nil { if err == nil {
var dkr [32]byte var dkr [32]byte
var salty [128]byte var salty [128]byte
@ -184,7 +183,8 @@ func LoadCwtchPeer(profilefile string, password string) (CwtchPeerInterface, err
copy(dkr[:], dk) copy(dkr[:], dk)
copy(salty[:], salt) copy(salty[:], salt)
cp, err := decryptProfile(encryptedbytes, dkr) var cp *cwtchPeer
cp, err = decryptProfile(encryptedbytes, dkr)
if err == nil { if err == nil {
cp.setup() cp.setup()
cp.profilefile = profilefile cp.profilefile = profilefile
@ -429,7 +429,7 @@ type CwtchPeerHandler struct {
func (cph *CwtchPeerHandler) ClientIdentity(ci *protocol.CwtchIdentity) { func (cph *CwtchPeerHandler) ClientIdentity(ci *protocol.CwtchIdentity) {
log.Printf("Received Client Identity from %v %v\n", cph.Onion, ci.String()) log.Printf("Received Client Identity from %v %v\n", cph.Onion, ci.String())
cph.Peer.Profile.AddCwtchIdentity(cph.Onion, ci) cph.Peer.Profile.AddCwtchIdentity(cph.Onion, ci)
cph.Peer.Save(cph.Peer.profilefile) cph.Peer.Save()
} }
// HandleGroupInvite handles incoming GroupInvites // HandleGroupInvite handles incoming GroupInvites

View File

@ -6,10 +6,10 @@ import (
func TestCwtchPeerGenerate(t *testing.T) { func TestCwtchPeerGenerate(t *testing.T) {
alice := NewCwtchPeer("alice", "testpass") alice := NewCwtchPeer("alice", "testpass", "./alice.json")
alice.Save("./test_profile") alice.Save()
aliceLoaded, err := LoadCwtchPeer("./test_profile", "testpass") aliceLoaded, err := LoadCwtchPeer("./alice.json", "testpass")
if err != nil || aliceLoaded.GetProfile().Name != "alice" { if err != nil || aliceLoaded.GetProfile().Name != "alice" {
t.Errorf("something went wrong saving and loading profiles %v %v", err, aliceLoaded) t.Errorf("something went wrong saving and loading profiles %v %v", err, aliceLoaded)
} }
@ -26,8 +26,8 @@ func TestCwtchPeerGenerate(t *testing.T) {
func TestTrustPeer(t *testing.T) { func TestTrustPeer(t *testing.T) {
groupName := "test.server" groupName := "test.server"
alice := NewCwtchPeer("alice", "alicepass") alice := NewCwtchPeer("alice", "alicepass", "")
bob := NewCwtchPeer("bob", "bobpass") bob := NewCwtchPeer("bob", "bobpass", "")
bobOnion := bob.GetProfile().Onion bobOnion := bob.GetProfile().Onion
aliceOnion := alice.GetProfile().Onion aliceOnion := alice.GetProfile().Onion

View File

@ -143,17 +143,17 @@ func TestCwtchPeerIntegration(t *testing.T) {
// ***** Peer setup ***** // ***** Peer setup *****
fmt.Println("Creating Alice...") fmt.Println("Creating Alice...")
alice := peer.NewCwtchPeer("Alice", "alicepass") alice := peer.NewCwtchPeer("Alice", "alicepass", "")
go alice.Listen() go alice.Listen()
fmt.Println("Alice created:", alice.GetProfile().Onion) fmt.Println("Alice created:", alice.GetProfile().Onion)
fmt.Println("Creating Bob...") fmt.Println("Creating Bob...")
bob := peer.NewCwtchPeer("Bob", "bobpass") bob := peer.NewCwtchPeer("Bob", "bobpass", "")
go bob.Listen() go bob.Listen()
fmt.Println("Bob created:", bob.GetProfile().Onion) fmt.Println("Bob created:", bob.GetProfile().Onion)
fmt.Println("Creating Carol...") fmt.Println("Creating Carol...")
carol := peer.NewCwtchPeer("Carol", "carolpass") carol := peer.NewCwtchPeer("Carol", "carolpass", "")
go carol.Listen() go carol.Listen()
fmt.Println("Carol created:", carol.GetProfile().Onion) fmt.Println("Carol created:", carol.GetProfile().Onion)