authenticate.go (11016B)
1 // GoToSocial 2 // Copyright (C) GoToSocial Authors admin@gotosocial.org 3 // SPDX-License-Identifier: AGPL-3.0-or-later 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package federation 19 20 import ( 21 "context" 22 "crypto/rsa" 23 "encoding/json" 24 "errors" 25 "fmt" 26 "net/http" 27 "net/url" 28 29 "codeberg.org/gruf/go-kv" 30 "github.com/go-fed/httpsig" 31 "github.com/superseriousbusiness/activity/streams" 32 "github.com/superseriousbusiness/gotosocial/internal/ap" 33 "github.com/superseriousbusiness/gotosocial/internal/config" 34 "github.com/superseriousbusiness/gotosocial/internal/gtscontext" 35 "github.com/superseriousbusiness/gotosocial/internal/gtserror" 36 "github.com/superseriousbusiness/gotosocial/internal/log" 37 ) 38 39 var ( 40 errUnsigned = errors.New("http request wasn't signed or http signature was invalid") 41 signingAlgorithms = []httpsig.Algorithm{ 42 httpsig.RSA_SHA256, // Prefer common RSA_SHA256. 43 httpsig.RSA_SHA512, // Fall back to less common RSA_SHA512. 44 httpsig.ED25519, // Try ED25519 as a long shot. 45 } 46 ) 47 48 // AuthenticateFederatedRequest authenticates any kind of incoming federated 49 // request from a remote server. This includes things like GET requests for 50 // dereferencing our users or statuses etc, and POST requests for delivering 51 // new Activities. The function returns the URL of the owner of the public key 52 // used in the requesting http signature. 53 // 54 // 'Authenticate' in this case is defined as making sure that the http request 55 // is actually signed by whoever claims to have signed it, by fetching the public 56 // key from the signature and checking it against the remote public key. 57 // 58 // The provided username will be used to generate a transport for making remote 59 // requests/derefencing the public key ID of the request signature. Ideally you 60 // should pass in the username of the user *being requested*, so that the remote 61 // server can decide how to handle the request based on who's making it. Ie., if 62 // the request on this server is for https://example.org/users/some_username then 63 // you should pass in the username 'some_username'. The remote server will then 64 // know that this is the user making the dereferencing request, and they can decide 65 // to allow or deny the request depending on their settings. 66 // 67 // Note that it is also valid to pass in an empty string here, in which case the 68 // keys of the instance account will be used. 69 // 70 // Also note that this function *does not* dereference the remote account that 71 // the signature key is associated with. Other functions should use the returned 72 // URL to dereference the remote account, if required. 73 func (f *federator) AuthenticateFederatedRequest(ctx context.Context, requestedUsername string) (*url.URL, gtserror.WithCode) { 74 // Thanks to the signature check middleware, 75 // we should already have an http signature 76 // verifier set on the context. If we don't, 77 // this is an unsigned request. 78 verifier := gtscontext.HTTPSignatureVerifier(ctx) 79 if verifier == nil { 80 err := gtserror.Newf("%w", errUnsigned) 81 errWithCode := gtserror.NewErrorUnauthorized(err, errUnsigned.Error(), "(verifier)") 82 return nil, errWithCode 83 } 84 85 // We should have the signature itself set too. 86 signature := gtscontext.HTTPSignature(ctx) 87 if signature == "" { 88 err := gtserror.Newf("%w", errUnsigned) 89 errWithCode := gtserror.NewErrorUnauthorized(err, errUnsigned.Error(), "(signature)") 90 return nil, errWithCode 91 } 92 93 // And finally the public key ID URI. 94 pubKeyID := gtscontext.HTTPSignaturePubKeyID(ctx) 95 if pubKeyID == nil { 96 err := gtserror.Newf("%w", errUnsigned) 97 errWithCode := gtserror.NewErrorUnauthorized(err, errUnsigned.Error(), "(pubKeyID)") 98 return nil, errWithCode 99 } 100 101 // At this point we know the request was signed, 102 // so now we need to validate the signature. 103 104 var ( 105 pubKeyIDStr = pubKeyID.String() 106 requestingAccountURI *url.URL 107 pubKey interface{} 108 errWithCode gtserror.WithCode 109 ) 110 111 l := log. 112 WithContext(ctx). 113 WithFields(kv.Fields{ 114 {"requestedUsername", requestedUsername}, 115 {"pubKeyID", pubKeyIDStr}, 116 }...) 117 118 if pubKeyID.Host == config.GetHost() { 119 l.Trace("public key is ours, no dereference needed") 120 requestingAccountURI, pubKey, errWithCode = f.derefDBOnly(ctx, pubKeyIDStr) 121 } else { 122 l.Trace("public key is not ours, checking if we need to dereference") 123 requestingAccountURI, pubKey, errWithCode = f.deref(ctx, requestedUsername, pubKeyIDStr, pubKeyID) 124 } 125 126 if errWithCode != nil { 127 return nil, errWithCode 128 } 129 130 // Ensure public key now defined. 131 if pubKey == nil { 132 err := gtserror.New("public key was nil") 133 return nil, gtserror.NewErrorInternalError(err) 134 } 135 136 // Try to authenticate using permitted algorithms in 137 // order of most -> least common. Return OK as soon 138 // as one passes. 139 for _, algo := range signingAlgorithms { 140 l.Tracef("trying %s", algo) 141 142 err := verifier.Verify(pubKey, algo) 143 if err == nil { 144 l.Tracef("authentication PASSED with %s", algo) 145 return requestingAccountURI, nil 146 } 147 148 l.Tracef("authentication NOT PASSED with %s: %q", algo, err) 149 } 150 151 // At this point no algorithms passed. 152 err := gtserror.Newf( 153 "authentication NOT PASSED for public key %s; tried algorithms %+v; signature value was '%s'", 154 pubKeyIDStr, signature, signingAlgorithms, 155 ) 156 157 return nil, gtserror.NewErrorUnauthorized(err, err.Error()) 158 } 159 160 // derefDBOnly tries to dereference the given public 161 // key using only entries already in the database. 162 func (f *federator) derefDBOnly( 163 ctx context.Context, 164 pubKeyIDStr string, 165 ) (*url.URL, interface{}, gtserror.WithCode) { 166 reqAcct, err := f.db.GetAccountByPubkeyID(ctx, pubKeyIDStr) 167 if err != nil { 168 err = gtserror.Newf("db error getting account with pubKeyID %s: %w", pubKeyIDStr, err) 169 return nil, nil, gtserror.NewErrorInternalError(err) 170 } 171 172 reqAcctURI, err := url.Parse(reqAcct.URI) 173 if err != nil { 174 err = gtserror.Newf("error parsing account uri with pubKeyID %s: %w", pubKeyIDStr, err) 175 return nil, nil, gtserror.NewErrorInternalError(err) 176 } 177 178 return reqAcctURI, reqAcct.PublicKey, nil 179 } 180 181 // deref tries to dereference the given public key by first 182 // checking in the database, and then (if no entries found) 183 // calling the remote pub key URI and extracting the key. 184 func (f *federator) deref( 185 ctx context.Context, 186 requestedUsername string, 187 pubKeyIDStr string, 188 pubKeyID *url.URL, 189 ) (*url.URL, interface{}, gtserror.WithCode) { 190 l := log. 191 WithContext(ctx). 192 WithFields(kv.Fields{ 193 {"requestedUsername", requestedUsername}, 194 {"pubKeyID", pubKeyIDStr}, 195 }...) 196 197 // Try a database only deref first. We may already 198 // have the requesting account cached locally. 199 reqAcctURI, pubKey, errWithCode := f.derefDBOnly(ctx, pubKeyIDStr) 200 if errWithCode == nil { 201 l.Trace("public key cached, no dereference needed") 202 return reqAcctURI, pubKey, nil 203 } 204 205 l.Trace("public key not cached, trying dereference") 206 207 // If we've tried to get this account before and we 208 // now have a tombstone for it (ie., it's been deleted 209 // from remote), don't try to dereference it again. 210 gone, err := f.CheckGone(ctx, pubKeyID) 211 if err != nil { 212 err := gtserror.Newf("error checking for tombstone for %s: %w", pubKeyIDStr, err) 213 return nil, nil, gtserror.NewErrorInternalError(err) 214 } 215 216 if gone { 217 err := gtserror.Newf("account with public key %s is gone", pubKeyIDStr) 218 return nil, nil, gtserror.NewErrorGone(err) 219 } 220 221 // Make an http call to get the pubkey. 222 pubKeyBytes, errWithCode := f.callForPubKey(ctx, requestedUsername, pubKeyID) 223 if errWithCode != nil { 224 return nil, nil, errWithCode 225 } 226 227 // Extract the key and the owner from the response. 228 pubKey, pubKeyOwner, err := parsePubKeyBytes(ctx, pubKeyBytes, pubKeyID) 229 if err != nil { 230 err := fmt.Errorf("error parsing public key %s: %w", pubKeyID, err) 231 return nil, nil, gtserror.NewErrorUnauthorized(err) 232 } 233 234 return pubKeyOwner, pubKey, nil 235 } 236 237 // callForPubKey handles the nitty gritty of actually 238 // making a request for the given pubKeyID with a 239 // transport created on behalf of requestedUsername. 240 func (f *federator) callForPubKey( 241 ctx context.Context, 242 requestedUsername string, 243 pubKeyID *url.URL, 244 ) ([]byte, gtserror.WithCode) { 245 // Use a transport to dereference the remote. 246 trans, err := f.transportController.NewTransportForUsername( 247 // We're on a hot path: don't retry if req fails. 248 gtscontext.SetFastFail(ctx), 249 requestedUsername, 250 ) 251 if err != nil { 252 err = gtserror.Newf("error creating transport for %s: %w", requestedUsername, err) 253 return nil, gtserror.NewErrorInternalError(err) 254 } 255 256 // The actual http call to the remote server is 257 // made right here by the Dereference function. 258 pubKeyBytes, err := trans.Dereference(ctx, pubKeyID) 259 if err == nil { 260 // No problem. 261 return pubKeyBytes, nil 262 } 263 264 if gtserror.StatusCode(err) == http.StatusGone { 265 // 410 indicates remote public key no longer exists 266 // (account deleted, moved, etc). Add a tombstone 267 // to our database so that we can avoid trying to 268 // dereference it in future. 269 if err := f.HandleGone(ctx, pubKeyID); err != nil { 270 err := gtserror.Newf("error marking public key %s as gone: %w", pubKeyID, err) 271 return nil, gtserror.NewErrorInternalError(err) 272 } 273 274 err := gtserror.Newf("account with public key %s is gone", pubKeyID) 275 return nil, gtserror.NewErrorGone(err) 276 } 277 278 // Fall back to generic error. 279 err = gtserror.Newf("error dereferencing public key %s: %w", pubKeyID, err) 280 return nil, gtserror.NewErrorInternalError(err) 281 } 282 283 // parsePubKeyBytes extracts an rsa public key from the 284 // given pubKeyBytes by trying to parse the pubKeyBytes 285 // as an ActivityPub type. It will return the public key 286 // itself, and the URI of the public key owner. 287 func parsePubKeyBytes( 288 ctx context.Context, 289 pubKeyBytes []byte, 290 pubKeyID *url.URL, 291 ) (*rsa.PublicKey, *url.URL, error) { 292 m := make(map[string]interface{}) 293 if err := json.Unmarshal(pubKeyBytes, &m); err != nil { 294 return nil, nil, err 295 } 296 297 t, err := streams.ToType(ctx, m) 298 if err != nil { 299 return nil, nil, err 300 } 301 302 withPublicKey, ok := t.(ap.WithPublicKey) 303 if !ok { 304 err = gtserror.Newf("resource at %s with type %T could not be converted to ap.WithPublicKey", pubKeyID, t) 305 return nil, nil, err 306 } 307 308 pubKey, _, pubKeyOwnerID, err := ap.ExtractPublicKey(withPublicKey) 309 if err != nil { 310 err = gtserror.Newf("resource at %s with type %T did not contain recognizable public key", pubKeyID, t) 311 return nil, nil, err 312 } 313 314 return pubKey, pubKeyOwnerID, nil 315 }