Compare commits

...

14 Commits

15 changed files with 122 additions and 29 deletions

View File

@ -5,27 +5,35 @@ workspace:
pipeline: pipeline:
fetch: fetch:
image: golang image: golang
environment:
- GO111MODULE=on
commands: commands:
- wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/tor - wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/tor
- wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/torrc - wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/torrc
- chmod a+x tor - chmod a+x tor
- go list ./... | xargs go get - go get -u golang.org/x/lint/golint
- go get -u github.com/golang/lint/golint - go mod download
quality: quality:
image: golang image: golang
environment:
- GO111MODULE=on
commands: commands:
- go list ./... | xargs go vet - go list ./... | xargs go vet
- go list ./... | grep -v "/wire/" | xargs golint -set_exit_status - go list ./... | grep -v "/wire/" | xargs golint -set_exit_status
units-tests: units-tests:
image: golang image: golang
environment:
- GO111MODULE=on
commands: commands:
- sh testing/tests.sh - sh testing/tests.sh
integ-test: integ-test:
image: golang image: golang
environment:
- GO111MODULE=on
commands: commands:
- ./tor -f ./torrc - ./tor -f ./torrc
- sleep 15 - sleep 15
- go test -v git.openprivacy.ca/openprivacy/libricochet-go/testing - go test -race -v git.openprivacy.ca/openprivacy/libricochet-go/testing
notify-email: notify-email:
image: drillster/drone-email image: drillster/drone-email
host: build.openprivacy.ca host: build.openprivacy.ca

View File

@ -144,7 +144,9 @@ func (ra *RicochetApplication) Run(ls connectivity.ListenService) {
if !ra.v3identity.Initialized() || ra.contactManager == nil { if !ra.v3identity.Initialized() || ra.contactManager == nil {
return return
} }
ra.lock.Lock()
ra.ls = ls ra.ls = ls
ra.lock.Unlock()
var err error var err error
for err == nil { for err == nil {
conn, err := ra.ls.Accept() conn, err := ra.ls.Accept()

View File

@ -32,8 +32,9 @@ type Connection struct {
messageBuilder utils.MessageBuilder messageBuilder utils.MessageBuilder
closed bool closed bool
closing bool closingLock sync.Mutex
closing bool
// This mutex is exclusively for preventing races during blocking // This mutex is exclusively for preventing races during blocking
// interactions with Process; specifically Do and Break. Don't use // interactions with Process; specifically Do and Break. Don't use
// it for anything else. See those functions for an explanation. // it for anything else. See those functions for an explanation.
@ -311,6 +312,8 @@ func (rc *Connection) Process(handler Handler) error {
go func() { go func() {
rc.processBlockMutex.Lock() rc.processBlockMutex.Lock()
defer rc.processBlockMutex.Unlock() defer rc.processBlockMutex.Unlock()
rc.closingLock.Lock()
defer rc.closingLock.Unlock()
rc.closed = true rc.closed = true
close(closedChan) close(closedChan)
}() }()
@ -468,5 +471,7 @@ func (rc *Connection) Close() {
// Kill the Ricochet Connection. // Kill the Ricochet Connection.
log.Debugf("Closing Ricochet Connection for %v", rc.RemoteHostname) log.Debugf("Closing Ricochet Connection for %v", rc.RemoteHostname)
rc.conn.Close() rc.conn.Close()
rc.closingLock.Lock()
rc.closed = true rc.closed = true
rc.closingLock.Unlock()
} }

View File

@ -53,6 +53,9 @@ func TestProcessAuthAs3DHServer(t *testing.T) {
t.Errorf("Error while testing ProcessAuthAsServer: %v", err) t.Errorf("Error while testing ProcessAuthAsServer: %v", err)
} }
// Wait for server to finish
time.Sleep(time.Second * 2)
// Test Close // Test Close
rc.Close() rc.Close()
} }

View File

@ -28,6 +28,9 @@ type ACN interface {
// Sets the calback function to be called when ACN status changes // Sets the calback function to be called when ACN status changes
SetStatusCallback(callback func(int, string)) SetStatusCallback(callback func(int, string))
// Restarts the underlying connection
Restart()
// Open takes a hostname and returns a net.conn to the derived endpoint // Open takes a hostname and returns a net.conn to the derived endpoint
// Open allows a client to resolve various hostnames to connections // Open allows a client to resolve various hostnames to connections
// The supported types are onions address are: // The supported types are onions address are:

View File

@ -63,6 +63,10 @@ func (lp *localProvider) Open(hostname string) (net.Conn, string, error) {
} }
func (lp *localProvider) Restart() {
//noop
}
func (lp *localProvider) Close() { func (lp *localProvider) Close() {
} }

View File

@ -0,0 +1,9 @@
// +build !windows
package connectivity
import (
"syscall"
)
var sysProcAttr = &syscall.SysProcAttr{}

View File

@ -0,0 +1,9 @@
// +build windows
package connectivity
import (
"syscall"
)
var sysProcAttr = &syscall.SysProcAttr{HideWindow: true}

View File

@ -1,10 +1,12 @@
package connectivity package connectivity
import ( import (
"context"
"errors" "errors"
"git.openprivacy.ca/openprivacy/libricochet-go/log" "git.openprivacy.ca/openprivacy/libricochet-go/log"
"git.openprivacy.ca/openprivacy/libricochet-go/utils" "git.openprivacy.ca/openprivacy/libricochet-go/utils"
"github.com/cretz/bine/control" "github.com/cretz/bine/control"
"github.com/cretz/bine/process"
"github.com/cretz/bine/tor" "github.com/cretz/bine/tor"
bineed255192 "github.com/cretz/bine/torutil/ed25519" bineed255192 "github.com/cretz/bine/torutil/ed25519"
"golang.org/x/crypto/ed25519" "golang.org/x/crypto/ed25519"
@ -42,6 +44,7 @@ type onionListenService struct {
type torProvider struct { type torProvider struct {
t *tor.Tor t *tor.Tor
dialer *tor.Dialer
appDirectory string appDirectory string
bundeledTorPath string bundeledTorPath string
lock sync.Mutex lock sync.Mutex
@ -109,6 +112,9 @@ func (tp *torProvider) Listen(identity PrivateKey, port int) (ListenService, err
var onion = "" var onion = ""
var privkey ed25519.PrivateKey var privkey ed25519.PrivateKey
tp.lock.Lock()
defer tp.lock.Unlock()
if tp.t == nil { if tp.t == nil {
return nil, errors.New("Tor Provider closed") return nil, errors.New("Tor Provider closed")
} }
@ -131,14 +137,8 @@ func (tp *torProvider) Listen(identity PrivateKey, port int) (ListenService, err
localport += 1024 localport += 1024
} }
if tp.t == nil {
return nil, errors.New("Tor is offline")
}
localListener, err := net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(localport)) localListener, err := net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(localport))
tp.lock.Lock()
defer tp.lock.Unlock()
conf := &tor.ListenConf{NoWait: true, Version3: true, Key: identity, RemotePorts: []int{port}, Detach: true, DiscardKey: true, LocalListener: localListener} conf := &tor.ListenConf{NoWait: true, Version3: true, Key: identity, RemotePorts: []int{port}, Detach: true, DiscardKey: true, LocalListener: localListener}
os, err := tp.t.Listen(nil, conf) os, err := tp.t.Listen(nil, conf)
if err != nil && strings.Contains(err.Error(), "550 Unspecified Tor error: Onion address collision") { if err != nil && strings.Contains(err.Error(), "550 Unspecified Tor error: Onion address collision") {
@ -157,24 +157,29 @@ func (tp *torProvider) Listen(identity PrivateKey, port int) (ListenService, err
return ols, nil return ols, nil
} }
func (tp *torProvider) Restart() {
if tp.statusCallback != nil {
tp.statusCallback(0, "rebooting")
}
tp.restart()
}
func (tp *torProvider) Open(hostname string) (net.Conn, string, error) { func (tp *torProvider) Open(hostname string) (net.Conn, string, error) {
tp.lock.Lock() tp.lock.Lock()
defer tp.lock.Unlock()
if tp.t == nil { if tp.t == nil {
tp.lock.Unlock()
return nil, hostname, errors.New("Tor is offline") return nil, hostname, errors.New("Tor is offline")
} }
torDailer, err := tp.t.Dialer(nil, &tor.DialConf{}) tp.lock.Unlock()
if err != nil {
return nil, "", err
}
resolvedHostname := hostname resolvedHostname := hostname
if strings.HasPrefix(hostname, "ricochet:") { if strings.HasPrefix(hostname, "ricochet:") {
addrParts := strings.Split(hostname, ":") addrParts := strings.Split(hostname, ":")
resolvedHostname = addrParts[1] resolvedHostname = addrParts[1]
} }
conn, err := torDailer.Dial("tcp", resolvedHostname+".onion:9878") conn, err := tp.dialer.Dial("tcp", resolvedHostname+".onion:9878")
return conn, resolvedHostname, err return conn, resolvedHostname, err
} }
@ -202,11 +207,25 @@ func (tp *torProvider) SetStatusCallback(callback func(int, string)) {
func StartTor(appDirectory string, bundledTorPath string) (ACN, error) { func StartTor(appDirectory string, bundledTorPath string) (ACN, error) {
tp, err := startTor(appDirectory, bundledTorPath) tp, err := startTor(appDirectory, bundledTorPath)
if err == nil { if err == nil {
go tp.monitorRestart() tp.dialer, err = tp.t.Dialer(nil, &tor.DialConf{})
if err == nil {
go tp.monitorRestart()
}
} }
return tp, err return tp, err
} }
// newHideCmd creates a Creator function for bine which generates a cmd that one windows will hide the dosbox
func newHideCmd(exePath string) process.Creator {
return process.CmdCreatorFunc(func(ctx context.Context, args ...string) (*exec.Cmd, error) {
cmd := exec.CommandContext(ctx, exePath, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.SysProcAttr = sysProcAttr
return cmd, nil
})
}
func startTor(appDirectory string, bundledTorPath string) (*torProvider, error) { func startTor(appDirectory string, bundledTorPath string) (*torProvider, error) {
dataDir := path.Join(appDirectory, "tor") dataDir := path.Join(appDirectory, "tor")
os.MkdirAll(dataDir, 0700) os.MkdirAll(dataDir, 0700)
@ -233,7 +252,7 @@ func startTor(appDirectory string, bundledTorPath string) (*torProvider, error)
// 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}) t, err := tor.Start(nil, &tor.StartConf{EnableNetwork: true, DataDir: dataDir, DebugWriter: nil, ProcessCreator: newHideCmd("tor")})
if err == nil { if err == nil {
tp.t = t tp.t = t
return tp, nil return tp, nil
@ -244,7 +263,7 @@ func startTor(appDirectory string, bundledTorPath string) (*torProvider, error)
// 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}) t, err := tor.Start(nil, &tor.StartConf{EnableNetwork: true, DataDir: dataDir, 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)
} }
@ -308,6 +327,7 @@ func (tp *torProvider) restart() {
newTp, err := startTor(tp.appDirectory, tp.bundeledTorPath) newTp, err := startTor(tp.appDirectory, tp.bundeledTorPath)
if err == nil { if err == nil {
tp.t = newTp.t tp.t = newTp.t
tp.dialer, _ = tp.t.Dialer(nil, &tor.DialConf{})
return return
} }
} }
@ -334,6 +354,7 @@ func createFromExisting(controlport *control.Conn, datadir string) *tor.Tor {
func checkCmdlineTorVersion(torCmd string) bool { func checkCmdlineTorVersion(torCmd string) bool {
cmd := exec.Command(torCmd, "--version") cmd := exec.Command(torCmd, "--version")
cmd.SysProcAttr = sysProcAttr
out, err := cmd.CombinedOutput() out, err := cmd.CombinedOutput()
re := regexp.MustCompile("[0-1]\\.[0-9]\\.[0-9]\\.[0-9]") re := regexp.MustCompile("[0-1]\\.[0-9]\\.[0-9]\\.[0-9]")
sysTorVersion := re.Find(out) sysTorVersion := re.Find(out)

View File

@ -15,10 +15,11 @@ func getStatusCallback(progChan chan int) func(int, string) {
func TestTorProvider(t *testing.T) { func TestTorProvider(t *testing.T) {
progChan := make(chan int) progChan := make(chan int)
acn, err := StartTor(".", "") acn, err := StartTor(".", "")
acn.SetStatusCallback(getStatusCallback(progChan))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return
} }
acn.SetStatusCallback(getStatusCallback(progChan))
progress := 0 progress := 0
for progress < 100 { for progress < 100 {

4
go.mod
View File

@ -2,10 +2,12 @@ module git.openprivacy.ca/openprivacy/libricochet-go
require ( require (
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412
github.com/cretz/bine v0.1.0 github.com/cretz/bine v0.1.1-0.20200124154328-f9f678b84cca
github.com/golang/protobuf v1.2.0 github.com/golang/protobuf v1.2.0
github.com/stretchr/testify v1.3.0 // indirect github.com/stretchr/testify v1.3.0 // indirect
golang.org/x/crypto v0.0.0-20190128193316-c7b33c32a30b golang.org/x/crypto v0.0.0-20190128193316-c7b33c32a30b
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 // indirect golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 // indirect
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect
) )
go 1.13

2
go.sum
View File

@ -2,6 +2,8 @@ github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7I
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
github.com/cretz/bine v0.1.0 h1:1/fvhLE+fk0bPzjdO5Ci+0ComYxEMuB1JhM4X5skT3g= github.com/cretz/bine v0.1.0 h1:1/fvhLE+fk0bPzjdO5Ci+0ComYxEMuB1JhM4X5skT3g=
github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw= github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
github.com/cretz/bine v0.1.1-0.20200124154328-f9f678b84cca h1:Q2r7AxHdJwWfLtBZwvW621M3sPqxPc6ITv2j1FGsYpw=
github.com/cretz/bine v0.1.1-0.20200124154328-f9f678b84cca/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=

View File

@ -39,8 +39,23 @@ func New(level Level) *Logger {
return &Logger{logger: golog.New(os.Stderr, "", golog.Ldate|golog.Ltime), level: level, everythingFromPatterns: make([]string, 0), nothingExceptPatterns: make([]string, 0)} return &Logger{logger: golog.New(os.Stderr, "", golog.Ldate|golog.Ltime), level: level, everythingFromPatterns: make([]string, 0), nothingExceptPatterns: make([]string, 0)}
} }
// NewFile returns a new Logger that logs to the supplied file with a filter set to the supplied level
func NewFile(level Level, filename string) (*Logger, error) {
logfile, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
if err != nil {
return nil, err
}
return &Logger{logger: golog.New(logfile, "", golog.Ldate|golog.Ltime), level: level, everythingFromPatterns: make([]string, 0), nothingExceptPatterns: make([]string, 0)}, nil
}
var std = New(LevelWarn) var std = New(LevelWarn)
// SetStd sets the default logger all other functions use
func SetStd(logger *Logger) {
std = logger
}
// filter // filter
func (l *Logger) filter(level Level) bool { func (l *Logger) filter(level Level) bool {

View File

@ -4,13 +4,14 @@
set -e set -e
pwd pwd
go test ${1} -coverprofile=utils.cover.out -v ./utils GORACE="haltonerror=1"
go test ${1} -coverprofile=channels.cover.out -v ./channels go test -race ${1} -coverprofile=utils.cover.out -v ./utils
go test ${1} -coverprofile=channels.v3.inbound.cover.out -v ./channels/v3/inbound go test -race ${1} -coverprofile=channels.cover.out -v ./channels
go test ${1} -coverprofile=connection.cover.out -v ./connection go test -race ${1} -coverprofile=channels.v3.inbound.cover.out -v ./channels/v3/inbound
go test ${1} -coverprofile=policies.cover.out -v ./policies go test -race ${1} -coverprofile=connection.cover.out -v ./connection
go test ${1} -coverprofile=identity.cover.out -v ./identity go test -race ${1} -coverprofile=policies.cover.out -v ./policies
go test ${1} -coverprofile=root.cover.out -v ./ go test -race ${1} -coverprofile=identity.cover.out -v ./identity
go test -race ${1} -coverprofile=root.cover.out -v ./
echo "mode: set" > coverage.out && cat *.cover.out | grep -v mode: | sort -r | \ echo "mode: set" > coverage.out && cat *.cover.out | grep -v mode: | sort -r | \
awk '{if($1 != last) {print $0;last=$1}}' >> coverage.out awk '{if($1 != last) {print $0;last=$1}}' >> coverage.out
rm -rf *.cover.out rm -rf *.cover.out

View File

@ -8,6 +8,7 @@ import (
"git.openprivacy.ca/openprivacy/libricochet-go/log" "git.openprivacy.ca/openprivacy/libricochet-go/log"
"golang.org/x/crypto/nacl/secretbox" "golang.org/x/crypto/nacl/secretbox"
"io" "io"
"sync"
) )
const ( const (
@ -43,10 +44,13 @@ type RicochetNetwork struct {
// Derived ephemeral session key for connection // Derived ephemeral session key for connection
key [32]byte key [32]byte
encrypt bool encrypt bool
lock sync.Mutex
} }
// SetEncryptionKey sets the ephemeral encryption key for this session. // SetEncryptionKey sets the ephemeral encryption key for this session.
func (rn *RicochetNetwork) SetEncryptionKey(key [32]byte) { func (rn *RicochetNetwork) SetEncryptionKey(key [32]byte) {
rn.lock.Lock()
defer rn.lock.Unlock()
log.Debugf("turning on ephemeral session encryption for connection") log.Debugf("turning on ephemeral session encryption for connection")
copy(rn.key[:], key[:]) copy(rn.key[:], key[:])
@ -67,6 +71,7 @@ func (rn *RicochetNetwork) SendRicochetPacket(dst io.Writer, channel int32, data
binary.BigEndian.PutUint16(packet[2:4], uint16(channel)) binary.BigEndian.PutUint16(packet[2:4], uint16(channel))
copy(packet[4:], data[:]) copy(packet[4:], data[:])
rn.lock.Lock()
if rn.encrypt { if rn.encrypt {
var nonce [24]byte var nonce [24]byte
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
@ -77,6 +82,7 @@ func (rn *RicochetNetwork) SendRicochetPacket(dst io.Writer, channel int32, data
binary.BigEndian.PutUint16(packet[0:2], uint16(len(encrypted)+2)) binary.BigEndian.PutUint16(packet[0:2], uint16(len(encrypted)+2))
packet = append(packet[0:2], encrypted...) packet = append(packet[0:2], encrypted...)
} }
rn.lock.Unlock()
for pos := 0; pos < len(packet); { for pos := 0; pos < len(packet); {
n, err := dst.Write(packet[pos:]) n, err := dst.Write(packet[pos:])
@ -112,6 +118,7 @@ func (rn *RicochetNetwork) RecvRicochetPacket(reader io.Reader) (RicochetData, e
return packet, err return packet, err
} }
rn.lock.Lock()
if rn.encrypt { if rn.encrypt {
var decryptNonce [24]byte var decryptNonce [24]byte
if len(packetBytes) > 24 { if len(packetBytes) > 24 {
@ -127,6 +134,7 @@ func (rn *RicochetNetwork) RecvRicochetPacket(reader io.Reader) (RicochetData, e
return packet, errors.New("ciphertext length was too short") return packet, errors.New("ciphertext length was too short")
} }
} }
rn.lock.Unlock()
packet.Channel = int32(binary.BigEndian.Uint16(packetBytes[0:2])) packet.Channel = int32(binary.BigEndian.Uint16(packetBytes[0:2]))
packet.Data = make([]byte, len(packetBytes)-2) packet.Data = make([]byte, len(packetBytes)-2)