Skip to content

Commit

Permalink
feat: instance administrator is able to unlock authorized fetch (secu…
Browse files Browse the repository at this point in the history
…re mode) on instance
  • Loading branch information
nyarla committed Jul 29, 2023
1 parent cf4bd70 commit 600954e
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 8 deletions.
1 change: 1 addition & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ type Configuration struct {
InstanceExposePublicTimeline bool `name:"instance-expose-public-timeline" usage:"Allow unauthenticated users to query /api/v1/timelines/public"`
InstanceDeliverToSharedInboxes bool `name:"instance-deliver-to-shared-inboxes" usage:"Deliver federated messages to shared inboxes, if they're available."`
InstanceInjectMastodonVersion bool `name:"instance-inject-mastodon-version" usage:"This injects a Mastodon compatible version in /api/v1/instance to help Mastodon clients that use that version for feature detection"`
InstanceAllowUnauthorizedFetch bool `name:"instance-allow-unauthorized-fetch": usage:"Unlock AUTHOZIED_FETCH (aka Secure mode in Mastodon) mode."`

AccountsRegistrationOpen bool `name:"accounts-registration-open" usage:"Allow anyone to submit an account signup request. If false, server will be invite-only."`
AccountsApprovalRequired bool `name:"accounts-approval-required" usage:"Do account signups require approval by an admin or moderator before user can log in? If false, new registrations will be automatically approved."`
Expand Down
25 changes: 25 additions & 0 deletions internal/config/helpers.gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,31 @@ func GetInstanceInjectMastodonVersion() bool { return global.GetInstanceInjectMa
// SetInstanceInjectMastodonVersion safely sets the value for global configuration 'InstanceInjectMastodonVersion' field
func SetInstanceInjectMastodonVersion(v bool) { global.SetInstanceInjectMastodonVersion(v) }

// GetInstanceAllowUnauthorizedFetch safely fetches the Configuration value for state's 'InstanceAllowUnauthorizedFetch' field
func (st *ConfigState) GetInstanceAllowUnauthorizedFetch() (v bool) {
st.mutex.RLock()
v = st.config.InstanceAllowUnauthorizedFetch
st.mutex.RUnlock()
return
}

// SetInstanceAllowUnauthorizedFetch safely sets the Configuration value for state's 'InstanceAllowUnauthorizedFetch' field
func (st *ConfigState) SetInstanceAllowUnauthorizedFetch(v bool) {
st.mutex.Lock()
defer st.mutex.Unlock()
st.config.InstanceAllowUnauthorizedFetch = v
st.reloadToViper()
}

// InstanceAllowUnauthorizedFetchFlag returns the flag name for the 'InstanceAllowUnauthorizedFetch' field
func InstanceAllowUnauthorizedFetchFlag() string { return "instance-allow-unauthorized-fetch" }

// GetInstanceAllowUnauthorizedFetch safely fetches the value for global configuration 'InstanceAllowUnauthorizedFetch' field
func GetInstanceAllowUnauthorizedFetch() bool { return global.GetInstanceAllowUnauthorizedFetch() }

// SetInstanceAllowUnauthorizedFetch safely sets the value for global configuration 'InstanceAllowUnauthorizedFetch' field
func SetInstanceAllowUnauthorizedFetch(v bool) { global.SetInstanceAllowUnauthorizedFetch(v) }

// GetAccountsRegistrationOpen safely fetches the Configuration value for state's 'AccountsRegistrationOpen' field
func (st *ConfigState) GetAccountsRegistrationOpen() (v bool) {
st.mutex.RLock()
Expand Down
9 changes: 9 additions & 0 deletions internal/middleware/signaturecheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"net/http"
"net/url"

"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
"github.com/superseriousbusiness/gotosocial/internal/log"

Expand Down Expand Up @@ -54,6 +55,14 @@ func SignatureCheck(uriBlocked func(context.Context, *url.URL) (bool, error)) fu
// Create the signature verifier from the request;
// this will error if the request wasn't signed.
verifier, err := httpsig.NewVerifier(c.Request)

// INSECURE MODIFIED by @nyarla@kalaclista.com
// If instance allowed unauthoeized fetch by configuration,
// this middleware skip verification HTTP Sigunature on GET requests.
if config.GetInstanceAllowUnauthorizedFetch() && verifier == nil && c.Request.Method == http.MethodGet {
return
}

if err != nil {
// Only actually *abort* the request with 401
// if a signature was present but malformed.
Expand Down
8 changes: 8 additions & 0 deletions internal/processing/fedi/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"errors"
"fmt"

"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
Expand Down Expand Up @@ -50,6 +51,13 @@ func (p *Processor) authenticate(ctx context.Context, requestedUsername string)
// get requesting account, dereferencing if necessary.
requestingAccountURI, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, requestedUsername)
if errWithCode != nil {
// INSECURE MODIFIED by @nyarla@kalaclista.com
// If cannot get requestingAccountURI but instance allowed unauthorized fetches,
// this method returns requestingAccount as nil
if config.GetInstanceAllowUnauthorizedFetch() {
return requestedAccount, nil, nil
}

return nil, nil, errWithCode
}

Expand Down
5 changes: 4 additions & 1 deletion internal/processing/fedi/emoji.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@ import (
"fmt"

"github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
)

// EmojiGet handles the GET for a federated emoji originating from this instance.
func (p *Processor) EmojiGet(ctx context.Context, requestedEmojiID string) (interface{}, gtserror.WithCode) {
if _, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, ""); errWithCode != nil {
return nil, errWithCode
if !config.GetInstanceAllowUnauthorizedFetch() {
return nil, errWithCode
}
}

requestedEmoji, err := p.state.DB.GetEmojiByID(ctx, requestedEmojiID)
Expand Down
28 changes: 21 additions & 7 deletions internal/processing/fedi/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,24 @@ func (p *Processor) StatusGet(ctx context.Context, requestedUsername string, req
return nil, gtserror.NewErrorNotFound(err)
}

visible, err := p.filter.StatusVisible(ctx, requestingAccount, status)
if err != nil {
return nil, gtserror.NewErrorInternalError(err)
if requestingAccount != nil {
visible, err := p.filter.StatusVisible(ctx, requestingAccount, status)
if err != nil {
return nil, gtserror.NewErrorInternalError(err)
}

if !visible {
err := fmt.Errorf("status with id %s not visible to user with id %s", status.ID, requestingAccount.ID)
return nil, gtserror.NewErrorNotFound(err)
}
}

if !visible {
err := fmt.Errorf("status with id %s not visible to user with id %s", status.ID, requestingAccount.ID)
return nil, gtserror.NewErrorNotFound(err)
// INSECURE MODIFIED by @nyarla@kalaclista.com
// If requestingAccount is nil but status is visible in public timeline,
// this method returns status to api request.
if requestingAccount == nil && status.Visibility != gtsmodel.VisibilityPublic { // TODO
err := fmt.Errorf("status with id %s not visible in public timeline", status.ID)
return nil, gtserror.NewErrorUnauthorized(err)
}

asStatus, err := p.tc.StatusToAS(ctx, status)
Expand Down Expand Up @@ -91,7 +101,7 @@ func (p *Processor) StatusRepliesGet(ctx context.Context, requestedUsername stri
return nil, gtserror.NewErrorInternalError(err)
}
if !visible {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("status with id %s not visible to user with id %s", status.ID, requestingAccount.ID))
return nil, gtserror.NewErrorNotFound(fmt.Errorf("status with id %s not visible to user with id %s", status.ID, requestedAccount.ID))
}

var data map[string]interface{}
Expand Down Expand Up @@ -153,6 +163,10 @@ func (p *Processor) StatusRepliesGet(ctx context.Context, requestedUsername stri
}

// only show replies that the requester can see
if requestingAccount == nil {
continue
}

visibleToRequester, err := p.filter.StatusVisible(ctx, requestingAccount, r)
if err != nil || !visibleToRequester {
continue
Expand Down
13 changes: 13 additions & 0 deletions internal/processing/fedi/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

"github.com/superseriousbusiness/activity/streams/vocab"
"github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
Expand Down Expand Up @@ -68,6 +69,18 @@ func (p *Processor) UserGet(ctx context.Context, requestedUsername string, reque
// we can serve a more complete profile.
requestingAccountURI, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, requestedUsername)
if errWithCode != nil {
// INSECURE MODIFIED by @nyarla@kalaclista.com
// If the instance allowes unauthorized fetch to user account,
// this method bypass authenticate remote user.
if config.GetInstanceAllowUnauthorizedFetch() {
person, err := p.tc.AccountToAS(ctx, requestedAccount)
if err != nil {
return nil, gtserror.NewErrorInternalError(err)
}

return data(person)
}

return nil, errWithCode // likely 401
}

Expand Down

0 comments on commit 600954e

Please sign in to comment.