From 358d442921db5f6ca9a77a2daab1222eac4bbdff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Mon, 4 Dec 2023 15:41:18 -0500 Subject: [PATCH] incusd/images: Perform access control after fingerprint expansion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- cmd/incusd/images.go | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/cmd/incusd/images.go b/cmd/incusd/images.go index 0ac76bffdf9..6e5d7941661 100644 --- a/cmd/incusd/images.go +++ b/cmd/incusd/images.go @@ -2795,17 +2795,7 @@ func imageGet(d *Daemon, r *http.Request) response.Response { return response.SmartError(err) } - var userCanViewImage bool - err = s.Authorizer.CheckPermission(r.Context(), r, auth.ObjectImage(projectName, fingerprint), auth.EntitlementCanView) - if err == nil { - userCanViewImage = true - } else if !api.StatusErrorCheck(err, http.StatusForbidden) { - return response.SmartError(err) - } - - public := d.checkTrustedClient(r) != nil || !userCanViewImage - secret := r.FormValue("secret") - + // Get the image (expand partial fingerprints). var info *api.Image err = s.DB.Cluster.Transaction(r.Context(), func(ctx context.Context, tx *db.ClusterTx) error { info, err = doImageGet(ctx, tx, projectName, fingerprint, false) @@ -2819,6 +2809,17 @@ func imageGet(d *Daemon, r *http.Request) response.Response { return response.SmartError(err) } + var userCanViewImage bool + err = s.Authorizer.CheckPermission(r.Context(), r, auth.ObjectImage(projectName, info.Fingerprint), auth.EntitlementCanView) + if err == nil { + userCanViewImage = true + } else if !api.StatusErrorCheck(err, http.StatusForbidden) { + return response.SmartError(err) + } + + public := d.checkTrustedClient(r) != nil || !userCanViewImage + secret := r.FormValue("secret") + op, err := imageValidSecret(s, r, projectName, info.Fingerprint, secret) if err != nil { return response.SmartError(err) @@ -3794,8 +3795,15 @@ func imageExport(d *Daemon, r *http.Request) response.Response { return response.SmartError(err) } + // Get the image (expand the fingerprint). + _, imgInfo, err := s.DB.Cluster.GetImage(fingerprint, dbCluster.ImageFilter{Project: &projectName}) + if err != nil { + return response.SmartError(err) + } + + // Access control. var userCanViewImage bool - err = s.Authorizer.CheckPermission(r.Context(), r, auth.ObjectImage(projectName, fingerprint), auth.EntitlementCanView) + err = s.Authorizer.CheckPermission(r.Context(), r, auth.ObjectImage(projectName, imgInfo.Fingerprint), auth.EntitlementCanView) if err == nil { userCanViewImage = true } else if !api.StatusErrorCheck(err, http.StatusForbidden) { @@ -3805,23 +3813,16 @@ func imageExport(d *Daemon, r *http.Request) response.Response { public := d.checkTrustedClient(r) != nil || !userCanViewImage secret := r.FormValue("secret") - var imgInfo *api.Image if r.RemoteAddr == "@dev_incus" { // /dev/incus API requires exact match - _, imgInfo, err = s.DB.Cluster.GetImage(fingerprint, dbCluster.ImageFilter{Project: &projectName}) - if err != nil { - return response.SmartError(err) + if imgInfo.Fingerprint != fingerprint { + return response.NotFound(fmt.Errorf("Image %q not found", fingerprint)) } if !imgInfo.Public && !imgInfo.Cached { return response.NotFound(fmt.Errorf("Image %q not found", fingerprint)) } } else { - _, imgInfo, err = s.DB.Cluster.GetImage(fingerprint, dbCluster.ImageFilter{Project: &projectName}) - if err != nil { - return response.SmartError(err) - } - op, err := imageValidSecret(s, r, projectName, imgInfo.Fingerprint, secret) if err != nil { return response.SmartError(err)