Check if httpsig keyID matches actor and attributedTo

pull/20391/head
Anthony Wang 2022-07-20 18:57:19 -05:00
parent c8a8e1ec91
commit 5196dcd9a5
No known key found for this signature in database
GPG Key ID: BC96B00AEC5F2D76
3 changed files with 51 additions and 11 deletions

View File

@ -7,6 +7,7 @@ package activitypub
import (
"io"
"net/http"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
@ -106,7 +107,25 @@ func PersonInbox(ctx *context.APIContext) {
}
var activity ap.Activity
activity.UnmarshalJSON(body)
err = activity.UnmarshalJSON(body)
if err != nil {
ctx.ServerError("UnmarshalJSON", err)
return
}
// Make sure keyID matches the user doing the activity
_, keyID, _ := getKeyID(ctx.Req)
if activity.Actor != nil && !strings.HasPrefix(keyID, activity.Actor.GetID().String()) {
ctx.ServerError("Actor does not match HTTP signature keyID", nil)
return
}
if activity.AttributedTo != nil && !strings.HasPrefix(keyID, activity.AttributedTo.GetID().String()) {
ctx.ServerError("AttributedTo does not match HTTP signature keyID", nil)
return
}
// TODO: Check activity.Object actor and attributedTo
// Process activity
switch activity.Type {
case ap.FollowType:
activitypub.Follow(ctx, activity)
@ -143,11 +162,11 @@ func PersonOutbox(ctx *context.APIContext) {
outbox := ap.OrderedCollectionNew(ap.IRI(link + "/outbox"))
feed, err := models.GetFeeds(ctx, models.GetFeedsOptions{
RequestedUser: ctx.ContextUser,
Actor: ctx.ContextUser,
IncludePrivate: false,
IncludeDeleted: false,
ListOptions: db.ListOptions{Page: 1, PageSize: 1000000},
RequestedUser: ctx.ContextUser,
Actor: ctx.ContextUser,
IncludePrivate: false,
IncludeDeleted: false,
ListOptions: db.ListOptions{Page: 1, PageSize: 1000000},
})
if err != nil {
ctx.ServerError("Couldn't fetch feed", err)
@ -172,7 +191,7 @@ func PersonOutbox(ctx *context.APIContext) {
for _, star := range stars {
object := ap.Note{Type: ap.NoteType, Content: ap.NaturalLanguageValuesNew()}
object.Content.Set("en", ap.Content("Starred " + star.Name))
object.Content.Set("en", ap.Content("Starred "+star.Name))
create := ap.Create{Type: ap.CreateType, Object: object}
outbox.OrderedItems.Append(create)
}

View File

@ -7,6 +7,7 @@ package activitypub
import (
"io"
"net/http"
"strings"
"code.gitea.io/gitea/models/forgefed"
"code.gitea.io/gitea/modules/activitypub"
@ -102,12 +103,26 @@ func RepoInbox(ctx *context.APIContext) {
return
}
// Make sure keyID matches the user doing the activity
_, keyID, _ := getKeyID(ctx.Req)
actor, ok := activity["actor"]
if ok && !strings.HasPrefix(keyID, actor.(string)) {
ctx.ServerError("Actor does not match HTTP signature keyID", nil)
return
}
attributedTo, ok := activity["attributedTo"]
if ok && !strings.HasPrefix(keyID, attributedTo.(string)) {
ctx.ServerError("AttributedTo does not match HTTP signature keyID", nil)
return
}
// Process activity
switch activity["type"].(ap.ActivityVocabularyType) {
case ap.CreateType:
// Create activity, extract the object
object, ok := activity["object"].(map[string]interface{})
if ok {
ctx.ServerError("Activity does not contain object", err)
ctx.ServerError("Create activity does not contain object", err)
return
}
objectBinary, err := json.Marshal(object)

View File

@ -42,15 +42,22 @@ func getPublicKeyFromResponse(b []byte, keyID *url.URL) (p crypto.PublicKey, err
return p, err
}
func getKeyID(r *http.Request) (httpsig.Verifier, string, error) {
v, err := httpsig.NewVerifier(r)
if err != nil {
return nil, "", err
}
return v, v.KeyId(), nil
}
func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, err error) {
r := ctx.Req
// 1. Figure out what key we need to verify
v, err := httpsig.NewVerifier(r)
v, ID, err := getKeyID(r)
if err != nil {
return
}
ID := v.KeyId()
idIRI, err := url.Parse(ID)
if err != nil {
return
@ -65,7 +72,6 @@ func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, er
return
}
// 3. Verify the other actor's key
// TODO: Verify attributedTo matches keyID
algo := httpsig.Algorithm(setting.Federation.Algorithms[0])
authenticated = v.Verify(pubKey, algo) == nil
return authenticated, err