Skip to content
This repository was archived by the owner on Jul 8, 2020. It is now read-only.

Commit 08acff7

Browse files
committed
Merge branch 'notorca-master'
2 parents 60c0b22 + 5adf095 commit 08acff7

5 files changed

Lines changed: 142 additions & 77 deletions

File tree

cmd.go

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ var (
4040
"ENC": commandEnc{},
4141
"EPRT": commandEprt{},
4242
"EPSV": commandEpsv{},
43-
//"FEAT": commandFeat{},
43+
"FEAT": commandFeat{},
4444
"LIST": commandList{},
4545
"NLST": commandNlst{},
4646
"MDTM": commandMdtm{},
@@ -175,6 +175,9 @@ func init() {
175175
}
176176

177177
func (cmd commandFeat) Execute(conn *Conn, param string) {
178+
if conn.tlsConfig != nil {
179+
featCmds += " AUTH TLS\n PBSZ\n PROT\n"
180+
}
178181
conn.writeMessage(211, fmt.Sprintf(feats, featCmds))
179182
}
180183

@@ -314,7 +317,7 @@ func (cmd commandEpsv) Execute(conn *Conn, param string) {
314317
return
315318
}
316319

317-
socket, err := newPassiveSocket(addr.String()[:lastIdx], conn.logger)
320+
socket, err := newPassiveSocket(addr.String()[:lastIdx], conn.logger, conn.tlsConfig)
318321
if err != nil {
319322
log.Error(err)
320323
conn.writeMessage(425, "Data connection failed")
@@ -595,7 +598,7 @@ func (cmd commandPasv) Execute(conn *Conn, param string) {
595598
conn.writeMessage(425, "Data connection failed")
596599
return
597600
}
598-
socket, err := newPassiveSocket(parts[0], conn.logger)
601+
socket, err := newPassiveSocket(parts[0], conn.logger, conn.tlsConfig)
599602
if err != nil {
600603
conn.writeMessage(425, "Data connection failed")
601604
return
@@ -853,7 +856,16 @@ func (cmd commandAuth) RequireAuth() bool {
853856
}
854857

855858
func (cmd commandAuth) Execute(conn *Conn, param string) {
856-
conn.writeMessage(550, "Action not taken")
859+
log.Println(param, conn)
860+
if param == "TLS" && conn.tlsConfig != nil {
861+
conn.writeMessage(234, "AUTH command OK")
862+
err := conn.upgradeToTLS()
863+
if err != nil {
864+
conn.logger.Printf("Error upgrading conection to TLS %v", err)
865+
}
866+
} else {
867+
conn.writeMessage(550, "Action not taken")
868+
}
857869
}
858870

859871
type commandCcc struct{}
@@ -925,7 +937,11 @@ func (cmd commandPbsz) RequireAuth() bool {
925937
}
926938

927939
func (cmd commandPbsz) Execute(conn *Conn, param string) {
928-
conn.writeMessage(550, "Action not taken")
940+
if conn.tls && param == "0" {
941+
conn.writeMessage(200, "OK")
942+
} else {
943+
conn.writeMessage(550, "Action not taken")
944+
}
929945
}
930946

931947
type commandProt struct{}
@@ -943,7 +959,13 @@ func (cmd commandProt) RequireAuth() bool {
943959
}
944960

945961
func (cmd commandProt) Execute(conn *Conn, param string) {
946-
conn.writeMessage(550, "Action not taken")
962+
if conn.tls && param == "P" {
963+
conn.writeMessage(200, "OK")
964+
} else if conn.tls {
965+
conn.writeMessage(536, "Only P level is supported")
966+
} else {
967+
conn.writeMessage(550, "Action not taken")
968+
}
947969
}
948970

949971
type commandConf struct{}

conn.go

Lines changed: 55 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bufio"
55
"crypto/rand"
66
"crypto/sha256"
7+
"crypto/tls"
78
"encoding/hex"
89
"fmt"
910
"io"
@@ -26,13 +27,16 @@ type Conn struct {
2627
auth Auth
2728
logger *Logger
2829
server *Server
29-
sessionId string
30+
tlsConfig *tls.Config
31+
sessionID string
3032
namePrefix string
3133
reqUser string
3234
user string
3335
renameFrom string
3436
lastFilePos int64
3537
appendData bool
38+
closed bool
39+
tls bool
3640
}
3741

3842
func (conn *Conn) LoginUser() string {
@@ -44,7 +48,7 @@ func (conn *Conn) IsLogin() bool {
4448
}
4549

4650
// returns a random 20 char string that can be used as a unique session ID
47-
func newSessionId() string {
51+
func newSessionID() string {
4852
hash := sha256.New()
4953
_, err := io.CopyN(hash, rand.Reader, 50)
5054
if err != nil {
@@ -76,6 +80,11 @@ func (conn *Conn) Serve() {
7680
break
7781
}
7882
conn.receiveLine(line)
83+
// QUIT command closes connection, break to avoid error on reading from
84+
// closed socket
85+
if conn.closed == true {
86+
break
87+
}
7988
}
8089
conn.Close()
8190
conn.logger.Print("Connection Terminated")
@@ -84,32 +93,46 @@ func (conn *Conn) Serve() {
8493
// Close will manually close this connection, even if the client isn't ready.
8594
func (conn *Conn) Close() {
8695
conn.conn.Close()
96+
conn.closed = true
8797
if conn.dataConn != nil {
8898
conn.dataConn.Close()
8999
conn.dataConn = nil
90100
}
91101
}
92102

103+
func (conn *Conn) upgradeToTLS() error {
104+
conn.logger.Print("Upgrading connectiion to TLS")
105+
tlsConn := tls.Server(conn.conn, conn.tlsConfig)
106+
err := tlsConn.Handshake()
107+
if err == nil {
108+
conn.conn = tlsConn
109+
conn.controlReader = bufio.NewReader(tlsConn)
110+
conn.controlWriter = bufio.NewWriter(tlsConn)
111+
conn.tls = true
112+
}
113+
return err
114+
}
115+
93116
// receiveLine accepts a single line FTP command and co-ordinates an
94117
// appropriate response.
95-
func (Conn *Conn) receiveLine(line string) {
96-
command, param := Conn.parseLine(line)
97-
Conn.logger.PrintCommand(command, param)
118+
func (conn *Conn) receiveLine(line string) {
119+
command, param := conn.parseLine(line)
120+
conn.logger.PrintCommand(command, param)
98121
cmdObj := commands[strings.ToUpper(command)]
99122
if cmdObj == nil {
100-
Conn.writeMessage(500, "Command not found")
123+
conn.writeMessage(500, "Command not found")
101124
return
102125
}
103126
if cmdObj.RequireParam() && param == "" {
104-
Conn.writeMessage(553, "action aborted, required param missing")
105-
} else if cmdObj.RequireAuth() && Conn.user == "" {
106-
Conn.writeMessage(530, "not logged in")
127+
conn.writeMessage(553, "action aborted, required param missing")
128+
} else if cmdObj.RequireAuth() && conn.user == "" {
129+
conn.writeMessage(530, "not logged in")
107130
} else {
108-
cmdObj.Execute(Conn, param)
131+
cmdObj.Execute(conn, param)
109132
}
110133
}
111134

112-
func (Conn *Conn) parseLine(line string) (string, string) {
135+
func (conn *Conn) parseLine(line string) (string, string) {
113136
params := strings.SplitN(strings.Trim(line, "\r\n"), " ", 2)
114137
if len(params) == 1 {
115138
return params[0], ""
@@ -118,11 +141,11 @@ func (Conn *Conn) parseLine(line string) (string, string) {
118141
}
119142

120143
// writeMessage will send a standard FTP response back to the client.
121-
func (Conn *Conn) writeMessage(code int, message string) (wrote int, err error) {
122-
Conn.logger.PrintResponse(code, message)
144+
func (conn *Conn) writeMessage(code int, message string) (wrote int, err error) {
145+
conn.logger.PrintResponse(code, message)
123146
line := fmt.Sprintf("%d %s\r\n", code, message)
124-
wrote, err = Conn.controlWriter.WriteString(line)
125-
Conn.controlWriter.Flush()
147+
wrote, err = conn.controlWriter.WriteString(line)
148+
conn.controlWriter.Flush()
126149
return
127150
}
128151

@@ -143,43 +166,43 @@ func (Conn *Conn) writeMessage(code int, message string) (wrote int, err error)
143166
// The driver implementation is responsible for deciding how to treat this path.
144167
// Obviously they MUST NOT just read the path off disk. The probably want to
145168
// prefix the path with something to scope the users access to a sandbox.
146-
func (Conn *Conn) buildPath(filename string) (fullPath string) {
169+
func (conn *Conn) buildPath(filename string) (fullPath string) {
147170
if len(filename) > 0 && filename[0:1] == "/" {
148171
fullPath = filepath.Clean(filename)
149172
} else if len(filename) > 0 && filename != "-a" {
150-
fullPath = filepath.Clean(Conn.namePrefix + "/" + filename)
173+
fullPath = filepath.Clean(conn.namePrefix + "/" + filename)
151174
} else {
152-
fullPath = filepath.Clean(Conn.namePrefix)
175+
fullPath = filepath.Clean(conn.namePrefix)
153176
}
154177
fullPath = strings.Replace(fullPath, "//", "/", -1)
155178
return
156179
}
157180

158181
// sendOutofbandData will send a string to the client via the currently open
159182
// data socket. Assumes the socket is open and ready to be used.
160-
func (Conn *Conn) sendOutofbandData(data []byte) {
183+
func (conn *Conn) sendOutofbandData(data []byte) {
161184
bytes := len(data)
162-
if Conn.dataConn != nil {
163-
Conn.dataConn.Write(data)
164-
Conn.dataConn.Close()
165-
Conn.dataConn = nil
185+
if conn.dataConn != nil {
186+
conn.dataConn.Write(data)
187+
conn.dataConn.Close()
188+
conn.dataConn = nil
166189
}
167190
message := "Closing data connection, sent " + strconv.Itoa(bytes) + " bytes"
168-
Conn.writeMessage(226, message)
191+
conn.writeMessage(226, message)
169192
}
170193

171-
func (Conn *Conn) sendOutofBandDataWriter(data io.ReadCloser) error {
172-
Conn.lastFilePos = 0
173-
bytes, err := io.Copy(Conn.dataConn, data)
194+
func (conn *Conn) sendOutofBandDataWriter(data io.ReadCloser) error {
195+
conn.lastFilePos = 0
196+
bytes, err := io.Copy(conn.dataConn, data)
174197
if err != nil {
175-
Conn.dataConn.Close()
176-
Conn.dataConn = nil
198+
conn.dataConn.Close()
199+
conn.dataConn = nil
177200
return err
178201
}
179202
message := "Closing data connection, sent " + strconv.Itoa(int(bytes)) + " bytes"
180-
Conn.writeMessage(226, message)
181-
Conn.dataConn.Close()
182-
Conn.dataConn = nil
203+
conn.writeMessage(226, message)
204+
conn.dataConn.Close()
205+
conn.dataConn = nil
183206

184207
return nil
185208
}

logger.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@ import (
77

88
// Use an instance of this to log in a standard format
99
type Logger struct {
10-
sessionId string
10+
sessionID string
1111
}
1212

1313
func newLogger(id string) *Logger {
1414
l := new(Logger)
15-
l.sessionId = id
15+
l.sessionID = id
1616
return l
1717
}
1818

1919
func (logger *Logger) Print(message interface{}) {
20-
log.Printf("%s %s", logger.sessionId, message)
20+
log.Printf("%s %s", logger.sessionID, message)
2121
}
2222

2323
func (logger *Logger) Printf(format string, v ...interface{}) {
@@ -26,12 +26,12 @@ func (logger *Logger) Printf(format string, v ...interface{}) {
2626

2727
func (logger *Logger) PrintCommand(command string, params string) {
2828
if command == "PASS" {
29-
log.Printf("%s > PASS ****", logger.sessionId)
29+
log.Printf("%s > PASS ****", logger.sessionID)
3030
} else {
31-
log.Printf("%s > %s %s", logger.sessionId, command, params)
31+
log.Printf("%s > %s %s", logger.sessionID, command, params)
3232
}
3333
}
3434

3535
func (logger *Logger) PrintResponse(code int, message string) {
36-
log.Printf("%s < %d %s", logger.sessionId, code, message)
36+
log.Printf("%s < %d %s", logger.sessionID, code, message)
3737
}

0 commit comments

Comments
 (0)