From 3fc06e3b127d134109dca40a51b411eabed53af5 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Mon, 5 Feb 2018 18:40:13 -0800 Subject: [PATCH] New functions (AddROSigner, etc.) and data structures for ROAgent. Almost compatible with ssh-add now. --- cmd/ro/main.go | 80 ++++++++++++------------ cmd/ro/roagent/roagent.go | 126 +++++++++++++++++++++++++++----------- 2 files changed, 129 insertions(+), 77 deletions(-) diff --git a/cmd/ro/main.go b/cmd/ro/main.go index d65c352..58f8fd4 100644 --- a/cmd/ro/main.go +++ b/cmd/ro/main.go @@ -112,18 +112,18 @@ func getUserCredentials() { if user == "" { user, err = readLine("Username: ") - processError(err) + processError("error", err) } if pswd == "" { pswd, err = gopass.GetPass("Password:") - processError(err) + processError("error", err) } } -func processError(err error) { +func processError(msg string, err error) { if err != nil { - log.Fatal("error:", err) + log.Fatalln(msg, ":", err) } } @@ -141,7 +141,7 @@ func runCreate() { Password: pswd, } resp, err := roServer.Create(req) - processError(err) + processError("error", err) fmt.Println(resp.Status) } @@ -153,7 +153,7 @@ func runCreateUser() { HipchatName: hcName, } resp, err := roServer.CreateUser(req) - processError(err) + processError("error", err) fmt.Println(resp.Status) } @@ -167,7 +167,7 @@ func runDelegate() { Labels: processCSL(labels), } resp, err := roServer.Delegate(req) - processError(err) + processError("error", err) fmt.Println(resp.Status) } @@ -180,7 +180,7 @@ func runRestore() { } resp, err := roServer.Restore(req) - processError(err) + processError("error", err) if resp.Status != "ok" { fmt.Fprintf(os.Stderr, "failed: %s\n", resp.Status) @@ -189,7 +189,7 @@ func runRestore() { var st core.StatusData err = json.Unmarshal(resp.Response, &st) - processError(err) + processError("error", err) fmt.Println("Restore delegation complete; persistence is now", st.Status) } @@ -201,13 +201,13 @@ func runSummary() { Password: pswd, } resp, err := roServer.Summary(req) - processError(err) + processError("error", err) fmt.Println(resp) } func runEncrypt() { inBytes, err := ioutil.ReadFile(inPath) - processError(err) + processError("error", err) req := core.EncryptRequest{ Name: user, Password: pswd, @@ -221,9 +221,9 @@ func runEncrypt() { } resp, err := roServer.Encrypt(req) - processError(err) + processError("error", err) if resp.Status != "ok" { - log.Fatal("response status error:", resp.Status) + log.Fatalln("response status error", resp.Status) return } fmt.Println("Response Status:", resp.Status) @@ -233,7 +233,7 @@ func runEncrypt() { func runReEncrypt() { inBytes, err := ioutil.ReadFile(inPath) - processError(err) + processError("error", err) // base64 decode the input encBytes, err := base64.StdEncoding.DecodeString(string(inBytes)) @@ -254,9 +254,9 @@ func runReEncrypt() { } resp, err := roServer.ReEncrypt(req) - processError(err) + processError("error", err) if resp.Status != "ok" { - log.Fatal("response status error:", resp.Status) + log.Fatalln("response status error", resp.Status) return } fmt.Println("Response Status:", resp.Status) @@ -266,7 +266,7 @@ func runReEncrypt() { func runDecrypt() { inBytes, err := ioutil.ReadFile(inPath) - processError(err) + processError("error", err) // base64 decode the input encBytes, err := base64.StdEncoding.DecodeString(string(inBytes)) @@ -300,25 +300,25 @@ func runDecrypt() { select { case <-sigChan: - log.Fatal("process is interrupted") + log.Fatalln("process is interrupted") return case <-time.After(30 * time.Second): } } default: - processError(err) + processError("error", err) } } if resp == nil { - log.Fatal("response status error:", resp.Status) + log.Fatalln("response status error", resp.Status) } fmt.Println("Response Status:", resp.Status) var msg core.DecryptWithDelegates err = json.Unmarshal(resp.Response, &msg) - processError(err) + processError("error", err) fmt.Println("Secure:", msg.Secure) fmt.Println("Delegates:", msg.Delegates) ioutil.WriteFile(outPath, msg.Data, 0644) @@ -334,19 +334,19 @@ func runOrder() { Users: processCSL(users), } resp, err := roServer.Order(req) - processError(err) + processError("error", err) var o order.Order err = json.Unmarshal(resp.Response, &o) - processError(err) + processError("error", err) if pollInterval > 0 { for o.Delegated < 2 { time.Sleep(pollInterval) resp, err = roServer.OrderInfo(core.OrderInfoRequest{Name: user, Password: pswd, OrderNum: o.Num}) - processError(err) + processError("error", err) err = json.Unmarshal(resp.Response, &o) - processError(err) + processError("error", err) } } fmt.Println(resp.Status) @@ -354,7 +354,7 @@ func runOrder() { func runOwner() { inBytes, err := ioutil.ReadFile(inPath) - processError(err) + processError("error", err) // attempt to base64 decode the input file base64decoded, err := base64.StdEncoding.DecodeString(string(inBytes)) @@ -367,7 +367,7 @@ func runOwner() { } resp, err := roServer.Owners(req) - processError(err) + processError("error", err) fmt.Println(resp.Status) fmt.Println(resp) @@ -380,7 +380,7 @@ func runStatus() { } resp, err := roServer.Status(req) - processError(err) + processError("error", err) fmt.Println(resp.Status) fmt.Println(resp) @@ -393,7 +393,7 @@ func runResetPersisted() { } resp, err := roServer.ResetPersisted(req) - processError(err) + processError("error", err) fmt.Println(resp.Status) fmt.Println(resp) @@ -404,7 +404,7 @@ func runSSHAgent() { // Prepare a socket dir, err := ioutil.TempDir("", "ro_ssh_") - processError(err) + processError("error", err) authSockPath := path.Join(dir, "roagent.sock") os.Setenv("SSH_AUTH_SOCK", authSockPath) @@ -412,11 +412,11 @@ func runSSHAgent() { socket := net.UnixAddr{Net: "unix", Name: authSockPath} ear, err := net.ListenUnix("unix", &socket) - processError(err) + processError("error", err) // Process the arguments inBytes, err := ioutil.ReadFile(inPath) - processError(err) + processError("error", err) encBytes, err := base64.StdEncoding.DecodeString(string(inBytes)) if err != nil { @@ -425,15 +425,15 @@ func runSSHAgent() { } inBytes, err = ioutil.ReadFile(pubKeyPath) - processError(err) + processError("error", err) pubKey, _, _, _, err := ssh.ParseAuthorizedKey(inBytes) - if err != nil { - log.Fatal("failed to parse SSH public key", err) - } + processError("failed to parse SSH public key", err) // Make an agent - roagent := roagent.NewROAgent(roServer, pubKey, encBytes, user, pswd) +// sshagent := agent.NewKeyring() + roagent, err := roagent.NewROAgent(roServer, pubKey, encBytes, user, pswd) + processError("failed to start ROAgent", err) sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) @@ -447,9 +447,7 @@ func runSSHAgent() { for { conn, err := ear.AcceptUnix() - if err != nil { - log.Fatal("error accepting socket connection", err) - } + processError("error accepting socket connection", err) // Serve the agent go agent.ServeAgent(roagent, conn) @@ -486,7 +484,7 @@ func main() { } else { var err error roServer, err = client.NewRemoteServer(server, caPath) - processError(err) + processError("error", err) getUserCredentials() cmd.Run() diff --git a/cmd/ro/roagent/roagent.go b/cmd/ro/roagent/roagent.go index 853def8..5b6f603 100644 --- a/cmd/ro/roagent/roagent.go +++ b/cmd/ro/roagent/roagent.go @@ -7,6 +7,7 @@ import ( "crypto/rand" "encoding/json" "errors" + "log" "io" "github.com/cloudflare/redoctober/client" @@ -15,12 +16,18 @@ import ( "golang.org/x/crypto/ssh/agent" ) -type ROSigner struct { +type ROAgent struct { server *client.RemoteServer - pub ssh.PublicKey - encryptedKey []byte user string pswd string + keyring []*ROSigner +} + +type ROSigner struct { + agent *ROAgent + pub ssh.PublicKey + rawKey []byte + encryptedKey []byte } func (signer ROSigner) PublicKey() ssh.PublicKey { @@ -28,14 +35,15 @@ func (signer ROSigner) PublicKey() ssh.PublicKey { } func (signer ROSigner) Sign(rand io.Reader, msg []byte) (signature *ssh.Signature, err error) { + // TODO encryptedKey vs rawKey req := core.SSHSignWithRequest{ - Name: signer.user, - Password: signer.pswd, + Name: signer.agent.user, + Password: signer.agent.pswd, Data: signer.encryptedKey, TBSData: msg, } - resp, err := signer.server.SSHSignWith(req) + resp, err := signer.agent.server.SSHSignWith(req) if err != nil { return nil, err } @@ -55,70 +63,116 @@ func (signer ROSigner) Sign(rand io.Reader, msg []byte) (signature *ssh.Signatur return &sshSignature, nil } -type ROAgent struct { - signer ROSigner -} - // NewROAgent creates a new SSH agent which forwards signature requests to the // provided remote server -func NewROAgent(server *client.RemoteServer, pubKey ssh.PublicKey, encryptedPrivKey []byte, user, pswd string) agent.Agent { - return &ROAgent{ - ROSigner{ - server, - pubKey, - encryptedPrivKey, - user, - pswd, - }, +func NewROAgent(server *client.RemoteServer, pubKey ssh.PublicKey, encryptedPrivKey []byte, user, pswd string) (agent.Agent, error) { + // FIXME these arguments are extra + roagent := &ROAgent{ + server, + user, + pswd, + []*ROSigner{}, } + + err := roagent.AddROSigner(pubKey, encryptedPrivKey) + if err != nil { + return nil, errors.New("failed to add new signer to the ROAgent") + } + + return roagent, nil } -// RemoveAll has no effect for the ROAgent +// NewROSigner adds a new SSH identity to the ROAgent +func (r *ROAgent) AddROSigner(pubKey ssh.PublicKey, encryptedPrivKey []byte) error { + rosigner := &ROSigner{ + agent: r, + pub: pubKey, + encryptedKey: encryptedPrivKey, + } + r.keyring = append(r.keyring, rosigner) + return nil +} + +// RemoveAll empties ROAgent's keyring func (r *ROAgent) RemoveAll() error { + r.keyring = []*ROSigner{} return nil } -// Remove has no effect for the ROAgent +// Removes the first matching key from ROAgent's keyring func (r *ROAgent) Remove(key ssh.PublicKey) error { - return nil + wanted := key.Marshal() + for i, signer := range r.keyring { + if bytes.Equal(signer.PublicKey().Marshal(), wanted) { + // Order is not preserved + r.keyring[i] = r.keyring[0] + r.keyring = r.keyring[1:] + log.Println("signer was removed") + return nil + } + } + return errors.New("could not remove signer") } -// Lock has no effect for the ROAgent +// Locks the ROAgent by removing the password +// TODO should this encrypt the password instead? func (r *ROAgent) Lock(passphrase []byte) error { - return nil + if bytes.Equal(passphrase, []byte(r.pswd)) { + r.pswd = "" + return nil + } + return errors.New("could not lock the agent") } -// Unlock has no effect for the ROAgent +// Unlocks the ROAgent by adding the password +// FIXME ask papa RO if the password is correct func (r *ROAgent) Unlock(passphrase []byte) error { + r.pswd = string(passphrase) return nil } // List returns the identities known to the agent. func (r *ROAgent) List() ([]*agent.Key, error) { - return []*agent.Key{ - { - Format: r.signer.PublicKey().Type(), - Blob: r.signer.PublicKey().Marshal(), - Comment: "Red October encrypted SSH key", - }, - }, nil + list := make([]*agent.Key, len(r.keyring)) + for i, signer := range r.keyring { + list[i] = &agent.Key{ + Format: signer.PublicKey().Type(), + Blob: signer.PublicKey().Marshal(), + Comment: r.user, + } + } + return list, nil } // Add has no effect for the ROAgent +// FIXME func (r *ROAgent) Add(key agent.AddedKey) error { + signer, _ := ssh.NewSignerFromKey(key.PrivateKey) + rosigner := &ROSigner{ + pub: signer.PublicKey(), + encryptedKey: nil, //[]byte + } + r.keyring = append(r.keyring, rosigner) + log.Println("new signer was added") return nil } // Sign returns a signature for the data. func (r *ROAgent) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { wanted := key.Marshal() - if bytes.Equal(r.signer.PublicKey().Marshal(), wanted) { - return r.signer.Sign(rand.Reader, data) + for _, signer := range r.keyring { + if bytes.Equal(signer.PublicKey().Marshal(), wanted) { + return signer.Sign(rand.Reader, data) + } } - return nil, errors.New("wrong key requested") + return nil, errors.New("requested key was not found on keyring") } // Signers returns signers for all the known keys. func (r *ROAgent) Signers() ([]ssh.Signer, error) { - return []ssh.Signer{r.signer}, nil + list := make([]ssh.Signer, len(r.keyring)) + for i, signer := range r.keyring { + list[i] = ssh.Signer(signer) + } + return list, nil }