From f48750eafed5f522a2e11622c8b20f4a7685bad6 Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Thu, 7 Apr 2016 12:10:12 +0200 Subject: parse multipart form as a stream to reject wrong authentication before the whole file got uploaded diff --git a/src/rhimportd/uploadWeb.go b/src/rhimportd/uploadWeb.go index 1c53fbc..ee86e0f 100644 --- a/src/rhimportd/uploadWeb.go +++ b/src/rhimportd/uploadWeb.go @@ -25,11 +25,14 @@ package main import ( + "bytes" "code.helsinki.at/rhrd-go/rddb" "code.helsinki.at/rhrd-go/rhimport" "encoding/json" + "fmt" "io" "io/ioutil" + "mime/multipart" "net/http" "os" "strings" @@ -62,6 +65,50 @@ const ( webUploadMaxRequestSize = (2 << 30) - 1 // 2GB, (2 << 30) overflows int on 32-bit systems therefore we use 2GB - 1 Byte ) +func webUploadParseForm(w http.ResponseWriter, r *http.Request) (username, password, srcfile string, src *multipart.Part, ok bool) { + mpr, err := r.MultipartReader() + if err != nil { + rhl.Printf("WebUploadHandler: error while parsing multi-part-form: %v", err) + webUploadErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + for { + p, err := mpr.NextPart() + if err != nil { + rhl.Printf("WebUploadHandler: error while parsing multi-part-form: %v", err) + webUploadErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + var dstfield *string + switch p.FormName() { + case "LOGIN_NAME": + dstfield = &username + case "PASSWORD": + dstfield = &password + case "FILENAME": + srcfile = p.FileName() + src = p + ok = true + return + default: + rhl.Printf("WebUploadHandler: unknown form field: '%s'", p.FormName()) + webUploadErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("unknown field %s", p.FormName())) + return + } + + var buf bytes.Buffer + if _, err := io.CopyN(&buf, p, 1<<10); err != nil && err != io.EOF { // 1kB should be enough + rhl.Printf("WebUploadHandler: error while extracting form field: %v", err) + webUploadErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + *dstfield = buf.String() + p.Close() + } + return +} + func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport.SessionStoreChan, trusted bool, w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { http.Redirect(w, r, "/static/upload-form.html", http.StatusTemporaryRedirect) @@ -81,14 +128,11 @@ func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport return } r.Body = http.MaxBytesReader(w, r.Body, webUploadMaxRequestSize) - if err := r.ParseMultipartForm(32 << 10); err != nil { - rhl.Printf("WebUploadHandler: error while parsing multi-part-form: %v", err) - webUploadErrorResponse(w, http.StatusBadRequest, err.Error()) + + username, password, srcfile, src, ok := webUploadParseForm(w, r) + if !ok { return } - - username := r.FormValue("LOGIN_NAME") - password := r.FormValue("PASSWORD") if username == "" { webUploadErrorResponse(w, http.StatusBadRequest, "missing field LOGIN_NAME") return @@ -108,15 +152,7 @@ func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport return } - src, hdr, err := r.FormFile("FILENAME") - if err != nil { - rhdl.Printf("WebUploadHandler: error looking for upload file in form: %s", err) - webUploadErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - defer src.Close() - - rhdl.Printf("WebUploadHandler: got request from user '%s', filename='%s'", username, hdr.Filename) + rhdl.Printf("WebUploadHandler: got request from user '%s', filename='%s'", username, srcfile) dstpath, err := ioutil.TempDir(conf.TempDir, "webupload-"+username+"-") if err != nil { @@ -125,7 +161,7 @@ func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport return } - dstfile := dstpath + "/" + hdr.Filename + dstfile := dstpath + "/" + srcfile dst, err := os.OpenFile(dstfile, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600) if err != nil { rhl.Printf("WebUploadHandler: Unable to create file '%s': %v", dstfile, err) -- cgit v0.10.2