From bc72e4226e669d98c8e0f3eccc9297be9251c692 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Thu, 28 Jul 2022 17:44:21 -0700 Subject: [PATCH] do not allow filesystem fallback in server download (#15429) It is possible for anyone with admin access to relatively to get any content of any random OS location by simply providing the file with 'mc admin update alias/ /etc/passwd`. Workaround is to disable 'admin:ServiceUpdate' action. Everyone is advised to upgrade to this patch. Thanks to @alevsk for finding this bug. --- cmd/update.go | 81 +++++++++++++++++++++------------------------------ 1 file changed, 34 insertions(+), 47 deletions(-) diff --git a/cmd/update.go b/cmd/update.go index c027a6308..8cae892e5 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -291,63 +291,50 @@ func getUserAgent(mode string) string { } func downloadReleaseURL(u *url.URL, timeout time.Duration, mode string) (content string, err error) { - var reader io.ReadCloser - if u.Scheme == "https" || u.Scheme == "http" { - req, err := http.NewRequest(http.MethodGet, u.String(), nil) - if err != nil { - return content, AdminError{ - Code: AdminUpdateUnexpectedFailure, - Message: err.Error(), - StatusCode: http.StatusInternalServerError, - } + req, err := http.NewRequest(http.MethodGet, u.String(), nil) + if err != nil { + return content, AdminError{ + Code: AdminUpdateUnexpectedFailure, + Message: err.Error(), + StatusCode: http.StatusInternalServerError, } - req.Header.Set("User-Agent", getUserAgent(mode)) + } + req.Header.Set("User-Agent", getUserAgent(mode)) - client := &http.Client{Transport: getUpdateTransport(timeout)} - resp, err := client.Do(req) - if err != nil { - if xnet.IsNetworkOrHostDown(err, false) { - return content, AdminError{ - Code: AdminUpdateURLNotReachable, - Message: err.Error(), - StatusCode: http.StatusServiceUnavailable, - } - } - return content, AdminError{ - Code: AdminUpdateUnexpectedFailure, - Message: err.Error(), - StatusCode: http.StatusInternalServerError, - } - } - if resp == nil { - return content, AdminError{ - Code: AdminUpdateUnexpectedFailure, - Message: fmt.Sprintf("No response from server to download URL %s", u), - StatusCode: http.StatusInternalServerError, - } - } - reader = resp.Body - defer xhttp.DrainBody(resp.Body) - - if resp.StatusCode != http.StatusOK { - return content, AdminError{ - Code: AdminUpdateUnexpectedFailure, - Message: fmt.Sprintf("Error downloading URL %s. Response: %v", u, resp.Status), - StatusCode: resp.StatusCode, - } - } - } else { - reader, err = os.Open(u.Path) - if err != nil { + client := &http.Client{Transport: getUpdateTransport(timeout)} + resp, err := client.Do(req) + if err != nil { + if xnet.IsNetworkOrHostDown(err, false) { return content, AdminError{ Code: AdminUpdateURLNotReachable, Message: err.Error(), StatusCode: http.StatusServiceUnavailable, } } + return content, AdminError{ + Code: AdminUpdateUnexpectedFailure, + Message: err.Error(), + StatusCode: http.StatusInternalServerError, + } + } + if resp == nil { + return content, AdminError{ + Code: AdminUpdateUnexpectedFailure, + Message: fmt.Sprintf("No response from server to download URL %s", u), + StatusCode: http.StatusInternalServerError, + } + } + defer xhttp.DrainBody(resp.Body) + + if resp.StatusCode != http.StatusOK { + return content, AdminError{ + Code: AdminUpdateUnexpectedFailure, + Message: fmt.Sprintf("Error downloading URL %s. Response: %v", u, resp.Status), + StatusCode: resp.StatusCode, + } } - contentBytes, err := ioutil.ReadAll(reader) + contentBytes, err := ioutil.ReadAll(resp.Body) if err != nil { return content, AdminError{ Code: AdminUpdateUnexpectedFailure,