diff --git a/.github/workflows/create_bins.yml b/.github/workflows/create_bins.yml index 8e107a57..ad765462 100644 --- a/.github/workflows/create_bins.yml +++ b/.github/workflows/create_bins.yml @@ -30,3 +30,32 @@ jobs: with: name: kr-${{ matrix.os }}.tar.gz path: kr-${{ matrix.os }}.tar.gz + + + buildwin: + name: Build Win + runs-on: windows-latest + + steps: + - name: Set up Go 1.13 + uses: actions/setup-go@v1 + with: + go-version: 1.13 + id: go + + - name: Check out code into the Go module directory + uses: actions/checkout@v1 + + - name: build + run: build.bat + shell: cmd + + - name: zip + run: Compress-Archive -Path bin\* -DestinationPath kr-windows-latest.zip + shell: powershell + + - name: upload + uses: actions/upload-artifact@master + with: + name: kr-windows-latest.zip + path: kr-windows-latest.zip diff --git a/build.bat b/build.bat new file mode 100644 index 00000000..c06b355f --- /dev/null +++ b/build.bat @@ -0,0 +1,6 @@ +mkdir bin +cd src +go.exe build -v -trimpath -o ../bin/kr.exe ./kr +go.exe build -v -trimpath -o ../bin/krd.exe ./krd +go.exe build -v -trimpath -o ../bin/krssh.exe ./krssh +cd .. diff --git a/src/common/analytics/analytics_ua_windows.go b/src/common/analytics/analytics_ua_windows.go new file mode 100644 index 00000000..ce300d47 --- /dev/null +++ b/src/common/analytics/analytics_ua_windows.go @@ -0,0 +1,27 @@ +package analytics + +import ( + "fmt" + . "krypt.co/kr/common/version" + "sync" +) + +// TODO +var analytics_user_agent = fmt.Sprintf("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Version/%s kr/%s", CURRENT_VERSION, CURRENT_VERSION) + +const analytics_os = "Windows" + +var cachedAnalyticsOSVersion *string +var osVersionMutex sync.Mutex + +func getAnalyticsOSVersion() *string { + osVersionMutex.Lock() + defer osVersionMutex.Unlock() + if cachedAnalyticsOSVersion != nil { + return cachedAnalyticsOSVersion + } + + //TODO: find system way to get version + // for now just use a constant here + return "WindowsOS" +} diff --git a/src/common/log/logging.go b/src/common/log/logging.go index 441a9d5b..883ddb4f 100644 --- a/src/common/log/logging.go +++ b/src/common/log/logging.go @@ -1,3 +1,5 @@ +// +build !windows + package log import ( diff --git a/src/common/log/logging_windows.go b/src/common/log/logging_windows.go new file mode 100644 index 00000000..8fdbd7e0 --- /dev/null +++ b/src/common/log/logging_windows.go @@ -0,0 +1,77 @@ +package log + +import ( + "os" + + "github.com/op/go-logging" + + . "krypt.co/kr/common/socket" +) + +var Log = logging.MustGetLogger("") +var syslogFormat = logging.MustStringFormatter( + `%{time:15:04:05.000} %{level:.6s} ▶ %{message}`, +) +var stderrFormat = logging.MustStringFormatter( + `%{color}Krypton ▶ %{message}%{color:reset}`, +) + +func SetupLogging(prefix string, defaultLogLevel logging.Level, trySyslog bool) *logging.Logger { + var backend logging.Backend + /* + if trySyslog { + var err error + backend, err = logging.NewSyslogBackendPriority(prefix, syslog.LOG_NOTICE) + if err == nil { + logging.SetFormatter(syslogFormat) + // direct panic output to syslog as well + if syslogBackend, ok := backend.(*logging.SyslogBackend); ok { + stdlog.SetOutput(syslogBackend.Writer) + } + } else { + backend = nil + } + + } + */ + if backend == nil { + var err error + var file *os.File + logName := prefix + if logName == "" { + logName = "kr" + } + logName += ".log" + path, err := KrDirFile(logName) + if err != nil { + file = os.Stderr + } else { + file, err = os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) + if err != nil { + file = os.Stderr + } + } + backend = logging.NewLogBackend(file, prefix, 0) + backend = logging.NewBackendFormatter(backend, stderrFormat) + } + leveled := logging.AddModuleLevel(backend) + switch os.Getenv("KR_LOG_LEVEL") { + case "CRITICAL": + leveled.SetLevel(logging.CRITICAL, prefix) + case "ERROR": + leveled.SetLevel(logging.ERROR, prefix) + case "WARNING": + leveled.SetLevel(logging.WARNING, prefix) + case "NOTICE": + leveled.SetLevel(logging.NOTICE, prefix) + case "INFO": + leveled.SetLevel(logging.INFO, prefix) + case "DEBUG": + leveled.SetLevel(logging.DEBUG, prefix) + default: + leveled.SetLevel(defaultLogLevel, prefix) + } + + logging.SetBackend(leveled) + return Log +} diff --git a/src/common/socket/socket.go b/src/common/socket/socket.go index b0f3e0ec..2c596b86 100644 --- a/src/common/socket/socket.go +++ b/src/common/socket/socket.go @@ -80,7 +80,7 @@ func KrDirFile(file string) (fullPath string, err error) { const AGENT_SOCKET_FILENAME = "krd-agent.sock" -func AgentListen() (listener net.Listener, err error) { +func AgentListenUnix() (listener net.Listener, err error) { socketPath, err := KrDirFile(AGENT_SOCKET_FILENAME) if err != nil { return @@ -177,8 +177,3 @@ func DaemonSocketOrFatal() (unixFile string) { } return } - -func IsKrdRunning() bool { - err := exec.Command("pgrep", "-U", User(), "krd").Run() - return nil == err -} diff --git a/src/common/socket/socket_darwin.go b/src/common/socket/socket_darwin.go index 1676cf93..291ca11c 100644 --- a/src/common/socket/socket_darwin.go +++ b/src/common/socket/socket_darwin.go @@ -3,6 +3,7 @@ package socket import ( "fmt" "net" + "os/exec" ) func DaemonDial(unixFile string) (conn net.Conn, err error) { @@ -12,3 +13,12 @@ func DaemonDial(unixFile string) (conn net.Conn, err error) { } return } + +func IsKrdRunning() bool { + err := exec.Command("pgrep", "-U", User(), "krd").Run() + return nil == err +} + +func AgentListen() (listener net.Listener, err error) { + return AgentListenUnix() +} diff --git a/src/common/socket/socket_unix.go b/src/common/socket/socket_unix.go index b8ca569b..7a46fe07 100644 --- a/src/common/socket/socket_unix.go +++ b/src/common/socket/socket_unix.go @@ -1,4 +1,4 @@ -// +build !darwin +// +build !darwin,!windows package socket @@ -37,3 +37,12 @@ func KillKrd() { exec.Command("pkill", "-U", User(), "-x", "krd").Run() <-time.After(1*time.Second) } + +func IsKrdRunning() bool { + err := exec.Command("pgrep", "-U", User(), "krd").Run() + return nil == err +} + +func AgentListen() (listener net.Listener, err error) { + return AgentListenUnix() +} diff --git a/src/common/socket/socket_windows.go b/src/common/socket/socket_windows.go new file mode 100644 index 00000000..81098ffc --- /dev/null +++ b/src/common/socket/socket_windows.go @@ -0,0 +1,72 @@ +// +build windows + +package socket + +import ( + "bytes" + "fmt" + "github.com/Microsoft/go-winio" + "krypt.co/kr/common/util" + "net" + "os" + "os/exec" + "path/filepath" + "time" +) + +const AGENT_PIPE = "\\\\.\\pipe\\krd-agent" + +func AgentListen() (listener net.Listener, err error) { + listener, err = winio.ListenPipe(AGENT_PIPE, nil) + return +} + +// TODO too much repeating... +func getPrefix() (string, error) { + if ex, err := os.Executable(); err == nil { + return filepath.Dir(ex), nil + } else { + return "", err + } +} + +func DaemonDial(unixFile string) (conn net.Conn, err error) { + if !IsKrdRunning() { + os.Stderr.WriteString(util.Yellow("Krypton ▶ Restarting krd...\r\n")) + exe := "krd.exe" + if pfx, err := getPrefix(); err == nil { + exe = pfx + `\krd.exe` + } + _ = exec.Command(exe).Start() + <-time.After(1 * time.Second) + } + conn, err = net.Dial("unix", unixFile) + /* + TODO + if err != nil { + // restart then try again + os.Stderr.WriteString(Yellow("Krypton ▶ Restarting krd...\r\n")) + KillKrd() + exec.Command("nohup", "krd").Start() + <-time.After(1 * time.Second) + conn, err = net.Dial("unix", unixFile) + } + */ + if err != nil { + err = fmt.Errorf("Failed to connect to Krypton daemon. Please make sure it is running by typing \"kr restart\".") + } + return +} + +func KillKrd() { + _ = exec.Command("taskkill", "/F", "/FI", `USERNAME eq ` + User(), "/IM", "krd.exe").Run() + <-time.After(1*time.Second) +} + +func IsKrdRunning() bool { + cmd := exec.Command("tasklist", "/FI", `USERNAME eq ` + User(), "/FI", `IMAGENAME eq krd.exe`) + if ret, err := cmd.CombinedOutput(); err == nil { + return bytes.Contains(ret, []byte("krd.exe")) + } + return false +} diff --git a/src/daemon/control/server_test.go b/src/daemon/control/server_test.go index 65221cd3..d991153d 100644 --- a/src/daemon/control/server_test.go +++ b/src/daemon/control/server_test.go @@ -8,20 +8,24 @@ import ( "testing" "time" - "github.com/kryptco/kr" "github.com/op/go-logging" + . "krypt.co/kr/daemon/enclave" + . "krypt.co/kr/common/transport" + . "krypt.co/kr/common/log" + . "krypt.co/kr/common/protocol" + . "krypt.co/kr/common/util" ) func NewTestControlServer(ec EnclaveClientI) *ControlServer { - return &ControlServer{ec, kr.SetupLogging("test", logging.INFO, false)} + return &ControlServer{ec, SetupLogging("test", logging.INFO, false)} } func TestControlServerPair(t *testing.T) { - transport := &kr.ResponseTransport{T: t} + transport := &ResponseTransport{T: t} ec := NewTestEnclaveClient(transport) cs := NewTestControlServer(ec) - var pairingOptions kr.PairingOptions + var pairingOptions PairingOptions var body, err = json.Marshal(pairingOptions) if err != nil { t.Fatal(err) @@ -37,7 +41,7 @@ func TestControlServerPair(t *testing.T) { if resp.StatusCode != http.StatusOK { t.Fatal("non-200 status") } - var pairingSecret kr.PairingSecret + var pairingSecret PairingSecret err = json.NewDecoder(resp.Body).Decode(&pairingSecret) if err != nil { t.Fatal(err) @@ -53,22 +57,22 @@ func TestControlServerPair(t *testing.T) { if resp.StatusCode != http.StatusOK { t.Fatal("non-200 status") } - var me kr.Profile + var me Profile err = json.NewDecoder(resp.Body).Decode(&me) if err != nil { t.Fatal(err) } - testMe, _, _ := kr.TestMe(t) + testMe, _, _ := TestMe(t) if !me.Equal(testMe) { t.Fatal("paired profile wrong") } } func TestControlServerUnpair(t *testing.T) { - transport := &kr.ResponseTransport{T: t} + transport := &ResponseTransport{T: t} ec := NewTestEnclaveClientShortTimeouts(transport) cs := NewTestControlServer(ec) - var pairingOptions kr.PairingOptions + var pairingOptions PairingOptions var body, err = json.Marshal(pairingOptions) if err != nil { @@ -85,7 +89,7 @@ func TestControlServerUnpair(t *testing.T) { if resp.StatusCode != http.StatusOK { t.Fatal("non-200 status") } - var pairingSecret kr.PairingSecret + var pairingSecret PairingSecret err = json.NewDecoder(resp.Body).Decode(&pairingSecret) if err != nil { t.Fatal(err) @@ -118,14 +122,14 @@ func TestControlServerUnpair(t *testing.T) { } func TestControlServerMe(t *testing.T) { - transport := &kr.ResponseTransport{T: t} + transport := &ResponseTransport{T: t} ec := NewTestEnclaveClient(transport) cs := NewTestControlServer(ec) - request, err := kr.NewRequest() + request, err := NewRequest() if err != nil { t.Fatal(err) } - request.MeRequest = &kr.MeRequest{} + request.MeRequest = &MeRequest{} meRequest, err := request.HTTPRequest() if err != nil { @@ -152,29 +156,29 @@ func TestControlServerMe(t *testing.T) { t.Fatal("non-200 status") } - var meResponse kr.Response + var meResponse Response err = json.NewDecoder(resp.Body).Decode(&meResponse) if err != nil { t.Fatal(err) } - me, _, _ := kr.TestMe(t) + me, _, _ := TestMe(t) if !meResponse.MeResponse.Me.Equal(me) { t.Fatal("profiles unequal") } } func TestControlServerSign(t *testing.T) { - transport := &kr.ResponseTransport{T: t} + transport := &ResponseTransport{T: t} ec := NewTestEnclaveClient(transport) cs := NewTestControlServer(ec) - request, err := kr.NewRequest() + request, err := NewRequest() if err != nil { t.Fatal(err) } - me, _, _ := kr.TestMe(t) - data, err := kr.RandNBytes(32) - request.SignRequest = &kr.SignRequest{ + me, _, _ := TestMe(t) + data, err := RandNBytes(32) + request.SignRequest = &SignRequest{ PublicKeyFingerprint: me.PublicKeyFingerprint(), Data: data, } @@ -204,7 +208,7 @@ func TestControlServerSign(t *testing.T) { t.Fatal("non-200 status") } - var signResponse kr.Response + var signResponse Response err = json.NewDecoder(resp.Body).Decode(&signResponse) if err != nil { t.Fatal(err) @@ -212,7 +216,7 @@ func TestControlServerSign(t *testing.T) { } func TestControlServerPing(t *testing.T) { - transport := &kr.ResponseTransport{T: t} + transport := &ResponseTransport{T: t} ec := NewTestEnclaveClient(transport) cs := NewTestControlServer(ec) pingRequest, err := http.NewRequest("GET", "/ping", nil) @@ -228,13 +232,13 @@ func TestControlServerPing(t *testing.T) { } func TestControlServerNoOp(t *testing.T) { - transport := &kr.ResponseTransport{T: t} + transport := &ResponseTransport{T: t} ec := NewTestEnclaveClient(transport) cs := NewTestControlServer(ec) PairClient(t, ec) defer ec.Stop() - request, err := kr.NewRequest() + request, err := NewRequest() if err != nil { t.Fatal(err) } @@ -250,7 +254,7 @@ func TestControlServerNoOp(t *testing.T) { t.Fatal("expected 200") } - kr.TrueBefore(t, func() bool { + TrueBefore(t, func() bool { return transport.GetSentNoOps() > 0 }, time.Now().Add(time.Second)) } diff --git a/src/daemon/enclave/enclave.go b/src/daemon/enclave/enclave.go index 3adbe24e..10a9344a 100644 --- a/src/daemon/enclave/enclave.go +++ b/src/daemon/enclave/enclave.go @@ -1,7 +1,7 @@ package enclave /* -* Facillitates communication with a mobile phone SSH key enclave. +* Facilitates communication with a mobile phone SSH key enclave. */ import ( diff --git a/src/daemon/ssh_agent.go b/src/daemon/ssh_agent.go index a3a2f4f3..1de2546c 100644 --- a/src/daemon/ssh_agent.go +++ b/src/daemon/ssh_agent.go @@ -9,7 +9,6 @@ import ( "fmt" "krypt.co/kr/common/socket" "net" - "os" "strings" "sync" "time" @@ -33,12 +32,11 @@ type sessionIDSig struct { type hostAuthCallback chan *HostAuth -func (a *Agent) withOriginalAgent(do func(agent.Agent)) error { - originalAgentSock := os.Getenv("SSH_AUTH_SOCK") - if strings.HasSuffix(originalAgentSock, "krd-agent.sock") { +func (a *Agent) withOriginalAgent(do func(extendedAgent agent.ExtendedAgent)) error { + conn, err := getOriginalAgentConn() + if conn == nil { return nil } - conn, err := net.Dial("unix", originalAgentSock) if err != nil { a.log.Error("error connecting to fallbackAgent: " + err.Error()) return err @@ -81,7 +79,7 @@ func (a *Agent) List() (keys []*agent.Key, err error) { a.notify("", Yellow("Krypton ▶ "+ErrNotPaired.Error())) } - a.withOriginalAgent(func(fallbackAgent agent.Agent) { + a.withOriginalAgent(func(fallbackAgent agent.ExtendedAgent) { fallbackKeys, err := fallbackAgent.List() if err == nil { keys = append(keys, fallbackKeys...) @@ -94,14 +92,18 @@ func (a *Agent) List() (keys []*agent.Key, err error) { // Sign has the agent sign the data using a protocol 2 key as defined // in [PROTOCOL.agent] section 2.6.2. func (a *Agent) Sign(key ssh.PublicKey, data []byte) (sshSignature *ssh.Signature, err error) { + return a.SignWithFlags(key, data, 0) +} + +func (a *Agent) SignWithFlags(key ssh.PublicKey, data []byte, flags agent.SignatureFlags) (sshSignature *ssh.Signature, err error) { keyFingerprint := sha256.Sum256(key.Marshal()) - a.withOriginalAgent(func(fallbackAgent agent.Agent) { + a.withOriginalAgent(func(fallbackAgent agent.ExtendedAgent) { fallbackKeys, fallbackErr := fallbackAgent.List() if fallbackErr == nil { for _, fallbackKey := range fallbackKeys { if bytes.Equal(fallbackKey.Marshal(), key.Marshal()) { - sshSignature, err = fallbackAgent.Sign(key, data) + sshSignature, err = fallbackAgent.SignWithFlags(key, data, flags) return } } @@ -221,7 +223,7 @@ func (a *Agent) Sign(key ssh.PublicKey, data []byte) (sshSignature *ssh.Signatur // Add adds a private key to the agent. func (a *Agent) Add(key agent.AddedKey) (err error) { - connErr := a.withOriginalAgent(func(fallbackAgent agent.Agent) { + connErr := a.withOriginalAgent(func(fallbackAgent agent.ExtendedAgent) { err = fallbackAgent.Add(key) }) if connErr != nil { @@ -232,7 +234,7 @@ func (a *Agent) Add(key agent.AddedKey) (err error) { // Remove removes all identities with the given public key. func (a *Agent) Remove(key ssh.PublicKey) (err error) { - connErr := a.withOriginalAgent(func(fallbackAgent agent.Agent) { + connErr := a.withOriginalAgent(func(fallbackAgent agent.ExtendedAgent) { err = fallbackAgent.Remove(key) }) if connErr != nil { @@ -243,7 +245,7 @@ func (a *Agent) Remove(key ssh.PublicKey) (err error) { // RemoveAll removes all identities. func (a *Agent) RemoveAll() (err error) { - connErr := a.withOriginalAgent(func(fallbackAgent agent.Agent) { + connErr := a.withOriginalAgent(func(fallbackAgent agent.ExtendedAgent) { err = fallbackAgent.RemoveAll() }) if connErr != nil { @@ -254,7 +256,7 @@ func (a *Agent) RemoveAll() (err error) { // Lock locks the agent. Sign and Remove will fail, and List will empty an empty list. func (a *Agent) Lock(passphrase []byte) (err error) { - connErr := a.withOriginalAgent(func(fallbackAgent agent.Agent) { + connErr := a.withOriginalAgent(func(fallbackAgent agent.ExtendedAgent) { err = fallbackAgent.Lock(passphrase) }) if connErr != nil { @@ -265,7 +267,7 @@ func (a *Agent) Lock(passphrase []byte) (err error) { // Unlock undoes the effect of Lock func (a *Agent) Unlock(passphrase []byte) (err error) { - connErr := a.withOriginalAgent(func(fallbackAgent agent.Agent) { + connErr := a.withOriginalAgent(func(fallbackAgent agent.ExtendedAgent) { err = fallbackAgent.Unlock(passphrase) }) if connErr != nil { @@ -276,7 +278,7 @@ func (a *Agent) Unlock(passphrase []byte) (err error) { // Signers returns signers for all the known keys. func (a *Agent) Signers() (signers []ssh.Signer, err error) { - connErr := a.withOriginalAgent(func(fallbackAgent agent.Agent) { + connErr := a.withOriginalAgent(func(fallbackAgent agent.ExtendedAgent) { signers, err = fallbackAgent.Signers() }) if connErr != nil { @@ -285,6 +287,16 @@ func (a *Agent) Signers() (signers []ssh.Signer, err error) { return } +func (a *Agent) Extension(extensionType string, contents []byte) (ret []byte, err error) { + connErr := a.withOriginalAgent(func(fallbackAgent agent.ExtendedAgent) { + ret, err = fallbackAgent.Extension(extensionType, contents) + }) + if connErr != nil { + err = agent.ErrExtensionUnsupported + } + return +} + func (a *Agent) notify(prefix, body string) { n, err := socket.OpenNotifier(prefix) if err != nil { diff --git a/src/daemon/ssh_agent_unix.go b/src/daemon/ssh_agent_unix.go new file mode 100644 index 00000000..b5a6a377 --- /dev/null +++ b/src/daemon/ssh_agent_unix.go @@ -0,0 +1,18 @@ +// +build !windows + +package daemon + +import ( + "net" + "os" + "strings" +) + +func getOriginalAgentConn() (net.Conn, error) { + originalAgentSock := os.Getenv("SSH_AUTH_SOCK") + if strings.HasSuffix(originalAgentSock, "krd-agent.sock") { + return nil, nil + } + + return net.Dial("unix", originalAgentSock) +} diff --git a/src/daemon/ssh_agent_windows.go b/src/daemon/ssh_agent_windows.go new file mode 100644 index 00000000..6ff93ced --- /dev/null +++ b/src/daemon/ssh_agent_windows.go @@ -0,0 +1,17 @@ +package daemon + +import ( + "github.com/Microsoft/go-winio" + "net" + "os" + "strings" +) + +func getOriginalAgentConn() (net.Conn, error) { + originalAgentSock := os.Getenv("SSH_AUTH_SOCK") + if strings.HasSuffix(originalAgentSock, "krd-agent") { + return nil, nil + } + + return winio.DialPipe(`\\.\pipe\openssh-ssh-agent`, nil) +} diff --git a/src/daemon/sshconfig.go b/src/daemon/sshconfig.go index 2b35012d..62ae020c 100644 --- a/src/daemon/sshconfig.go +++ b/src/daemon/sshconfig.go @@ -4,6 +4,8 @@ import ( "bytes" "io/ioutil" "os" + + . "krypt.co/kr/common/socket" ) func replaceKryptoniteWithKrypton(in []byte) []byte { @@ -13,7 +15,7 @@ func replaceKryptoniteWithKrypton(in []byte) []byte { } func UpgradeSSHConfig() (err error) { - sshDirPath := os.Getenv("HOME") + "/.ssh" + sshDirPath := HomeDir() + "/.ssh" _ = os.MkdirAll(sshDirPath, 0700) sshConfigPath := sshDirPath + "/config" diff --git a/src/go.mod b/src/go.mod index 00a2fd74..8674bd74 100644 --- a/src/go.mod +++ b/src/go.mod @@ -3,6 +3,7 @@ module krypt.co/kr go 1.13 require ( + github.com/Microsoft/go-winio v0.4.14 github.com/atotto/clipboard v0.1.2 github.com/aws/aws-sdk-go v1.25.15 github.com/blang/semver v3.5.1+incompatible @@ -17,10 +18,12 @@ require ( github.com/mattn/go-isatty v0.0.10 // indirect github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 github.com/paypal/gatt v0.0.0-20151011220935-4ae819d591cf // indirect + github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 github.com/satori/go.uuid v1.2.0 github.com/urfave/cli v1.22.1 github.com/youtube/vitess v2.1.1+incompatible golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 + golang.org/x/sys v0.0.0-20191008105621-543471e840be ) replace golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 => github.com/kryptco/go-crypto v0.0.0-20191020215841-c5850b359d8a diff --git a/src/go.sum b/src/go.sum index 963681b7..949fb730 100644 --- a/src/go.sum +++ b/src/go.sum @@ -1,4 +1,6 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/atotto/clipboard v0.1.2 h1:YZCtFu5Ie8qX2VmVTBnrqLSiU9XOWwqNRmdT3gIQzbY= github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aws/aws-sdk-go v1.25.15 h1:2XrAm3F7QuNguJXJ6SUJxywL1TPNDM3z/mNEaS0CHtk= @@ -7,6 +9,7 @@ github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdn github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc h1:55rEp52jU6bkyslZ1+C/7NGfpQsEc6pxGLAGDOctqbw= @@ -17,6 +20,7 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5i github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/keybase/saltpack v0.0.0-20190828020936-3f47e8e2e6ec h1:ejQ18jEAjyiFen49m7h8x46kzC3heD/HbUiOPzBLVWg= github.com/keybase/saltpack v0.0.0-20190828020936-3f47e8e2e6ec/go.mod h1:loEJH62iafRcA8ZYlUEq9WgJh/Z9BHdHHfAmxzacQng= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kryptco/gf256 v0.0.0-20160413180133-bbd714a0764d h1:oDNL85JcK6Ex87EMV/BUzUD6gCTUos0BxmOh+asxvSI= github.com/kryptco/gf256 v0.0.0-20160413180133-bbd714a0764d/go.mod h1:ISD7LkDBEju/eoUn4vtV/WgGAbM/k4wlJneRmsTXA2I= github.com/kryptco/go-crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -35,6 +39,9 @@ github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0C github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/paypal/gatt v0.0.0-20151011220935-4ae819d591cf h1:RHRtrMle1AlWsMdCoIQIbq7IB2y8/5qEsUoAzjCCSCw= github.com/paypal/gatt v0.0.0-20151011220935-4ae819d591cf/go.mod h1:+AwQL2mK3Pd3S+TUwg0tYQjid0q1txyNUJuuSmz8Kdk= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -42,6 +49,9 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/youtube/vitess v2.1.1+incompatible h1:SE+P7DNX/jw5RHFs5CHRhZQjq402EJFCD33JhzQMdDw= @@ -50,9 +60,11 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/src/kr/kr.go b/src/kr/kr.go index 6c21ba1d..9f7e64f9 100644 --- a/src/kr/kr.go +++ b/src/kr/kr.go @@ -523,6 +523,7 @@ func restartCommand(c *cli.Context) (err error) { } func main() { + initTerminal() app := cli.NewApp() app.Name = "kr" app.Usage = "communicate with Krypton and krd - the Krypton daemon" diff --git a/src/kr/kr_darwin.go b/src/kr/kr_darwin.go index 20369712..a23c8a33 100644 --- a/src/kr/kr_darwin.go +++ b/src/kr/kr_darwin.go @@ -37,6 +37,9 @@ const PLIST_TEMPLATE = ` ` +func initTerminal() { +} + func copyPlist() (err error) { output, err := exec.Command("which", "krd").Output() if err != nil { diff --git a/src/kr/kr_unix.go b/src/kr/kr_unix.go index b9169faa..c90ce219 100644 --- a/src/kr/kr_unix.go +++ b/src/kr/kr_unix.go @@ -1,4 +1,4 @@ -// +build !darwin +// +build !darwin,!windows package main @@ -11,6 +11,9 @@ import ( . "krypt.co/kr/common/socket" ) +func initTerminal() { +} + func restartCommandOptions(c *cli.Context, isUserInitiated bool) (err error) { if isUserInitiated { Analytics{}.PostEventUsingPersistedTrackingID("kr", "restart", nil, nil) diff --git a/src/kr/kr_windows.go b/src/kr/kr_windows.go new file mode 100644 index 00000000..921183b4 --- /dev/null +++ b/src/kr/kr_windows.go @@ -0,0 +1,76 @@ +// +build windows + +package main + +import ( + "fmt" + "github.com/pkg/browser" + "github.com/urfave/cli" + "golang.org/x/sys/windows" + "os" + "os/exec" + + . "krypt.co/kr/common/analytics" + . "krypt.co/kr/common/socket" +) + +func initTerminal() { + var m uint32 + windows.GetConsoleMode(windows.Stdout, &m) + windows.SetConsoleMode(windows.Stdout, m|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) +} + +func restartCommandOptions(c *cli.Context, isUserInitiated bool) (err error) { + if isUserInitiated { + Analytics{}.PostEventUsingPersistedTrackingID("kr", "restart", nil, nil) + } + + _ = migrateSSHConfig() + + KillKrd() + startKrd() + + if isUserInitiated { + PrintErr(os.Stderr, "Restarted Krypton daemon.") + } + return +} + +func upgradeCommand(c *cli.Context) (err error) { + return fmt.Errorf("Upgrade not supported") +} + +func uninstallCommand(c *cli.Context) (err error) { + go func() { + Analytics{}.PostEventUsingPersistedTrackingID("kr", "uninstall", nil, nil) + }() + confirmOrFatal(os.Stderr, "Uninstall Krypton from this workstation?") + + cleanSSHConfig() + + KillKrd() + + //uninstallCodesigning() + PrintErr(os.Stderr, "Krypton uninstalled. If you experience any issues, please refer to https://krypt.co/docs/start/installation.html#uninstalling-kr") + return +} + +func startKrd() (err error) { + exe := "krd.exe" + if pfx, err := getPrefix(); err == nil { + exe = pfx + `\krd.exe` + } + cmd := exec.Command(exe) + return cmd.Start() +} + +func openBrowser(url string) { + err := browser.OpenURL(url) + if err != nil { + os.Stderr.WriteString("Unable to open browser, please visit " + url + "\r\n") + } +} + +func killKrd() { + KillKrd() +} diff --git a/src/kr/sshconfig.go b/src/kr/sshconfig.go index 02014d0e..cd44fdfd 100644 --- a/src/kr/sshconfig.go +++ b/src/kr/sshconfig.go @@ -6,12 +6,14 @@ import ( "io/ioutil" "os" "os/exec" + "runtime" + "strconv" "strings" "time" - "strconv" "github.com/urfave/cli" + . "krypt.co/kr/common/socket" . "krypt.co/kr/common/util" ) @@ -35,6 +37,18 @@ Host * IdentityFile ~/.ssh/id_ecdsa IdentityFile ~/.ssh/id_dsa` +const SSH_CONFIG_FORMAT_WIN = `# Added by Krypton +Host * + IdentityAgent \\.\pipe\krd-agent + ProxyCommand %s\krssh.exe %%h %%p` +/* + IdentityFile ~/.ssh/id_krypton + IdentityFile ~/.ssh/id_ed25519 + IdentityFile ~/.ssh/id_rsa + IdentityFile ~/.ssh/id_ecdsa + IdentityFile ~/.ssh/id_dsa` +*/ + const OLD_PKCS11_PROVIDER_FORMAT = `PKCS11Provider %s/lib/kr-pkcs11.so` const NEW_IDENTITY_AGENT = `IdentityAgent ~/.kr/krd-agent.sock` @@ -48,7 +62,11 @@ func getKrSSHConfigBlockOrFatal() string { var sshConfigWithPrefix string if localSSHSupportsIdentityAgent(){ - sshConfigWithPrefix = fmt.Sprintf(SSH_CONFIG_FORMAT, prefix) + if runtime.GOOS == "windows" { + sshConfigWithPrefix = fmt.Sprintf(SSH_CONFIG_FORMAT_WIN, prefix) + } else { + sshConfigWithPrefix = fmt.Sprintf(SSH_CONFIG_FORMAT, prefix) + } } else { sshConfigWithPrefix = fmt.Sprintf(OLD_SSH_CONFIG_FORMAT, prefix, prefix) } @@ -72,7 +90,7 @@ func autoEditSSHConfig() (err error) { } func getSSHConfigAndBakPaths() (string, string) { - sshDirPath := os.Getenv("HOME") + "/.ssh" + sshDirPath := HomeDir() + "/.ssh" _ = os.MkdirAll(sshDirPath, 0700) sshConfigPath := sshDirPath + "/config" sshConfigBackupPath := sshConfigPath + ".bak.kr" @@ -141,11 +159,13 @@ func localSSHSupportsIdentityAgent() bool { // Valid OpenSSH version strings: // OpenSSH_6.7p1 Debian-5+deb8u4, OpenSSL 1.0.1t 3 May 2016 // OpenSSH_7.7p1, OpenSSL 1.0.2o 27 Mar 2018 + // OpenSSH_for_Windows_7.7p1, LibreSSL 2.6.5 versionOutput, err := exec.Command("ssh", "-V").CombinedOutput() if err != nil { return false } versionString := string(versionOutput) + versionString = strings.TrimPrefix(versionString, "OpenSSH_for_Windows_") versionString = strings.TrimPrefix(versionString, "OpenSSH_") for _, suffixDelim := range []string{" ", ",", "p"} { versionString = strings.Split(versionString, suffixDelim)[0] @@ -204,7 +224,7 @@ func migrateSSHConfig() (err error) { func cleanSSHConfig() (err error) { configBlock := []byte(getKrSSHConfigBlockOrFatal()) - sshDirPath := os.Getenv("HOME") + "/.ssh" + sshDirPath := HomeDir() + "/.ssh" sshConfigPath := sshDirPath + "/config" sshConfigBackupPath := sshConfigPath + ".bak.kr.uninstall" @@ -231,12 +251,3 @@ func cleanSSHConfig() (err error) { } return } - -func getPrefix() (string, error) { - krAbsPath, err := exec.Command("which", "kr").Output() - if err != nil { - PrintErr(os.Stderr, Red("Krypton ▶ Could not find kr on PATH")) - return "", err - } - return strings.TrimSuffix(strings.TrimSpace(string(krAbsPath)), "/bin/kr"), nil -} diff --git a/src/kr/sshconfig_unix.go b/src/kr/sshconfig_unix.go new file mode 100644 index 00000000..ecb618f5 --- /dev/null +++ b/src/kr/sshconfig_unix.go @@ -0,0 +1,20 @@ +// +build !windows + +package main + +import ( + "os" + "os/exec" + "strings" + + . "krypt.co/kr/common/util" +) + +func getPrefix() (string, error) { + krAbsPath, err := exec.Command("which", "kr").Output() + if err != nil { + PrintErr(os.Stderr, Red("Krypton ▶ Could not find kr on PATH")) + return "", err + } + return strings.TrimSuffix(strings.TrimSpace(string(krAbsPath)), "/bin/kr"), nil +} diff --git a/src/kr/sshconfig_windows.go b/src/kr/sshconfig_windows.go new file mode 100644 index 00000000..f579bd8f --- /dev/null +++ b/src/kr/sshconfig_windows.go @@ -0,0 +1,17 @@ +package main + +import ( + "os" + "path/filepath" + + . "krypt.co/kr/common/util" +) + +func getPrefix() (string, error) { + if ex, err := os.Executable(); err == nil { + return filepath.Dir(ex), nil + } else { + PrintErr(os.Stderr, Red("Krypton ▶ Problem getting path of kr.exe")) + return "", err + } +}