mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 03:18:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			210 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			210 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
		
			Executable File
		
	
	
	
	
| // Copyright 2012 The Go Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| package agent
 | |
| 
 | |
| import (
 | |
| 	"crypto/rsa"
 | |
| 	"encoding/binary"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"log"
 | |
| 	"math/big"
 | |
| 
 | |
| 	"github.com/gogits/gogs/modules/crypto/ssh"
 | |
| )
 | |
| 
 | |
| // Server wraps an Agent and uses it to implement the agent side of
 | |
| // the SSH-agent, wire protocol.
 | |
| type server struct {
 | |
| 	agent Agent
 | |
| }
 | |
| 
 | |
| 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)
 | |
| }
 | |
| 
 | |
| func marshalKey(k *Key) []byte {
 | |
| 	var record struct {
 | |
| 		Blob    []byte
 | |
| 		Comment string
 | |
| 	}
 | |
| 	record.Blob = k.Marshal()
 | |
| 	record.Comment = k.Comment
 | |
| 
 | |
| 	return ssh.Marshal(&record)
 | |
| }
 | |
| 
 | |
| type agentV1IdentityMsg struct {
 | |
| 	Numkeys uint32 `sshtype:"2"`
 | |
| }
 | |
| 
 | |
| type agentRemoveIdentityMsg struct {
 | |
| 	KeyBlob []byte `sshtype:"18"`
 | |
| }
 | |
| 
 | |
| type agentLockMsg struct {
 | |
| 	Passphrase []byte `sshtype:"22"`
 | |
| }
 | |
| 
 | |
| type agentUnlockMsg struct {
 | |
| 	Passphrase []byte `sshtype:"23"`
 | |
| }
 | |
| 
 | |
| 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 (s *server) insertIdentity(req []byte) error {
 | |
| 	var record struct {
 | |
| 		Type string `sshtype:"17"`
 | |
| 		Rest []byte `ssh:"rest"`
 | |
| 	}
 | |
| 	if err := ssh.Unmarshal(req, &record); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	switch record.Type {
 | |
| 	case ssh.KeyAlgoRSA:
 | |
| 		var k rsaKeyMsg
 | |
| 		if err := ssh.Unmarshal(req, &k); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		priv := rsa.PrivateKey{
 | |
| 			PublicKey: rsa.PublicKey{
 | |
| 				E: int(k.E.Int64()),
 | |
| 				N: k.N,
 | |
| 			},
 | |
| 			D:      k.D,
 | |
| 			Primes: []*big.Int{k.P, k.Q},
 | |
| 		}
 | |
| 		priv.Precompute()
 | |
| 
 | |
| 		return s.agent.Add(AddedKey{PrivateKey: &priv, Comment: k.Comments})
 | |
| 	}
 | |
| 	return fmt.Errorf("not implemented: %s", record.Type)
 | |
| }
 | |
| 
 | |
| // ServeAgent serves the agent protocol on the given connection. It
 | |
| // returns when an I/O error occurs.
 | |
| func ServeAgent(agent Agent, c io.ReadWriter) error {
 | |
| 	s := &server{agent}
 | |
| 
 | |
| 	var length [4]byte
 | |
| 	for {
 | |
| 		if _, err := io.ReadFull(c, length[:]); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		l := binary.BigEndian.Uint32(length[:])
 | |
| 		if l > maxAgentResponseBytes {
 | |
| 			// We also cap requests.
 | |
| 			return fmt.Errorf("agent: request too large: %d", l)
 | |
| 		}
 | |
| 
 | |
| 		req := make([]byte, l)
 | |
| 		if _, err := io.ReadFull(c, req); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		repData := s.processRequestBytes(req)
 | |
| 		if len(repData) > maxAgentResponseBytes {
 | |
| 			return fmt.Errorf("agent: reply too large: %d bytes", len(repData))
 | |
| 		}
 | |
| 
 | |
| 		binary.BigEndian.PutUint32(length[:], uint32(len(repData)))
 | |
| 		if _, err := c.Write(length[:]); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if _, err := c.Write(repData); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| }
 |