serve dlna: bound SOAP request bodies
The unauthenticated DLNA control endpoint decoded arbitrary SOAP bodies into an in-memory XML field. A LAN client could send a large request and exhaust the server's memory. Limit SOAP request bodies to 1 MiB and return 413 when the limit is exceeded.
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -148,6 +149,7 @@ const (
|
||||
rootDescPath = "/rootDesc.xml"
|
||||
resPath = "/r/"
|
||||
serviceControlURL = "/ctl"
|
||||
maxSOAPBodySize = 1 << 20
|
||||
)
|
||||
|
||||
type server struct {
|
||||
@@ -303,7 +305,13 @@ func (s *server) serviceControlHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
var env soap.Envelope
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxSOAPBodySize)
|
||||
if err := xml.NewDecoder(r.Body).Decode(&env); err != nil {
|
||||
var maxBytesErr *http.MaxBytesError
|
||||
if errors.As(err, &maxBytesErr) {
|
||||
http.Error(w, "SOAP request body too large", http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
serveError(ctx, s, w, "Could not parse SOAP request body", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -57,6 +57,22 @@ func TestInit(t *testing.T) {
|
||||
startServer(t, f)
|
||||
}
|
||||
|
||||
func TestServiceControlRejectsOversizedBody(t *testing.T) {
|
||||
body := `<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body>` +
|
||||
`<u:RegisterDevice xmlns:u="urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1">` +
|
||||
strings.Repeat("x", maxSOAPBodySize) +
|
||||
`</u:RegisterDevice></s:Body></s:Envelope>`
|
||||
req, err := http.NewRequest("POST", baseURL+serviceControlURL, strings.NewReader(body))
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("SOAPACTION", `"urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1#RegisterDevice"`)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, resp.Body.Close())
|
||||
}()
|
||||
assert.Equal(t, http.StatusRequestEntityTooLarge, resp.StatusCode)
|
||||
}
|
||||
|
||||
// Make sure that it serves rootDesc.xml (SCPD in uPnP parlance).
|
||||
func TestRootSCPD(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", baseURL+rootDescPath, nil)
|
||||
|
||||
Reference in New Issue
Block a user