/
session.go
106 lines (94 loc) · 2.42 KB
/
session.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package gitolite
import (
"errors"
"fmt"
"io"
"log"
"path/filepath"
"golang.org/x/crypto/ssh"
)
type session struct {
handler interface{}
channel ssh.Channel
requests <-chan *ssh.Request
}
func (s *session) loop() {
defer s.channel.Close()
for req := range s.requests {
switch req.Type {
case "exec":
if err := s.handleExec(req); err != nil {
log.Println(err)
req.Reply(false, nil)
return
}
req.Reply(true, nil)
return
case "pty-req":
// do not allocate a pty since we just want to allow the upcoming shell request
// so we can return a version string
req.Reply(true, nil)
case "shell":
io.WriteString(s.channel, "Authentication succeeded.\n")
req.Reply(true, nil)
return
default:
if req.WantReply {
req.Reply(false, nil)
}
}
}
}
type execRequest struct {
Command string
}
func (s *session) handleExec(req *ssh.Request) error {
cmd := execRequest{}
if err := ssh.Unmarshal(req.Payload, &cmd); err != nil {
return err
}
args, err := shellArgs([]byte(cmd.Command))
if err != nil {
return err
}
if err := s.exec(args); err != nil {
log.Println("handleExec:", err)
return fmt.Errorf("%s: %s", err, cmd.Command)
}
return nil
}
func (s *session) exec(args []string) error {
if len(args) != 2 {
return errors.New("Invalid command")
}
// Take the command from the first argument and the path from the second.
// Join the path with the root path and clean it up. This should prevent
// any attacks where ../.. are used to try and gain additional access to
// the system because we are always assuming the path referenced is always
// relative to the root. We want to do this clean up before we pass it to
// the handler so the handler doesn't have to worry about validation.
// This also means that the path passed to the underlying handler will
// always have a slash at the beginning.
command := args[0]
path := filepath.Clean(filepath.Join("/", args[1]))
w := channelWriter{s.channel}
r := channelReader{s.channel}
switch command {
case "git-upload-pack":
switch handler := s.handler.(type) {
case UploadPack:
return handler.UploadPack(path, &r, &w)
default:
return errors.New("Unsupported operation")
}
case "git-receive-pack":
switch handler := s.handler.(type) {
case ReceivePack:
return handler.ReceivePack(path, &r, &w)
default:
return errors.New("Unsupported operation")
}
default:
return errors.New("Invalid command")
}
}