From c289052aae67749ce6df0cb2c36166b17f5b80a4 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 10 Jul 2018 15:42:47 -0500 Subject: [PATCH] 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 --- .drone.yml | 2 +- app/app.go | 81 +++++++++---------- app/cli/main.go | 46 ++++++----- connectivity/tor/tormanager.go | 25 +++++- connectivity/tor/tormanager_test.go | 10 ++- peer/cwtch_peer.go | 16 ++-- peer/cwtch_peer_test.go | 10 +-- .../cwtch_peer_server_intergration_test.go | 6 +- 8 files changed, 112 insertions(+), 84 deletions(-) diff --git a/.drone.yml b/.drone.yml index ea0f02f..d912a8d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -36,7 +36,7 @@ pipeline: when: status: [ success, changed, failure ] notify-gogs: - image: mindstab/drone-gogs + image: openpriv/drone-gogs when: event: pull_request secrets: [gogs_account_token] diff --git a/app/app.go b/app/app.go index c9a3a7c..e24304b 100644 --- a/app/app.go +++ b/app/app.go @@ -3,10 +3,10 @@ package app import ( "cwtch.im/cwtch/connectivity/tor" "cwtch.im/cwtch/peer" + "errors" "fmt" "log" "os" - "os/user" "path" ) @@ -14,54 +14,53 @@ import ( type Application struct { Peer peer.CwtchPeerInterface 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. -func (app *Application) NewProfile(name string, filename string, password string) error { - profile := peer.NewCwtchPeer(name, password) - app.Peer = profile - err := profile.Save(filename) +func (app *Application) NewProfile(name string, password string) error { + log.Printf("NewProfile(%v, %v)\n", name, password) + if app.Peer != nil { + 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 { - - err := app.startTor() - if err != nil { - return err - } - go func() { - err := app.Peer.Listen() - if err != nil { - log.Panic(err) - } - }() + err = app.startPeer() } 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 - usr, err := user.Current() - if err != nil { - return err - } - - // creating /home//.cwtch/torrc file + // creating $app.directory/torrc file // SOCKSPort socksPort // ControlPort controlPort - torrc := path.Join(usr.HomeDir, ".cwtch", "torrc") + torrc := path.Join(app.directory, "tor", "torrc") if _, err := os.Stat(torrc); os.IsNotExist(err) { - - os.MkdirAll(path.Join(usr.HomeDir, ".cwtch"), 0700) - + log.Printf("writing torrc to: %v\n", torrc) file, err := os.Create(torrc) if err != nil { 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() } - tm, err := tor.NewTorManager(9050, 9051, torrc) + tm, err := tor.NewTorManager(9050, 9051, torPath, torrc) if err != nil { return err } @@ -76,20 +75,18 @@ func (app *Application) SetProfile(filename string, password string) error { return err } app.Peer = profile - if err == nil { + return app.startPeer() +} - err := app.startTor() - if err != nil { - return err +func (app *Application) startPeer() error { + go func() { + e := app.Peer.Listen() + if e != nil { + log.Panic(e) } - go func() { - err := app.Peer.Listen() - if err != nil { - log.Panic(err) - } - }() - } - return err + }() + + return nil } // PeerRequest attempts to setup peer relationship with the given onion address.` diff --git a/app/cli/main.go b/app/cli/main.go index bc870e0..a3834d2 100644 --- a/app/cli/main.go +++ b/app/cli/main.go @@ -10,14 +10,18 @@ import ( "bytes" "golang.org/x/crypto/ssh/terminal" + "log" "os" + "os/exec" + "os/user" + "path" "syscall" ) -var app app2.Application +var app *app2.Application 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: "quit", Description: "quit cwtch"}, {Text: "info", Description: "show user info"}, @@ -37,7 +41,7 @@ var suggestions = []prompt.Suggest{ } var usages = map[string]string{ - "new-profile": "new-profile [name] [filename]", + "new-profile": "new-profile [name]", "load-profile": "load-profile [filename]", "quit": "", "servers": "", @@ -163,8 +167,18 @@ func main() { fmt.Printf("%v\n\n", cwtch) 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 for !quit { profile := "unset" @@ -181,10 +195,10 @@ func main() { history = append(history, text) switch commands[0] { case "quit": - app.Peer.Save(profilefile) + app.Peer.Save() quit = true case "new-profile": - if len(commands) == 3 { + if len(commands) == 2 { fmt.Print("** WARNING: PASSWORDS CANNOT BE RECOVERED! **\n") password := "" @@ -209,8 +223,7 @@ func main() { if failcount >= 3 { fmt.Printf("Error creating profile for %v: Your password entries must match!\n", commands[1]) } else { - err := app.NewProfile(commands[1], commands[2], password) - profilefile = commands[2] + err := app.NewProfile(commands[1], password) if err == nil { fmt.Printf("\nNew profile created for %v\n", commands[1]) } else { @@ -224,10 +237,9 @@ func main() { if len(commands) == 2 { fmt.Print("Enter a password to decrypt the profile: ") 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 { fmt.Printf("\nLoaded profile for %v\n", commands[1]) - profilefile = commands[1] } else { fmt.Printf("Error loading profile for %v: %v\n", commands[1], err) } @@ -288,7 +300,7 @@ func main() { if err != nil { fmt.Printf("Error: %v\n", err) } else { - app.Peer.Save(profilefile) + app.Peer.Save() group := app.Peer.GetGroup(groupID) if group == nil { fmt.Printf("Error: group does not exist\n") @@ -315,7 +327,7 @@ func main() { id, _, err := app.Peer.StartGroup(commands[1]) if err == nil { 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) if group == nil { 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"]) } case "save": - app.Peer.Save(profilefile) + app.Peer.Save() case "help": for _, command := range suggestions { 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 { - app.Peer.Save(profilefile) - } + if app.Peer != nil { + app.Peer.Save() } } diff --git a/connectivity/tor/tormanager.go b/connectivity/tor/tormanager.go index 63c1c83..3f4f4f8 100644 --- a/connectivity/tor/tormanager.go +++ b/connectivity/tor/tormanager.go @@ -8,7 +8,9 @@ import ( "net" "net/http" "net/url" + "os" "os/exec" + "path" "strings" "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. -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.socksPort = socksPort torManager.controlPort = controlPort + err := torManager.TestConnection() if err == nil { @@ -33,16 +36,30 @@ func NewTorManager(socksPort int, controlPort int, torrc string) (*Manager, erro } // 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") err = cmd.Start() if err != nil { log.Printf("starting tor failed %v", err) return nil, err } - time.Sleep(time.Second * 5) 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 } diff --git a/connectivity/tor/tormanager_test.go b/connectivity/tor/tormanager_test.go index 80a5c85..6081ae8 100644 --- a/connectivity/tor/tormanager_test.go +++ b/connectivity/tor/tormanager_test.go @@ -3,21 +3,25 @@ package tor import ( "fmt" "os" + "os/exec" "testing" ) func TestTorManager(t *testing.T) { - + tor, err := exec.LookPath("tor") + if err != nil { + t.Errorf("tor not found in PATH") + } os.Remove("/tmp/torrc") file, _ := os.Create("/tmp/torrc") fmt.Fprintf(file, "SOCKSPort %d\nControlPort %d\nDataDirectory /tmp/tor\n", 10050, 10051) file.Close() - tm, err := NewTorManager(10050, 10051, "/tmp/torrc") + tm, err := NewTorManager(10050, 10051, tor, "/tmp/torrc") if err != nil { t.Errorf("creating a new tor manager failed: %v", err) } else { - tm2, err := NewTorManager(10050, 10051, "/tmp/torrc") + tm2, err := NewTorManager(10050, 10051, tor, "/tmp/torrc") if err != nil { t.Errorf("creating a new tor manager failed: %v", err) } diff --git a/peer/cwtch_peer.go b/peer/cwtch_peer.go index b4d50c6..5aaab02 100644 --- a/peer/cwtch_peer.go +++ b/peer/cwtch_peer.go @@ -43,7 +43,7 @@ type cwtchPeer struct { // CwtchPeerInterface provides us with a way of testing systems built on top of cwtch without having to // directly implement a cwtchPeer. type CwtchPeerInterface interface { - Save(string) error + Save() error PeerWithOnion(string) InviteOnionToGroup(string, string) error @@ -145,8 +145,9 @@ func (cp *cwtchPeer) setup() { } // 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.profilefile = profilefile cp.Profile = model.GenerateNewProfile(name) cp.setup() key, salt := createKey(password) @@ -156,14 +157,13 @@ func NewCwtchPeer(name string, password string) CwtchPeerInterface { } // Save saves the cwtchPeer profile state to a file. -func (cp *cwtchPeer) Save(profilefile string) error { +func (cp *cwtchPeer) Save() error { cp.mutex.Lock() encryptedbytes := encryptProfile(cp, cp.key) // the salt for the derived key is appended to the front of the file encryptedbytes = append(cp.salt[:], encryptedbytes...) - err := ioutil.WriteFile(profilefile, encryptedbytes, 0600) - cp.profilefile = profilefile + err := ioutil.WriteFile(cp.profilefile, encryptedbytes, 0600) cp.mutex.Unlock() return err } @@ -171,7 +171,6 @@ func (cp *cwtchPeer) Save(profilefile string) error { // LoadCwtchPeer loads an existing cwtchPeer from a file. func LoadCwtchPeer(profilefile string, password string) (CwtchPeerInterface, error) { encryptedbytes, err := ioutil.ReadFile(profilefile) - if err == nil { var dkr [32]byte var salty [128]byte @@ -184,7 +183,8 @@ func LoadCwtchPeer(profilefile string, password string) (CwtchPeerInterface, err copy(dkr[:], dk) copy(salty[:], salt) - cp, err := decryptProfile(encryptedbytes, dkr) + var cp *cwtchPeer + cp, err = decryptProfile(encryptedbytes, dkr) if err == nil { cp.setup() cp.profilefile = profilefile @@ -429,7 +429,7 @@ type CwtchPeerHandler struct { func (cph *CwtchPeerHandler) ClientIdentity(ci *protocol.CwtchIdentity) { log.Printf("Received Client Identity from %v %v\n", cph.Onion, ci.String()) cph.Peer.Profile.AddCwtchIdentity(cph.Onion, ci) - cph.Peer.Save(cph.Peer.profilefile) + cph.Peer.Save() } // HandleGroupInvite handles incoming GroupInvites diff --git a/peer/cwtch_peer_test.go b/peer/cwtch_peer_test.go index 21d9578..02d404e 100644 --- a/peer/cwtch_peer_test.go +++ b/peer/cwtch_peer_test.go @@ -6,10 +6,10 @@ import ( func TestCwtchPeerGenerate(t *testing.T) { - alice := NewCwtchPeer("alice", "testpass") - alice.Save("./test_profile") + alice := NewCwtchPeer("alice", "testpass", "./alice.json") + alice.Save() - aliceLoaded, err := LoadCwtchPeer("./test_profile", "testpass") + aliceLoaded, err := LoadCwtchPeer("./alice.json", "testpass") if err != nil || aliceLoaded.GetProfile().Name != "alice" { 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) { groupName := "test.server" - alice := NewCwtchPeer("alice", "alicepass") - bob := NewCwtchPeer("bob", "bobpass") + alice := NewCwtchPeer("alice", "alicepass", "") + bob := NewCwtchPeer("bob", "bobpass", "") bobOnion := bob.GetProfile().Onion aliceOnion := alice.GetProfile().Onion diff --git a/testing/cwtch_peer_server_intergration_test.go b/testing/cwtch_peer_server_intergration_test.go index 8f35541..bb9d5cd 100644 --- a/testing/cwtch_peer_server_intergration_test.go +++ b/testing/cwtch_peer_server_intergration_test.go @@ -143,17 +143,17 @@ func TestCwtchPeerIntegration(t *testing.T) { // ***** Peer setup ***** fmt.Println("Creating Alice...") - alice := peer.NewCwtchPeer("Alice", "alicepass") + alice := peer.NewCwtchPeer("Alice", "alicepass", "") go alice.Listen() fmt.Println("Alice created:", alice.GetProfile().Onion) fmt.Println("Creating Bob...") - bob := peer.NewCwtchPeer("Bob", "bobpass") + bob := peer.NewCwtchPeer("Bob", "bobpass", "") go bob.Listen() fmt.Println("Bob created:", bob.GetProfile().Onion) fmt.Println("Creating Carol...") - carol := peer.NewCwtchPeer("Carol", "carolpass") + carol := peer.NewCwtchPeer("Carol", "carolpass", "") go carol.Listen() fmt.Println("Carol created:", carol.GetProfile().Onion)