New functions (AddROSigner, etc.) and data structures for ROAgent.

Almost compatible with ssh-add now.
This commit is contained in:
Mahrud Sayrafi
2018-03-12 12:39:32 -07:00
committed by Kyle Isom
parent 433afa9574
commit 3fc06e3b12
2 changed files with 129 additions and 77 deletions
+39 -41
View File
@@ -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()
+90 -36
View File
@@ -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
}