Check if httpsig keyID matches actor and attributedTo
This commit is contained in:
parent
c8a8e1ec91
commit
5196dcd9a5
3 changed files with 51 additions and 11 deletions
|
@ -7,6 +7,7 @@ package activitypub
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
@ -106,7 +107,25 @@ func PersonInbox(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var activity ap.Activity
|
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 {
|
switch activity.Type {
|
||||||
case ap.FollowType:
|
case ap.FollowType:
|
||||||
activitypub.Follow(ctx, activity)
|
activitypub.Follow(ctx, activity)
|
||||||
|
@ -172,7 +191,7 @@ func PersonOutbox(ctx *context.APIContext) {
|
||||||
|
|
||||||
for _, star := range stars {
|
for _, star := range stars {
|
||||||
object := ap.Note{Type: ap.NoteType, Content: ap.NaturalLanguageValuesNew()}
|
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}
|
create := ap.Create{Type: ap.CreateType, Object: object}
|
||||||
outbox.OrderedItems.Append(create)
|
outbox.OrderedItems.Append(create)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ package activitypub
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/forgefed"
|
"code.gitea.io/gitea/models/forgefed"
|
||||||
"code.gitea.io/gitea/modules/activitypub"
|
"code.gitea.io/gitea/modules/activitypub"
|
||||||
|
@ -102,12 +103,26 @@ func RepoInbox(ctx *context.APIContext) {
|
||||||
return
|
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) {
|
switch activity["type"].(ap.ActivityVocabularyType) {
|
||||||
case ap.CreateType:
|
case ap.CreateType:
|
||||||
// Create activity, extract the object
|
// Create activity, extract the object
|
||||||
object, ok := activity["object"].(map[string]interface{})
|
object, ok := activity["object"].(map[string]interface{})
|
||||||
if ok {
|
if ok {
|
||||||
ctx.ServerError("Activity does not contain object", err)
|
ctx.ServerError("Create activity does not contain object", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
objectBinary, err := json.Marshal(object)
|
objectBinary, err := json.Marshal(object)
|
||||||
|
|
|
@ -42,15 +42,22 @@ func getPublicKeyFromResponse(b []byte, keyID *url.URL) (p crypto.PublicKey, err
|
||||||
return p, 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) {
|
func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, err error) {
|
||||||
r := ctx.Req
|
r := ctx.Req
|
||||||
|
|
||||||
// 1. Figure out what key we need to verify
|
// 1. Figure out what key we need to verify
|
||||||
v, err := httpsig.NewVerifier(r)
|
v, ID, err := getKeyID(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ID := v.KeyId()
|
|
||||||
idIRI, err := url.Parse(ID)
|
idIRI, err := url.Parse(ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -65,7 +72,6 @@ func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, er
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 3. Verify the other actor's key
|
// 3. Verify the other actor's key
|
||||||
// TODO: Verify attributedTo matches keyID
|
|
||||||
algo := httpsig.Algorithm(setting.Federation.Algorithms[0])
|
algo := httpsig.Algorithm(setting.Federation.Algorithms[0])
|
||||||
authenticated = v.Verify(pubKey, algo) == nil
|
authenticated = v.Verify(pubKey, algo) == nil
|
||||||
return authenticated, err
|
return authenticated, err
|
||||||
|
|
Loading…
Add table
Reference in a new issue