[backend/federation] Enforce matching activity actor & auth fetch signature user

This commit is contained in:
Laura Hausmann 2024-02-17 02:06:09 +01:00
parent 0d5f987a8d
commit 69610a61d1
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
3 changed files with 19 additions and 3 deletions

View file

@ -65,7 +65,11 @@ public class ActivityPubController : Controller {
using var reader = new StreamReader(Request.Body, Encoding.UTF8, true, 1024, true); using var reader = new StreamReader(Request.Body, Encoding.UTF8, true, 1024, true);
var body = await reader.ReadToEndAsync(); var body = await reader.ReadToEndAsync();
Request.Body.Position = 0; Request.Body.Position = 0;
await queues.InboxQueue.EnqueueAsync(new InboxJob { Body = body, InboxUserId = id }); await queues.InboxQueue.EnqueueAsync(new InboxJob {
Body = body,
InboxUserId = id,
AuthFetchUserId = HttpContext.GetActor()?.Id
});
return Accepted(); return Accepted();
} }
} }

View file

@ -18,17 +18,19 @@ namespace Iceshrimp.Backend.Core.Federation.ActivityPub;
public class ActivityHandlerService( public class ActivityHandlerService(
ILogger<ActivityHandlerService> logger, ILogger<ActivityHandlerService> logger,
NoteService noteSvc, NoteService noteSvc,
UserService userSvc,
UserResolver userResolver, UserResolver userResolver,
DatabaseContext db, DatabaseContext db,
QueueService queueService, QueueService queueService,
ActivityRenderer activityRenderer, ActivityRenderer activityRenderer,
IOptions<Config.InstanceSection> config, IOptions<Config.InstanceSection> config,
IOptions<Config.SecuritySection> security,
FederationControlService federationCtrl, FederationControlService federationCtrl,
ObjectResolver resolver, ObjectResolver resolver,
NotificationService notificationSvc, NotificationService notificationSvc,
ActivityDeliverService deliverSvc ActivityDeliverService deliverSvc
) { ) {
public async Task PerformActivityAsync(ASActivity activity, string? inboxUserId) { public async Task PerformActivityAsync(ASActivity activity, string? inboxUserId, string? authFetchUserId) {
logger.LogDebug("Processing activity: {activity}", activity.Id); logger.LogDebug("Processing activity: {activity}", activity.Id);
if (activity.Actor == null) if (activity.Actor == null)
throw GracefulException.UnprocessableEntity("Cannot perform activity as actor 'null'"); throw GracefulException.UnprocessableEntity("Cannot perform activity as actor 'null'");
@ -37,6 +39,15 @@ public class ActivityHandlerService(
if (activity.Object == null) if (activity.Object == null)
throw GracefulException.UnprocessableEntity("Activity object is null"); throw GracefulException.UnprocessableEntity("Activity object is null");
var resolvedActor = await userResolver.ResolveAsync(activity.Actor.Id);
if (security.Value.AuthorizedFetch && authFetchUserId == null)
throw GracefulException
.UnprocessableEntity("Refusing to process activity without authFetchUserId in authorized fetch mode");
if (resolvedActor.Id != authFetchUserId && authFetchUserId != null)
throw GracefulException
.UnprocessableEntity($"Authorized fetch user id {authFetchUserId} doesn't match resolved actor id {resolvedActor.Id}");
// Resolve object & children // Resolve object & children
activity.Object = await resolver.ResolveObject(activity.Object) ?? activity.Object = await resolver.ResolveObject(activity.Object) ??
throw GracefulException.UnprocessableEntity("Failed to resolve activity object"); throw GracefulException.UnprocessableEntity("Failed to resolve activity object");

View file

@ -30,7 +30,7 @@ public class InboxQueue {
var logger = scope.GetRequiredService<ILogger<InboxQueue>>(); var logger = scope.GetRequiredService<ILogger<InboxQueue>>();
logger.LogTrace("Preparation took {ms} ms", job.Duration); logger.LogTrace("Preparation took {ms} ms", job.Duration);
await apHandler.PerformActivityAsync(activity, job.InboxUserId); await apHandler.PerformActivityAsync(activity, job.InboxUserId, job.AuthFetchUserId);
} }
} }
@ -38,4 +38,5 @@ public class InboxQueue {
public class InboxJob : Job { public class InboxJob : Job {
[ProtoMember(1)] public required string Body; [ProtoMember(1)] public required string Body;
[ProtoMember(2)] public required string? InboxUserId; [ProtoMember(2)] public required string? InboxUserId;
[ProtoMember(3)] public required string? AuthFetchUserId;
} }