wings/router/router_download.go
Matthew Penner d1c0ca5260
server(filesystem): rebuild everything imaginable
This wonderfully large commit replaces basically everything under the
`server/filesystem` package, re-implementing essentially everything.

This is related to
https://github.com/pterodactyl/wings/security/advisories/GHSA-494h-9924-xww9

If any vulnerabilities related to symlinks persist after this commit, I
will be very upset.

Signed-off-by: Matthew Penner <me@matthewp.io>
2024-03-13 12:27:29 -06:00

100 lines
2.5 KiB
Go

package router
import (
"bufio"
"errors"
"net/http"
"os"
"strconv"
"github.com/gin-gonic/gin"
"github.com/pterodactyl/wings/router/middleware"
"github.com/pterodactyl/wings/router/tokens"
"github.com/pterodactyl/wings/server/backup"
)
// Handle a download request for a server backup.
func getDownloadBackup(c *gin.Context) {
client := middleware.ExtractApiClient(c)
manager := middleware.ExtractManager(c)
token := tokens.BackupPayload{}
if err := tokens.ParseToken([]byte(c.Query("token")), &token); err != nil {
middleware.CaptureAndAbort(c, err)
return
}
if _, ok := manager.Get(token.ServerUuid); !ok || !token.IsUniqueRequest() {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
"error": "The requested resource was not found on this server.",
})
return
}
b, st, err := backup.LocateLocal(client, token.BackupUuid)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
"error": "The requested backup was not found on this server.",
})
return
}
middleware.CaptureAndAbort(c, err)
return
}
// The use of `os` here is safe as backups are not stored within server
// accessible directories.
f, err := os.Open(b.Path())
if err != nil {
middleware.CaptureAndAbort(c, err)
return
}
defer f.Close()
c.Header("Content-Length", strconv.Itoa(int(st.Size())))
c.Header("Content-Disposition", "attachment; filename="+strconv.Quote(st.Name()))
c.Header("Content-Type", "application/octet-stream")
_, _ = bufio.NewReader(f).WriteTo(c.Writer)
}
// Handles downloading a specific file for a server.
func getDownloadFile(c *gin.Context) {
manager := middleware.ExtractManager(c)
token := tokens.FilePayload{}
if err := tokens.ParseToken([]byte(c.Query("token")), &token); err != nil {
middleware.CaptureAndAbort(c, err)
return
}
s, ok := manager.Get(token.ServerUuid)
if !ok || !token.IsUniqueRequest() {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
"error": "The requested resource was not found on this server.",
})
return
}
f, st, err := s.Filesystem().File(token.FilePath)
if err != nil {
middleware.CaptureAndAbort(c, err)
return
}
defer f.Close()
if st.IsDir() {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
"error": "The requested resource was not found on this server.",
})
return
}
c.Header("Content-Length", strconv.Itoa(int(st.Size())))
c.Header("Content-Disposition", "attachment; filename="+strconv.Quote(st.Name()))
c.Header("Content-Type", "application/octet-stream")
_, _ = bufio.NewReader(f).WriteTo(c.Writer)
}