func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string) error { var req []byte switch k := s.(type) { case *rsa.PrivateKey: if len(k.Primes) != 2 { return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes)) } k.Precompute() req = ssh.Marshal(rsaCertMsg{ Type: cert.Type(), CertBytes: cert.Marshal(), D: k.D, Iqmp: k.Precomputed.Qinv, P: k.Primes[0], Q: k.Primes[1], Comments: comment, }) case *dsa.PrivateKey: req = ssh.Marshal(dsaCertMsg{ Type: cert.Type(), CertBytes: cert.Marshal(), X: k.X, Comments: comment, }) case *ecdsa.PrivateKey: req = ssh.Marshal(ecdsaCertMsg{ Type: cert.Type(), CertBytes: cert.Marshal(), D: k.D, Comments: comment, }) default: return fmt.Errorf("agent: unsupported key type %T", s) } signer, err := ssh.NewSignerFromKey(s) if err != nil { return err } if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 { return errors.New("agent: signer and cert have different public key") } resp, err := c.call(req) if err != nil { return err } if _, ok := resp.(*successAgentMsg); ok { return nil } return errors.New("agent: failure") }
// Insert adds a private key to the agent. func (c *client) insertKey(s interface{}, comment string) error { var req []byte switch k := s.(type) { case *rsa.PrivateKey: if len(k.Primes) != 2 { return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes)) } k.Precompute() req = ssh.Marshal(rsaKeyMsg{ Type: ssh.KeyAlgoRSA, N: k.N, E: big.NewInt(int64(k.E)), D: k.D, Iqmp: k.Precomputed.Qinv, P: k.Primes[0], Q: k.Primes[1], Comments: comment, }) case *dsa.PrivateKey: req = ssh.Marshal(dsaKeyMsg{ Type: ssh.KeyAlgoDSA, P: k.P, Q: k.Q, G: k.G, Y: k.Y, X: k.X, Comments: comment, }) case *ecdsa.PrivateKey: nistID := fmt.Sprintf("nistp%d", k.Params().BitSize) req = ssh.Marshal(ecdsaKeyMsg{ Type: "ecdsa-sha2-" + nistID, Curve: nistID, KeyBytes: elliptic.Marshal(k.Curve, k.X, k.Y), D: k.D, Comments: comment, }) default: return fmt.Errorf("agent: unsupported key type %T", s) } resp, err := c.call(req) if err != nil { return err } if _, ok := resp.(*successAgentMsg); ok { return nil } return errors.New("agent: failure") }
func marshalKey(k *Key) []byte { var record struct { Blob []byte Comment string } record.Blob = k.Marshal() record.Comment = k.Comment return ssh.Marshal(&record) }
func (s *server) processRequestBytes(reqData []byte) []byte { rep, err := s.processRequest(reqData) if err != nil { if err != errLocked { // TODO(hanwen): provide better logging interface? log.Printf("agent %d: %v", reqData[0], err) } return []byte{agentFailure} } if err == nil && rep == nil { return []byte{agentSuccess} } return ssh.Marshal(rep) }
// Sign has the agent sign the data using a protocol 2 key as defined // in [PROTOCOL.agent] section 2.6.2. func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { req := ssh.Marshal(signRequestAgentMsg{ KeyBlob: key.Marshal(), Data: data, }) msg, err := c.call(req) if err != nil { return nil, err } switch msg := msg.(type) { case *signResponseAgentMsg: var sig ssh.Signature if err := ssh.Unmarshal(msg.SigBlob, &sig); err != nil { return nil, err } return &sig, nil case *failureAgentMsg: return nil, errors.New("agent: failed to sign challenge") } panic("unreachable") }
func handleChannel(conn *ssh.ServerConn, newChan ssh.NewChannel) { ch, reqs, err := newChan.Accept() if err != nil { log.Println("newChan.Accept failed:", err) return } defer ch.Close() for req := range reqs { switch req.Type { case "exec": fail := func(at string, err error) { log.Printf("%s failed: %s", at, err) ch.Stderr().Write([]byte("Internal error.\n")) } if req.WantReply { req.Reply(true, nil) } cmdline := string(req.Payload[4:]) cmdargs, err := shlex.Split(cmdline) if err != nil || len(cmdargs) != 2 { ch.Stderr().Write([]byte("Invalid arguments.\n")) return } if cmdargs[0] != "git-receive-pack" { ch.Stderr().Write([]byte("Only `git push` is supported.\n")) return } cmdargs[1] = strings.TrimSuffix(strings.TrimPrefix(cmdargs[1], "/"), ".git") if strings.Contains(cmdargs[1], "..") { ch.Stderr().Write([]byte("Invalid repo.\n")) return } if err := ensureCacheRepo(cmdargs[1]); err != nil { fail("ensureCacheRepo", err) return } cmd := exec.Command("git-shell", "-c", cmdargs[0]+" '"+cmdargs[1]+"'") cmd.Dir = *repoPath cmd.Env = append(os.Environ(), "RECEIVE_USER="******"RECEIVE_REPO="+cmdargs[1], ) done, err := attachCmd(cmd, ch, ch.Stderr(), ch) if err != nil { fail("attachCmd", err) return } if err := cmd.Start(); err != nil { fail("cmd.Start", err) return } done.Wait() status, err := exitStatus(cmd.Wait()) if err != nil { fail("exitStatus", err) return } if _, err := ch.SendRequest("exit-status", false, ssh.Marshal(&status)); err != nil { fail("sendExit", err) } return case "env": if req.WantReply { req.Reply(true, nil) } } } }
func handleChannel(conn *ssh.ServerConn, newChan ssh.NewChannel) { ch, reqs, err := newChan.Accept() if err != nil { log.Println("newChan.Accept failed:", err) return } defer ch.Close() for req := range reqs { switch req.Type { case "exec": fail := func(at string, err error) { log.Printf("%s failed: %s", at, err) ch.Stderr().Write([]byte("Internal error.\n")) } if req.WantReply { req.Reply(true, nil) } cmdline := string(req.Payload[4:]) cmdargs, err := shlex.Split(cmdline) if err != nil || len(cmdargs) != 2 { ch.Stderr().Write([]byte("Invalid arguments.\n")) return } if cmdargs[0] != "git-receive-pack" { ch.Stderr().Write([]byte("Only `git push` is supported.\n")) return } cmdargs[1] = strings.TrimSuffix(strings.TrimPrefix(cmdargs[1], "/"), ".git") if strings.Contains(cmdargs[1], "..") { ch.Stderr().Write([]byte("Invalid repo.\n")) return } cacheKey := cmdargs[1] if *cacheKeyHook != "" { var result bytes.Buffer var errout bytes.Buffer cmd := exec.Command(*cacheKeyHook, cmdargs[0], cmdargs[1]) cmd.Stdout = &result cmd.Stderr = &errout if err := cmd.Run(); err != nil { fail("cacheKeyHook", errors.New(errout.String())) return } cacheKey = result.String() } tempDir := *repoPath if *useBlobstore { path, err := ioutil.TempDir("", "") if err != nil { fail("generateTempDir", err) return } tempDir = path } if err := ensureCacheRepo(tempDir, cacheKey); err != nil { fail("ensureCacheRepo", err) return } if *useBlobstore { if err := restoreBlobstoreCache(tempDir, cacheKey); err != nil { fail("restoreBlobstoreCache", err) return } } cmd := exec.Command("git-shell", "-c", cmdargs[0]+" '"+cacheKey+"'") cmd.Dir = tempDir cmd.Env = append(os.Environ(), "RECEIVE_USER="******"RECEIVE_REPO="+cmdargs[1], ) done, err := attachCmd(cmd, ch, ch.Stderr(), ch) if err != nil { fail("attachCmd", err) return } if err := cmd.Start(); err != nil { fail("cmd.Start", err) return } done.Wait() status, err := exitStatus(cmd.Wait()) if err != nil { fail("exitStatus", err) return } if *useBlobstore { if err := uploadCache(tempDir, cacheKey); err != nil { fail("uploadCache", err) } } if _, err := ch.SendRequest("exit-status", false, ssh.Marshal(&status)); err != nil { fail("sendExit", err) return } return case "env": if req.WantReply { req.Reply(true, nil) } } } }
func (s *server) processRequest(data []byte) (interface{}, error) { switch data[0] { case agentRequestV1Identities: return &agentV1IdentityMsg{0}, nil case agentRemoveIdentity: var req agentRemoveIdentityMsg if err := ssh.Unmarshal(data, &req); err != nil { return nil, err } var wk wireKey if err := ssh.Unmarshal(req.KeyBlob, &wk); err != nil { return nil, err } return nil, s.agent.Remove(&Key{Format: wk.Format, Blob: req.KeyBlob}) case agentRemoveAllIdentities: return nil, s.agent.RemoveAll() case agentLock: var req agentLockMsg if err := ssh.Unmarshal(data, &req); err != nil { return nil, err } return nil, s.agent.Lock(req.Passphrase) case agentUnlock: var req agentLockMsg if err := ssh.Unmarshal(data, &req); err != nil { return nil, err } return nil, s.agent.Unlock(req.Passphrase) case agentSignRequest: var req signRequestAgentMsg if err := ssh.Unmarshal(data, &req); err != nil { return nil, err } var wk wireKey if err := ssh.Unmarshal(req.KeyBlob, &wk); err != nil { return nil, err } k := &Key{ Format: wk.Format, Blob: req.KeyBlob, } sig, err := s.agent.Sign(k, req.Data) // TODO(hanwen): flags. if err != nil { return nil, err } return &signResponseAgentMsg{SigBlob: ssh.Marshal(sig)}, nil case agentRequestIdentities: keys, err := s.agent.List() if err != nil { return nil, err } rep := identitiesAnswerAgentMsg{ NumKeys: uint32(len(keys)), } for _, k := range keys { rep.Keys = append(rep.Keys, marshalKey(k)...) } return rep, nil case agentAddIdentity: return nil, s.insertIdentity(data) } return nil, fmt.Errorf("unknown opcode %d", data[0]) }
func (c *client) Unlock(passphrase []byte) error { req := ssh.Marshal(&agentUnlockMsg{ Passphrase: passphrase, }) return c.simpleCall(req) }
func (c *client) Remove(key ssh.PublicKey) error { req := ssh.Marshal(&agentRemoveIdentityMsg{ KeyBlob: key.Marshal(), }) return c.simpleCall(req) }