Skip to content

Commit

Permalink
http_caldav_sched.c: fix for organizer getting stomped in sched_reque…
Browse files Browse the repository at this point in the history
…st()

When the organizer gets stomped it can result in binary data
being used when building the JSON structure which then results
in an 'Invalid UTF-8 string' error.

The stomping of the organizer value seems consistent if the
iTIP request contains a large number of ATTENDEEs, but
the JSON failure is random depending on what data the
stomped organizer contains.
  • Loading branch information
ksmurchison committed Jan 3, 2025
1 parent 4bc392d commit f254602
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 21 deletions.
89 changes: 89 additions & 0 deletions cassandane/tiny-tests/Caldav/invite_lots_of_attendees
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!perl
use Cassandane::Tiny;

sub test_invite_lots_of_attendees
:VirtDomains :min_version_3_0
{
my ($self) = @_;

my $service = $self->{instance}->get_service("http");
my $CalDAV = Net::CalDAVTalk->new(
user => "cassandane%example.com",
password => 'pass',
host => $service->host(),
port => $service->port(),
scheme => 'http',
url => '/',
expandurl => 1,
);

my $CalendarId = $CalDAV->NewCalendar({name => 'hello'});
$self->assert_not_null($CalendarId);

my $uuid = "6de280c9-edff-4019-8ebd-cfebc73f8201";
my $href = "$CalendarId/$uuid.ics";
my $card = <<EOF;
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Apple Inc.//Mac OS X 10.10.4//EN
CALSCALE:GREGORIAN
BEGIN:VTIMEZONE
TZID:Australia/Melbourne
BEGIN:STANDARD
TZOFFSETFROM:+1100
RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU
DTSTART:20080406T030000
TZNAME:AEST
TZOFFSETTO:+1000
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+1000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU
DTSTART:20081005T020000
TZNAME:AEDT
TZOFFSETTO:+1100
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
CREATED:20150806T234327Z
UID:$uuid
DTEND;TZID=Australia/Melbourne:20160831T183000
TRANSP:OPAQUE
SUMMARY:An Event
DTSTART;TZID=Australia/Melbourne:20160831T153000
DTSTAMP:20150806T234327Z
SEQUENCE:0
ATTENDEE;CN=Test User;PARTSTAT=ACCEPTED;RSVP=TRUE:MAILTO:cassandane\@example.com
ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:MAILTO:friend\@example.com
ORGANIZER;CN=Test User:MAILTO:cassandane\@example.com
EOF

my @recipients = ('[email protected]');

for my $idx (1..49) {
$card .= <<EOF;
ATTENDEE;X-JMAP-ID=abcdeabcdeabcde${idx};CN=Some One ${idx};
EMAIL=attendee${idx}\@example.com;CUTYPE=INDIVIDUAL;X-JMAP-ROLE=attendee;
PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:attendee${idx}\@example.com
EOF

push @recipients, "attendee${idx}\@example.com";
}

$card .= <<EOF;
END:VEVENT
END:VCALENDAR
EOF

$CalDAV->Request('PUT', $href, $card, 'Content-Type' => 'text/calendar');

$self->assert_caldav_notified(
map {
+{
recipient => $_,
is_update => JSON::false,
method => 'REQUEST'
},
} @recipients
);
}
63 changes: 42 additions & 21 deletions imap/http_caldav_sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -720,27 +720,40 @@ static int imip_send(const char *cal_ownerid, const char *sched_userid,

/* Don't send a bogus message - check late to not allocate our own copy */
const char *ical_str = icalcomponent_as_ical_string(sched_data->itip);
if (!ical_str) goto done;
if (!ical_str) {
r = IMAP_INTERNAL;
goto done;
}

const char *imip_method = icalproperty_method_to_string(
icalcomponent_get_method(sched_data->itip));

json_t *val = json_pack("{s:s s:s s:s s:s s:o s:s s:o s:o s:b s:s}",
"calendarOwner", cal_ownerid,
"recipient", buf_cstring(&recipient),
"sender", sender,
"method", imip_method,
"id", id,
"ical", ical_str,
"jsevent", jsevent,
"patch", patch,
"is_update", SCHED_IS_UPDATE(sched_data),
"schedulingMechanism",
sched_mechanisms[sched_data->mech]);
char *serial = json_dumps(val, JSON_COMPACT);
notify(notifier, "IMIP", NULL, sched_userid, NULL, 0, NULL, serial, NULL);
free(serial);
json_decref(val);
json_error_t jerr;
json_t *val = json_pack_ex(&jerr, 0,
"{s:s s:s s:s s:s s:o s:s s:o s:o s:b s:s}",
"calendarOwner", cal_ownerid,
"recipient", buf_cstring(&recipient),
"sender", sender,
"method", imip_method,
"id", id,
"ical", ical_str,
"jsevent", jsevent,
"patch", patch,
"is_update", SCHED_IS_UPDATE(sched_data),
"schedulingMechanism",
sched_mechanisms[sched_data->mech]);
if (!val) {
xsyslog(LOG_ERR, "failed to create iMIP notification message",
"ownerid=<%s> sender=<%s> recipient=<%s> err=<%s>",
cal_ownerid, sender, buf_cstring(&recipient), jerr.text);
r = IMAP_INTERNAL;
}
else {
char *serial = json_dumps(val, JSON_COMPACT);
notify(notifier, "IMIP", NULL, sched_userid, NULL, 0, NULL, serial, NULL);
free(serial);
json_decref(val);
}

done:
buf_free(&recipient);
Expand Down Expand Up @@ -2507,6 +2520,13 @@ void sched_request(const char *cal_ownerid, const char *sched_userid,
return;
}

/* The organizer string that we get passed as an argument gets
randomly stomped by this function and/or its sub-functions,
possibly due to libical memory buffers/management.
So, we make a copy for ourselves.
*/
char *myorganizer = xstrdup(organizer);

/* Rewrite CalDAV managed attachments */
caldav_rewrite_attachments(sched_userid, caldav_attachments_to_binary,
oldical, newical, &myoldical, &mynewical);
Expand All @@ -2530,8 +2550,8 @@ void sched_request(const char *cal_ownerid, const char *sched_userid,
hash_table attendees = HASH_TABLE_INITIALIZER;
construct_hash_table(&attendees,
count_attendees(oldical) + count_attendees(newical) + 1, 0);
add_attendees(oldical, organizer, hide_attendees, &attendees);
add_attendees(newical, organizer, hide_attendees, &attendees);
add_attendees(oldical, myorganizer, hide_attendees, &attendees);
add_attendees(newical, myorganizer, hide_attendees, &attendees);

icaltimetype h_cutoff = caldav_get_historical_cutoff();

Expand All @@ -2550,9 +2570,9 @@ void sched_request(const char *cal_ownerid, const char *sched_userid,
}

syslog(LOG_NOTICE, "iTIP scheduling request from %s to %s",
organizer, attendee);
myorganizer, attendee);
schedule_one_attendee(cal_ownerid, sched_userid, schedule_addresses,
organizer, attendee,
myorganizer, attendee,
h_cutoff, oldical, newical, mech);

if (hide_attendees) {
Expand All @@ -2571,6 +2591,7 @@ void sched_request(const char *cal_ownerid, const char *sched_userid,
if (myoldical) icalcomponent_free(myoldical);
if (mynewical) icalcomponent_free(mynewical);
free_hash_table(&attendees, NULL);
free(myorganizer);
}

/*******************************************************************/
Expand Down

0 comments on commit f254602

Please sign in to comment.