Skip to content

Commit

Permalink
circumvent permission check when creating internal links
Browse files Browse the repository at this point in the history
Signed-off-by: jkoberg <jkoberg@owncloud.com>
  • Loading branch information
kobergj committed Apr 26, 2023
1 parent 100c192 commit a8714c0
Showing 1 changed file with 78 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,27 +53,39 @@ func (h *Handler) createPublicLinkShare(w http.ResponseWriter, r *http.Request,
}
}

user := ctxpkg.ContextMustGetUser(ctx)
resp, err := c.CheckPermission(ctx, &permissionsv1beta1.CheckPermissionRequest{
SubjectRef: &permissionsv1beta1.SubjectReference{
Spec: &permissionsv1beta1.SubjectReference_UserId{
UserId: user.Id,
},
},
Permission: "PublicLink.Write",
})
permKey, err := permKeyFromRequest(r, h)
if err != nil {
return nil, &ocsError{
Code: response.MetaServerError.StatusCode,
Message: "failed to check user permission",
Code: response.MetaBadRequest.StatusCode,
Message: "Could not read permission from request",
Error: err,
}
}

if resp.Status.Code != rpc.Code_CODE_OK {
return nil, &ocsError{
Code: response.MetaForbidden.StatusCode,
Message: "user is not allowed to create a public link",
// NOTE: one is allowed to create an internal link without the `Publink.Write` permission
if permKey != 0 {
user := ctxpkg.ContextMustGetUser(ctx)
resp, err := c.CheckPermission(ctx, &permissionsv1beta1.CheckPermissionRequest{
SubjectRef: &permissionsv1beta1.SubjectReference{
Spec: &permissionsv1beta1.SubjectReference_UserId{
UserId: user.Id,
},
},
Permission: "PublicLink.Write",
})
if err != nil {
return nil, &ocsError{
Code: response.MetaServerError.StatusCode,
Message: "failed to check user permission",
Error: err,
}
}

if resp.Status.Code != rpc.Code_CODE_OK {
return nil, &ocsError{
Code: response.MetaForbidden.StatusCode,
Message: "user is not allowed to create a public link",
}
}
}

Expand Down Expand Up @@ -113,19 +125,18 @@ func (h *Handler) createPublicLinkShare(w http.ResponseWriter, r *http.Request,
}
}

newPermissions, err := permissionFromRequest(r, h)
permissions, err := ocPublicPermToCs3(permKey, h)
if err != nil {
return nil, &ocsError{
Code: response.MetaBadRequest.StatusCode,
Message: "Could not read permission from request",
Message: "Could not create permission from permission key",
Error: err,
}
}

if newPermissions == nil {
if permissions == nil {
// default perms: read-only
// TODO: the default might change depending on allowed permissions and configs
newPermissions, err = ocPublicPermToCs3(1, h)
permissions, err = ocPublicPermToCs3(1, h)
if err != nil {
return nil, &ocsError{
Code: response.MetaServerError.StatusCode,
Expand All @@ -137,14 +148,14 @@ func (h *Handler) createPublicLinkShare(w http.ResponseWriter, r *http.Request,

if statInfo != nil && statInfo.Type == provider.ResourceType_RESOURCE_TYPE_FILE {
// Single file shares should never have delete or create permissions
role := conversions.RoleFromResourcePermissions(newPermissions, true)
permissions := role.OCSPermissions()
permissions &^= conversions.PermissionCreate
permissions &^= conversions.PermissionDelete
newPermissions = conversions.RoleFromOCSPermissions(permissions).CS3ResourcePermissions()
role := conversions.RoleFromResourcePermissions(permissions, true)
p := role.OCSPermissions()
p &^= conversions.PermissionCreate
p &^= conversions.PermissionDelete
permissions = conversions.RoleFromOCSPermissions(p).CS3ResourcePermissions()
}

if !sufficientPermissions(statInfo.PermissionSet, newPermissions, true) {
if !sufficientPermissions(statInfo.PermissionSet, permissions, true) {
response.WriteOCSError(w, r, http.StatusNotFound, "no share permission", nil)
return nil, &ocsError{
Code: http.StatusNotFound,
Expand All @@ -157,7 +168,7 @@ func (h *Handler) createPublicLinkShare(w http.ResponseWriter, r *http.Request,
ResourceInfo: statInfo,
Grant: &link.Grant{
Permissions: &link.PublicSharePermissions{
Permissions: newPermissions,
Permissions: permissions,
},
Password: r.FormValue("password"),
},
Expand Down Expand Up @@ -296,23 +307,11 @@ func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, shar
}

ctx := r.Context()

user := ctxpkg.ContextMustGetUser(ctx)
resp, err := gwC.CheckPermission(ctx, &permissionsv1beta1.CheckPermissionRequest{
SubjectRef: &permissionsv1beta1.SubjectReference{
Spec: &permissionsv1beta1.SubjectReference_UserId{
UserId: user.Id,
},
},
Permission: "PublicLink.Write",
})
if err != nil {
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "failed to check user permission", err)
return
}

if resp.Status.Code != rpc.Code_CODE_OK {
response.WriteOCSError(w, r, response.MetaForbidden.StatusCode, "user is not allowed to create a public link", nil)
permKey, err := permKeyFromRequest(r, h)
if err != nil {
response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, "invalid permissions", err)
return
}

Expand All @@ -330,8 +329,30 @@ func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, shar
return
}

u := ctxpkg.ContextMustGetUser(r.Context())
if !publicshare.IsCreatedByUser(*before.Share, u) {
createdByUser := publicshare.IsCreatedByUser(*before.Share, user)

// NOTE: you are allowed to update a link TO a public link without the `PublicLink.Write` permission if you created it yourself
if permKey != 0 || !createdByUser {
resp, err := gwC.CheckPermission(ctx, &permissionsv1beta1.CheckPermissionRequest{
SubjectRef: &permissionsv1beta1.SubjectReference{
Spec: &permissionsv1beta1.SubjectReference_UserId{
UserId: user.Id,
},
},
Permission: "PublicLink.Write",
})
if err != nil {
response.WriteOCSError(w, r, response.MetaServerError.StatusCode, "failed to check user permission", err)
return
}

if resp.Status.Code != rpc.Code_CODE_OK {
response.WriteOCSError(w, r, response.MetaForbidden.StatusCode, "user is not allowed to update the public link", nil)
return
}
}

if !createdByUser {
sRes, err := gwC.Stat(r.Context(), &provider.StatRequest{Ref: &provider.Reference{ResourceId: before.Share.ResourceId}})
if err != nil {
log.Err(err).Interface("resource_id", before.Share.ResourceId).Msg("failed to stat shared resource")
Expand Down Expand Up @@ -368,7 +389,7 @@ func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, shar
}

// Permissions
newPermissions, err := permissionFromRequest(r, h)
newPermissions, err := ocPublicPermToCs3(permKey, h)
logger.Debug().Interface("newPermissions", newPermissions).Msg("Parsed permissions")
if err != nil {
response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, "invalid permissions", err)
Expand Down Expand Up @@ -583,7 +604,7 @@ func ocPublicPermToCs3(permKey int, h *Handler) (*provider.ResourcePermissions,
return conversions.RoleFromOCSPermissions(perm).CS3ResourcePermissions(), nil
}

func permissionFromRequest(r *http.Request, h *Handler) (*provider.ResourcePermissions, error) {
func permKeyFromRequest(r *http.Request, h *Handler) (int, error) {
var err error
// phoenix sends: {"permissions": 15}. See ocPublicPermToRole struct for mapping

Expand All @@ -593,37 +614,33 @@ func permissionFromRequest(r *http.Request, h *Handler) (*provider.ResourcePermi

// handle legacy "publicUpload" arg that overrides permissions differently depending on the scenario
// https://github.com/owncloud/core/blob/v10.4.0/apps/files_sharing/lib/Controller/Share20OcsController.php#L447
publicUploadString, ok := r.Form["publicUpload"]
if ok {
publicUploadFlag, err := strconv.ParseBool(publicUploadString[0])
publicUploadString := r.FormValue("publicUpload")
if publicUploadString != "" {
publicUploadFlag, err := strconv.ParseBool(publicUploadString)
if err != nil {
log.Error().Err(err).Str("publicUpload", publicUploadString[0]).Msg("could not parse publicUpload argument")
return nil, err
log.Error().Err(err).Str("publicUpload", publicUploadString).Msg("could not parse publicUpload argument")
return 0, err
}

if publicUploadFlag {
// all perms except reshare
permKey = 15
}
} else {
permissionsString, ok := r.Form["permissions"]
if !ok {
permissionsString := r.FormValue("permissions")
if permissionsString == "" {
// no permission values given
return nil, nil
return 1, nil
}

permKey, err = strconv.Atoi(permissionsString[0])
permKey, err = strconv.Atoi(permissionsString)
if err != nil {
log.Error().Str("permissionFromRequest", "shares").Msgf("invalid type: %T", permKey)
return nil, fmt.Errorf("invalid type: %T", permKey)
return 0, fmt.Errorf("invalid type: %T", permKey)
}
}

p, err := ocPublicPermToCs3(permKey, h)
if err != nil {
return nil, err
}
return p, err
return permKey, nil
}

// TODO: add mapping for user share permissions to role
Expand Down

0 comments on commit a8714c0

Please sign in to comment.