123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- package inbox
- import (
- "context"
- "crypto/x509"
- "encoding/pem"
- "fmt"
- "net/http"
- "net/url"
- "strings"
- "github.com/pkg/errors"
- "github.com/go-fed/httpsig"
- "github.com/owncast/owncast/activitypub/apmodels"
- "github.com/owncast/owncast/activitypub/persistence"
- "github.com/owncast/owncast/activitypub/resolvers"
- "github.com/owncast/owncast/persistence/configrepository"
- log "github.com/sirupsen/logrus"
- )
- func handle(request apmodels.InboxRequest) {
- if verified, err := Verify(request.Request); err != nil {
- log.Debugln("Error in attempting to verify request", err)
- return
- } else if !verified {
- log.Debugln("Request failed verification", err)
- return
- }
- if err := resolvers.Resolve(context.Background(), request.Body, handleUpdateRequest, handleFollowInboxRequest, handleLikeRequest, handleAnnounceRequest, handleUndoInboxRequest, handleCreateRequest); err != nil {
- log.Debugln("resolver error:", err)
- }
- }
- // Verify will Verify the http signature of an inbound request as well as
- // check it against the list of blocked domains.
- // nolint: cyclop
- func Verify(request *http.Request) (bool, error) {
- verifier, err := httpsig.NewVerifier(request)
- if err != nil {
- return false, errors.Wrap(err, "failed to create key verifier for request")
- }
- pubKeyID, err := url.Parse(verifier.KeyId())
- if err != nil {
- return false, errors.Wrap(err, "failed to parse key to get key ID")
- }
- // Force federation only via servers using https.
- if pubKeyID.Scheme != "https" {
- return false, errors.New("federated servers must use https: " + pubKeyID.String())
- }
- signature := request.Header.Get("signature")
- if signature == "" {
- return false, errors.New("http signature header not found in request")
- }
- var algorithmString string
- signatureComponents := strings.Split(signature, ",")
- for _, component := range signatureComponents {
- kv := strings.Split(component, "=")
- if kv[0] == "algorithm" {
- algorithmString = kv[1]
- break
- }
- }
- algorithmString = strings.Trim(algorithmString, "\"")
- if algorithmString == "" {
- return false, errors.New("Unable to determine algorithm to verify request")
- }
- publicKey, err := resolvers.GetResolvedPublicKeyFromIRI(pubKeyID.String())
- if err != nil {
- return false, errors.Wrap(err, "failed to resolve actor from IRI to fetch key")
- }
- var publicKeyActorIRI *url.URL
- if ownerProp := publicKey.GetW3IDSecurityV1Owner(); ownerProp != nil {
- publicKeyActorIRI = ownerProp.Get()
- }
- if publicKeyActorIRI == nil {
- return false, errors.New("public key owner IRI is empty")
- }
- // Test to see if the actor is in the list of blocked federated domains.
- if isBlockedDomain(publicKeyActorIRI.Hostname()) {
- return false, errors.New("domain is blocked")
- }
- // If actor is specifically blocked, then fail validation.
- if blocked, err := isBlockedActor(publicKeyActorIRI); err != nil || blocked {
- return false, err
- }
- key := publicKey.GetW3IDSecurityV1PublicKeyPem().Get()
- block, _ := pem.Decode([]byte(key))
- if block == nil {
- log.Errorln("failed to parse PEM block containing the public key")
- return false, errors.New("failed to parse PEM block containing the public key")
- }
- parsedKey, err := x509.ParsePKIXPublicKey(block.Bytes)
- if err != nil {
- log.Errorln("failed to parse DER encoded public key: " + err.Error())
- return false, errors.Wrap(err, "failed to parse DER encoded public key")
- }
- algos := []httpsig.Algorithm{
- httpsig.Algorithm(algorithmString), // try stated algorithm first then other common algorithms
- httpsig.RSA_SHA256, // <- used by almost all fedi software
- httpsig.RSA_SHA512,
- }
- // The verifier will verify the Digest in addition to the HTTP signature
- triedAlgos := make(map[httpsig.Algorithm]error)
- for _, algorithm := range algos {
- if _, tried := triedAlgos[algorithm]; !tried {
- err := verifier.Verify(parsedKey, algorithm)
- if err == nil {
- return true, nil
- }
- triedAlgos[algorithm] = err
- }
- }
- return false, fmt.Errorf("http signature verification error(s) for: %s: %+v", pubKeyID.String(), triedAlgos)
- }
- func isBlockedDomain(domain string) bool {
- configRepository := configrepository.Get()
- blockedDomains := configRepository.GetBlockedFederatedDomains()
- for _, blockedDomain := range blockedDomains {
- if strings.Contains(domain, blockedDomain) {
- return true
- }
- }
- return false
- }
- func isBlockedActor(actorIRI *url.URL) (bool, error) {
- blockedactor, err := persistence.GetFollower(actorIRI.String())
- if blockedactor != nil && blockedactor.DisabledAt != nil {
- return true, errors.Wrap(err, "remote actor is blocked")
- }
- return false, nil
- }
|