flounder

A simple gemini site builder
Log | Files | Refs | README | LICENSE

commit 60a52059eba7a22d2fa7d28328e3a296271aeb79
parent 897b5345c3bf2b1f452e07c8df255e8a1caf7cf7
Author: alex wennerberg <alex@alexwennerberg.com>
Date:   Mon, 29 Nov 2021 17:34:07 -0800

fixy fix

Diffstat:
Madmin.go | 12++++++------
Mfinger.go | 3+--
Mgemini.go | 5++---
Mhttp.go | 17++++++++---------
Msftp.go | 11+++++------
Mutils.go | 14+++++++++++---
6 files changed, 33 insertions(+), 29 deletions(-)

diff --git a/admin.go b/admin.go @@ -9,14 +9,14 @@ package main import ( "flag" "fmt" - "golang.org/x/crypto/bcrypt" - "golang.org/x/crypto/ssh/terminal" "io/ioutil" "log" "os" "path" - "path/filepath" "syscall" + + "golang.org/x/crypto/bcrypt" + "golang.org/x/crypto/ssh/terminal" ) // TODO improve cli @@ -102,8 +102,8 @@ And here's a guide to the text format that this site uses to create pages. These => //admin.flounder.online/gemini_text_guide.gmi Have fun!` - // Redundant filepath.Clean call just in case. - username = filepath.Clean(username) + // Redundant cleanPath call just in case. + username = cleanPath(username) os.Mkdir(path.Join(c.FilesDirectory, username), os.ModePerm) ioutil.WriteFile(path.Join(c.FilesDirectory, username, "index.gmi"), []byte(baseIndex), 0644) os.Mkdir(path.Join(c.FilesDirectory, username), os.ModePerm) @@ -151,7 +151,7 @@ func deleteUser(username string) error { if err != nil { return err } - username = filepath.Clean(username) + username = cleanPath(username) err = os.RemoveAll(path.Join(c.FilesDirectory, username)) if err != nil { // bad state diff --git a/finger.go b/finger.go @@ -6,7 +6,6 @@ import ( "log" "os" "path" - "path/filepath" "strings" "time" @@ -26,7 +25,7 @@ func runFingerServer() { w.Write([]byte(strings.Join(users, "\n"))) return } - userName := filepath.Clean(q.Username) + userName := cleanPath(q.Username) fullPath := path.Join(c.FilesDirectory, userName, ".plan") f, err := os.Open(fullPath) if err != nil { diff --git a/gemini.go b/gemini.go @@ -8,7 +8,6 @@ import ( "log" "os" "path" - "path/filepath" "strings" "text/template" "time" @@ -90,9 +89,9 @@ func gmiPage(_ context.Context, w gmi.ResponseWriter, r *gmi.Request) { if custom != "" { userName = custom } else { - userName = filepath.Clean(strings.Split(r.URL.Host, ".")[0]) // clean probably unnecessary + userName = cleanPath(strings.Split(r.URL.Host, ".")[0]) // clean probably unnecessary } - fileName := filepath.Clean(r.URL.Path) + fileName := cleanPath(r.URL.Path) if strings.HasPrefix(fileName, "/"+HiddenFolder) { w.WriteHeader(gmi.StatusNotFound, "Not found") return diff --git a/http.go b/http.go @@ -12,7 +12,6 @@ import ( "net/url" "os" "path" - "path/filepath" "strings" "time" @@ -53,7 +52,7 @@ func renderError(w http.ResponseWriter, errorMsg string, statusCode int) { func rootHandler(w http.ResponseWriter, r *http.Request) { // serve everything inside static directory if r.URL.Path != "/" { - fileName := path.Join(c.TemplatesDirectory, "static", filepath.Clean(r.URL.Path)) + fileName := path.Join(c.TemplatesDirectory, "static", cleanPath(r.URL.Path)) _, err := os.Stat(fileName) if err != nil { renderDefaultError(w, http.StatusNotFound) @@ -92,7 +91,7 @@ func updatesHandler(w http.ResponseWriter, r *http.Request) { authUser := getAuthUser(r) var username string if strings.HasSuffix(r.URL.Path, "atom.xml") { - username = filepath.Clean(r.URL.Path[len("/updates/") : len(r.URL.Path)-len("atom.xml")]) + username = cleanPath(r.URL.Path[len("/updates/") : len(r.URL.Path)-len("atom.xml")]) w.Header().Set("Content-Type", "application/atom+xml") // build atom feed files, err := getUpdatedFiles(authUser.IsAdmin, username) @@ -122,7 +121,7 @@ func updatesHandler(w http.ResponseWriter, r *http.Request) { io.Copy(w, strings.NewReader(res)) return } else { - username = filepath.Clean(r.URL.Path[len("/updates/"):]) + username = cleanPath(r.URL.Path[len("/updates/"):]) } files, err := getUpdatedFiles(authUser.IsAdmin, username) if err != nil { @@ -148,7 +147,7 @@ func editFileHandler(w http.ResponseWriter, r *http.Request) { renderDefaultError(w, http.StatusForbidden) return } - fileName := filepath.Clean(r.URL.Path[len("/edit/"):]) + fileName := cleanPath(r.URL.Path[len("/edit/"):]) filePath := path.Join(c.FilesDirectory, user.Username, fileName) isText := isTextFile(filePath) alert := "" @@ -178,7 +177,7 @@ func editFileHandler(w http.ResponseWriter, r *http.Request) { } // create directories if dne os.MkdirAll(path.Dir(filePath), os.ModePerm) - newName := filepath.Clean(r.Form.Get("rename")) + newName := cleanPath(r.Form.Get("rename")) err = checkIfValidFile(user.Username, newName, fileBytes) if err != nil { log.Println(err) @@ -255,7 +254,7 @@ func uploadFilesHandler(w http.ResponseWriter, r *http.Request) { renderError(w, "No file selected. Please go back and select a file.", http.StatusBadRequest) return } - fileName := filepath.Clean(fileHeader.Filename) + fileName := cleanPath(fileHeader.Filename) defer file.Close() dest, _ := ioutil.ReadAll(file) err = checkIfValidFile(user.Username, fileName, dest) @@ -569,14 +568,14 @@ func userFile(w http.ResponseWriter, r *http.Request) { if custom != "" { userName = custom } else { - userName = filepath.Clean(strings.Split(r.Host, ".")[0]) // Clean probably unnecessary + userName = cleanPath(strings.Split(r.Host, ".")[0]) // Clean probably unnecessary } unescaped, err := url.QueryUnescape(r.URL.Path) if err != nil { serverError(w, err) return } - p := filepath.Clean(unescaped) + p := cleanPath(unescaped) fmt.Println(p) var isDir bool fullPath := path.Join(c.FilesDirectory, userName, p) // TODO rename filepath diff --git a/sftp.go b/sftp.go @@ -14,7 +14,6 @@ import ( "net" "os" "path" - "path/filepath" "runtime/debug" "time" @@ -29,7 +28,7 @@ type Connection struct { func (con *Connection) Fileread(request *sftp.Request) (io.ReaderAt, error) { // check user perms -- cant read others hidden files userDir := getUserDirectory(con.User) // NOTE -- not cross platform - fullpath := path.Join(userDir, filepath.Clean(request.Filepath)) + fullpath := path.Join(userDir, cleanPath(request.Filepath)) f, err := os.OpenFile(fullpath, os.O_RDONLY, 0) if err != nil { return nil, err @@ -40,7 +39,7 @@ func (con *Connection) Fileread(request *sftp.Request) (io.ReaderAt, error) { func (conn *Connection) Filewrite(request *sftp.Request) (io.WriterAt, error) { // check user perms -- cant write others files userDir := getUserDirectory(conn.User) // NOTE -- not cross platform - fullpath := path.Join(userDir, filepath.Clean(request.Filepath)) + fullpath := path.Join(userDir, cleanPath(request.Filepath)) err := checkIfValidFile(conn.User, fullpath, []byte{}) if err != nil { return nil, err @@ -54,7 +53,7 @@ func (conn *Connection) Filewrite(request *sftp.Request) (io.WriterAt, error) { func (conn *Connection) Filelist(request *sftp.Request) (sftp.ListerAt, error) { userDir := getUserDirectory(conn.User) // NOTE -- not cross platform - fullpath := path.Join(userDir, filepath.Clean(request.Filepath)) + fullpath := path.Join(userDir, cleanPath(request.Filepath)) switch request.Method { case "List": f, err := os.Open(fullpath) @@ -79,8 +78,8 @@ func (conn *Connection) Filelist(request *sftp.Request) (sftp.ListerAt, error) { func (conn *Connection) Filecmd(request *sftp.Request) error { // remove, rename, setstat? find out userDir := getUserDirectory(conn.User) // NOTE -- not cross platform - fullpath := path.Join(userDir, filepath.Clean(request.Filepath)) - targetPath := path.Join(userDir, filepath.Clean(request.Target)) + fullpath := path.Join(userDir, cleanPath(request.Filepath)) + targetPath := path.Join(userDir, cleanPath(request.Target)) var err error switch request.Method { case "Remove": diff --git a/utils.go b/utils.go @@ -133,13 +133,21 @@ func GetIPFromRemoteAddress(remoteAddress string) string { // safe func getUserDirectory(username string) string { // extra filepath.clean just to be safe - userFolder := path.Join(c.FilesDirectory, filepath.Clean(username)) + userFolder := path.Join(c.FilesDirectory, cleanPath(username)) return userFolder } -// ugh idk func safeGetFilePath(username string, filename string) string { - return path.Join(getUserDirectory(username), filepath.Clean(filename)) + return path.Join(getUserDirectory(username), cleanPath(filename)) +} + +// Safe +func cleanPath(thepath string) string { + res := filepath.FromSlash(path.Clean("/" + strings.Trim(thepath, "/"))) + if strings.Contains(res, "..") { // sanity check + return "" + } + return res[1:] } func dirSize(path string) (int64, error) {