setPhoto wrote ENCODING=BASE64, the vCard 2.1 spelling, inside cards
announced as VERSION:3.0. Strict CardDAV parsers such as Apple Contacts
drop the property, so LDAP directory photos (jpegPhoto) never displayed
on those clients.
Emit TYPE=JPEG;ENCODING=b as of RFC 2426. The parser already accepts
both encodings, and legacy ENCODING=BASE64 cards keep decoding.
https://bugs.sogo.nu/view.php?id=5134
AngularJS (EOL since 2022-01) and AngularJS Material no longer receive
security fixes, so any new vulnerability in them will stay unpatched.
In AngularJS, templates are code: any attacker-controlled text that
ends up in a compiled template is evaluated as an expression, and
since 1.6 there is no expression sandbox, so a single
{{constructor.constructor('...')()}} gives full script execution.
SOGo renders untrusted data (mail bodies, invitations, attachment
names, contact fields) into such templates and currently relies on
server-side filtering, like stripping "{{" and "}}", to prevent this.
Any gap in that filtering, or a bypass of $sanitize/ng-bind-html,
would mean XSS in the webmail session: reading and sending mail as
the user, and access to calendars and contacts.
A Content Security Policy adds a second layer in the browser that
doesn't depend on catching every injection. The new
SOGoContentSecurityPolicy system default ("report-only" or "enforce",
disabled when unset) sends a strict policy with the pages of the web
interface:
script-src 'self' 'nonce-<random>' (no 'unsafe-inline'/'unsafe-eval')
style-src 'self' 'unsafe-inline' (Angular Material, CKEditor 5)
object-src 'none'; base-uri 'self'; frame-ancestors 'self'; ...
The policy is generated by sogod, since a fresh 128-bit nonce is needed
per response: it is shared by all components of the response and set
on the inline configuration scripts of the page frame and module views.
The page also gets ng-csp="no-unsafe-eval", so AngularJS evaluates
expressions with its interpreter instead of Function().
With the policy enforced, an expression injected into a compiled
template, such as {{constructor.constructor('...')()}}, can no longer
reach Function() and fails with an EvalError, and injected inline
scripts are blocked. Injected expressions are limited to calling
functions available on the scope.
Tested with Chromium in both modes on login, Mail (HTML messages,
remote content, invitations, compose), Calendar and Contacts editors,
Preferences and Administration: no violations from SOGo's own code.
With the default unset, the generated output is unchanged.
form-action is deliberately not restricted, so SAML2/CAS/OpenID
logins posting to an external IdP keep working. Scripts listed in
SOGoUIAdditionalJSFiles are served from WebServerResources and allowed,
but must not load scripts from other origins, use eval() or rely on
inline handlers; their dependencies have to be installed and listed
alongside them.
Several places in the TNEF attachment decoder read a fixed number of
bytes, or index at a fixed offset, out of a MAPI property's data buffer
without checking the buffer is actually that large:
- the possibly-embedded-TNEF signature, read at offset 16 for an object
property and at offset 0 for plain file data
- the same object-attachment data, when building the actual attachment
content a few lines later: the length used there is derived from the
same unchecked offset and underflows to a huge value for a short buffer
- the classification flag, read as an 8-byte value
- the recurrence pattern, indexed up to offset 0x23 but only checked for
0x1F bytes
A short enough attachment property made these read, or in the attachment
case allocate, past the end of the buffer.
Add the missing length checks; where no further data is left to build
from, skip that part of the output instead.
The receipt email that SOGo generates and sends to the organizer
after they create a new calendar event with attendees, displays
an empty table in case the attendees have no display name. The
email address is only used in an a tag's href attribute which is
not visible to the user when the HTML email is rendered.
This commit adapts SOGoAptMailReceipt.wox to use the email address
as content of the a tags in case the display name is not set. This
is done in the same way as SOGoAptMailReminder.wox already does it.
NGSocketAddressFromString answers the first family returned by
getaddrinfo, and resolvers on dual-stack hosts may answer ::1 before
127.0.0.1 for localhost, so the IPv4 expectation for that name cannot be
guaranteed on every host.
generateMessageID: appended the substring following the last '@'
without sanitizing it, so a sender value such as 'Doe, John <a@b>'
produced 'Message-Id: <uuid@b>>' with a duplicated closing bracket on
calendar invitations. A nil domain also left the message-id
unterminated.
The domain part is now cut at the first address delimiter or
whitespace - a domain is a single token, and anything else would
allow header injection through generated message-ids.
Fixes#6201
the generic attribute writer used for these two spots does not escape what it
writes, so a value taken from the message ends the attribute and starts a new
one. both are inside a part that is compiled, which is where an injected
handler runs.
* the organizer link took inEvent.organizer.email verbatim. a value such as
mailto:x@y" onpointerover="... produced a live handler on the anchor, and an
entity encoded scheme such as javascript: reached the href, where the
browser decodes it. the href is now built as mailto: plus the parsed address
and escaped as an attribute value.
* the attachment name paragraph of the image and the link viewer took
filenameForDisplay verbatim. a quote inside an RFC 2231 encoded filename
ended the title attribute. the writer escapes & < > there but not the quote,
so the accessor drops the quote instead of escaping it, which keeps a plain
filename such as A&B.pdf unchanged in the tooltip.
the img title of the image viewer is left alone: attributes of that element are
escaped by the framework already.
the trailing while loop reuses the regex variable, which by then points at the
angular brace pattern whenever stripAngular is YES. that pattern has two
capture groups while the loop substitutes $1@im****$3, so NSRegularExpression
raises NSInvalidArgumentException and the NS_HANDLER swallows it.
give the @import pattern its own variable and use it in both the replacement
and the loop. the replacement also used the template of the block above, which
dropped the whole style element instead of masking the @import in place, so
test_stringWithoutHTMLInjection failed. it passes again, and a case with
stripAngular YES covers the loop.
the whole server built html of an iMIP card is rendered through sg-compile
(Message.service.js sets part.compile for UIxMailPartICalViewer), and the user
comment and the description are emitted with escapeHTML="NO". event data from
the invitation is therefore both an angular template and raw html.
47133fdf3 closed the description against interpolation with ng-non-bindable.
this closes both paths for every remaining field:
* ng-non-bindable on the user comment, the location, the organizer common name
and the raw body shown when the calendar cannot be parsed
* the user comment and the description are read through accessors that escape
them, so the url detection and insertBR produce the only markup left in the
output
the marker sits on the leaf containers, not on md-card-content, so the buttons,
the delegation autocomplete and the attendee chips keep being compiled. url
detection still turns a bare url in a comment or a description into a link.