Reworked MSC1915. Add request validation.

This commit is contained in:
Anatoly Sablin
2019-07-27 15:51:01 +03:00
parent a96920f533
commit a1f64f5159
28 changed files with 419 additions and 236 deletions

View File

@@ -31,8 +31,8 @@ notification:
text: <Path to file containing the raw text part of the email. Do not set to not use one>
html: <Path to file containing the HTML part of the email. Do not set to not use one>
unbind:
fraudulent:
subject: <Subject of the email notification sent for potentially fraudulent 3PID unbinds>
notification:
subject: <Subject of the email notification sent for 3PID unbinds>
body:
text: <Path to file containing the raw text part of the email. Do not set to not use one>
html: <Path to file containing the raw text part of the email. Do not set to not use one>

View File

@@ -9,7 +9,7 @@ provide your own custom templates.
Templates for the following events/actions are available:
- [3PID invite](../../features/identity.md)
- [3PID session: validation](../session/session.md)
- [3PID session: fraudulent unbind](https://github.com/kamax-matrix/ma1sd/wiki/ma1sd-and-your-privacy#improving-your-privacy-one-commit-at-the-time)
- [3PID session: unbind](https://github.com/kamax-matrix/ma1sd/wiki/ma1sd-and-your-privacy#improving-your-privacy-one-commit-at-the-time)
- [Matrix ID invite](../../features/experimental/application-service.md#email-notification-about-room-invites-by-matrix-ids)
## Placeholders
@@ -71,7 +71,7 @@ under the namespace `threepid.medium.<medium>.generators.template`.
Under such namespace, the following keys are available:
- `invite`: Path to the 3PID invite notification template
- `session.validation`: Path to the 3PID session validation notification template
- `session.unbind.fraudulent`: Path to the 3PID session fraudulent unbind notification template
- `session.unbind`: Path to the 3PID session unbind notification template
- `generic.matrixId`: Path to the Matrix ID invite notification template
- `placeholder`: Map of key/values to set static values for some placeholders.
@@ -104,7 +104,7 @@ threepid:
session:
validation: '/path/to/validate-template.eml'
unbind:
fraudulent: '/path/to/unbind-fraudulent-template.eml'
notification: '/path/to/unbind-notification-template.eml'
generic:
matrixId: '/path/to/mxid-invite-template.eml'
placeholder:

View File

@@ -103,8 +103,8 @@ session:
validation:
enabled: true
unbind:
fraudulent:
sendWarning: true
notification:
enabled: true
# DO NOT COPY/PASTE AS-IS IN YOUR CONFIGURATION
# CONFIGURATION EXAMPLE
@@ -115,11 +115,7 @@ are allowed to do in terms of 3PID sessions. The policy has a global on/off swit
---
`unbind.fraudulent` controls warning notifications if an illegal/fraudulent 3PID removal is attempted on the Identity server.
This is directly related to synapse disregard for privacy and new GDPR laws in Europe in an attempt to inform users about
potential privacy leaks.
For more information, see the corresponding [synapse issue](https://github.com/matrix-org/synapse/issues/4540).
`unbind` controls warning notifications for 3PID removal.
### Web views
Once a user click on a validation link, it is taken to the Identity Server validation page where the token is submitted.

View File

@@ -118,7 +118,7 @@ public class Mxisd {
idStrategy = new RecursivePriorityLookupStrategy(cfg.getLookup(), ThreePidProviders.get(), bridgeFetcher);
pMgr = new ProfileManager(ProfileProviders.get(), clientDns, httpClient);
notifMgr = new NotificationManager(cfg.getNotification(), NotificationHandlers.get());
sessMgr = new SessionManager(cfg.getSession(), cfg.getMatrix(), store, notifMgr);
sessMgr = new SessionManager(cfg.getSession(), cfg.getMatrix(), store, notifMgr, resolver, httpClient, signMgr);
invMgr = new InvitationManager(cfg, store, idStrategy, keyMgr, signMgr, resolver, notifMgr, pMgr);
authMgr = new AuthManager(cfg, AuthProviders.get(), idStrategy, invMgr, clientDns, httpClient);
dirMgr = new DirectoryManager(cfg.getDirectory(), clientDns, httpClient, DirectoryProviders.get());

View File

@@ -62,6 +62,7 @@ public class MatrixConfig {
private transient final Logger log = LoggerFactory.getLogger(MatrixConfig.class);
private String domain;
private String trustedIdServer;
private Identity identity = new Identity();
public String getDomain() {
@@ -72,6 +73,14 @@ public class MatrixConfig {
this.domain = domain;
}
public String getTrustedIdServer() {
return trustedIdServer;
}
public void setTrustedIdServer(String trustedIdServer) {
this.trustedIdServer = trustedIdServer;
}
public Identity getIdentity() {
return identity;
}

View File

@@ -46,30 +46,15 @@ public class SessionConfig {
public static class PolicyUnbind {
public static class PolicyUnbindFraudulent {
private boolean enabled = true;
private boolean sendWarning = true;
public boolean getSendWarning() {
return sendWarning;
}
public void setSendWarning(boolean sendWarning) {
this.sendWarning = sendWarning;
}
public boolean getEnabled() {
return enabled;
}
private PolicyUnbindFraudulent fraudulent = new PolicyUnbindFraudulent();
public PolicyUnbindFraudulent getFraudulent() {
return fraudulent;
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void setFraudulent(PolicyUnbindFraudulent fraudulent) {
this.fraudulent = fraudulent;
}
}
public Policy() {

View File

@@ -115,24 +115,10 @@ public class EmailSendGridConfig {
public static class Templates {
public static class TemplateSessionUnbind {
private EmailTemplate fraudulent = new EmailTemplate();
public EmailTemplate getFraudulent() {
return fraudulent;
}
public void setFraudulent(EmailTemplate fraudulent) {
this.fraudulent = fraudulent;
}
}
public static class TemplateSession {
private EmailTemplate validation = new EmailTemplate();
private TemplateSessionUnbind unbind = new TemplateSessionUnbind();
private EmailTemplate unbind = new EmailTemplate();
public EmailTemplate getValidation() {
return validation;
@@ -142,11 +128,11 @@ public class EmailSendGridConfig {
this.validation = validation;
}
public TemplateSessionUnbind getUnbind() {
public EmailTemplate getUnbind() {
return unbind;
}
public void setUnbind(TemplateSessionUnbind unbind) {
public void setUnbind(EmailTemplate unbind) {
this.unbind = unbind;
}

View File

@@ -31,7 +31,8 @@ public class EmailTemplateConfig extends GenericTemplateConfig {
setInvite("classpath:/threepids/email/invite-template.eml");
getGeneric().put("matrixId", "classpath:/threepids/email/mxid-template.eml");
getSession().setValidation("classpath:/threepids/email/validate-template.eml");
getSession().getUnbind().setFraudulent("classpath:/threepids/email/unbind-fraudulent.eml");
getSession().getUnbind().setValidation("classpath:/threepids/email/unbind-template.eml");
getSession().getUnbind().setNotification("classpath:/threepids/email/unbind-notification.eml");
}
public EmailTemplateConfig build() {
@@ -40,7 +41,8 @@ public class EmailTemplateConfig extends GenericTemplateConfig {
log.info("Session:");
log.info(" Validation: {}", getSession().getValidation());
log.info(" Unbind:");
log.info(" Fraudulent: {}", getSession().getUnbind().getFraudulent());
log.info(" Validation: {}", getSession().getUnbind().getValidation());
log.info(" Notification: {}", getSession().getUnbind().getNotification());
return this;
}

View File

@@ -41,16 +41,25 @@ public class GenericTemplateConfig {
public static class SessionUnbind {
private String fraudulent;
private String validation;
public String getFraudulent() {
return fraudulent;
private String notification;
public String getValidation() {
return validation;
}
public void setFraudulent(String fraudulent) {
this.fraudulent = fraudulent;
public void setValidation(String validation) {
this.validation = validation;
}
public String getNotification() {
return notification;
}
public void setNotification(String notification) {
this.notification = notification;
}
}
private String validation;

View File

@@ -30,7 +30,8 @@ public class PhoneSmsTemplateConfig extends GenericTemplateConfig {
public PhoneSmsTemplateConfig() {
setInvite("classpath:/threepids/sms/invite-template.txt");
getSession().setValidation("classpath:/threepids/sms/validate-template.txt");
getSession().getUnbind().setFraudulent("classpath:/threepids/sms/unbind-fraudulent.txt");
getSession().getUnbind().setValidation("classpath:/threepids/sms/unbind-validation.txt");
getSession().getUnbind().setNotification("classpath:/threepids/sms/unbind-notification.txt");
}
public PhoneSmsTemplateConfig build() {
@@ -39,7 +40,8 @@ public class PhoneSmsTemplateConfig extends GenericTemplateConfig {
log.info("Session:");
log.info(" Validation: {}", getSession().getValidation());
log.info(" Unbind:");
log.info(" Fraudulent: {}", getSession().getUnbind().getFraudulent());
log.info(" Validation: {}", getSession().getUnbind().getValidation());
log.info(" Notification: {}", getSession().getUnbind().getNotification());
return this;
}

View File

@@ -58,5 +58,4 @@ public class CryptoFactory {
public static SignatureManager getSignatureManager(MxisdConfig cfg, Ed25519KeyManager keyMgr) {
return new Ed25519SignatureManager(cfg, keyMgr);
}
}

View File

@@ -26,6 +26,7 @@ import io.kamax.matrix.event.EventKey;
import io.kamax.matrix.json.MatrixJson;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.util.Objects;
public interface SignatureManager {
@@ -106,4 +107,13 @@ public interface SignatureManager {
*/
Signature sign(byte[] data);
/**
* Verify the data.
*
* @param publicKey public key to verify
* @param signature signature to verify
* @param data the data to verify
* @return {@code true} if signature is valid, else {@code false}
*/
boolean verify(PublicKey publicKey, String signature, byte[] data);
}

View File

@@ -33,7 +33,9 @@ import net.i2p.crypto.eddsa.EdDSAEngine;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.util.Base64;
public class Ed25519SignatureManager implements SignatureManager {
@@ -92,4 +94,15 @@ public class Ed25519SignatureManager implements SignatureManager {
}
}
@Override
public boolean verify(PublicKey publicKey, String signature, byte[] data) {
try {
EdDSAEngine signEngine = new EdDSAEngine(MessageDigest.getInstance(keyMgr.getKeySpecs().getHashAlgorithm()));
signEngine.initVerify(publicKey);
signEngine.update(data);
return signEngine.verify(Base64.getDecoder().decode(signature));
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -21,13 +21,10 @@
package io.kamax.mxisd.http.undertow.handler.identity.v1;
import com.google.gson.JsonObject;
import io.kamax.mxisd.exception.BadRequestException;
import io.kamax.mxisd.exception.NotAllowedException;
import io.kamax.mxisd.http.IsAPIv1;
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
import io.kamax.mxisd.session.SessionManager;
import io.undertow.server.HttpServerExchange;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,20 +43,9 @@ public class SessionTpidUnbindHandler extends BasicHttpHandler {
@Override
public void handleRequest(HttpServerExchange exchange) {
String auth = exchange.getRequestHeaders().getFirst("Authorization");
if (StringUtils.isNotEmpty(auth)) {
// We have a auth header to process
if (StringUtils.startsWith(auth, "X-Matrix ")) {
log.warn("A remote host attempted to unbind without proper authorization. Request was denied");
log.warn("See https://github.com/kamax-matrix/mxisd/wiki/mxisd-and-your-privacy for more info");
throw new NotAllowedException("3PID can only be removed via 3PID sessions, not via Homeserver signature");
} else {
throw new BadRequestException("Illegal authorization type");
}
}
JsonObject body = parseJsonObject(exchange);
sessionMgr.unbind(body);
sessionMgr.unbind(auth, body);
writeBodyAsUtf8(exchange, "{}");
}
}

View File

@@ -37,6 +37,6 @@ public interface NotificationHandler {
void sendForValidation(IThreePidSession session);
void sendForFraudulentUnbind(ThreePid tpid);
void sendForUnbind(ThreePid tpid);
}

View File

@@ -78,8 +78,8 @@ public class NotificationManager {
ensureMedium(session.getThreePid().getMedium()).sendForValidation(session);
}
public void sendForFraudulentUnbind(ThreePid tpid) throws NotImplementedException {
ensureMedium(tpid.getMedium()).sendForFraudulentUnbind(tpid);
public void sendForUnbind(ThreePid tpid) throws NotImplementedException {
ensureMedium(tpid.getMedium()).sendForUnbind(tpid);
}
}

View File

@@ -20,33 +20,49 @@
package io.kamax.mxisd.session;
import static io.kamax.mxisd.config.SessionConfig.Policy.PolicyTemplate;
import com.google.gson.JsonObject;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix.ThreePid;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.matrix.json.MatrixJson;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.SessionConfig;
import io.kamax.mxisd.crypto.SignatureManager;
import io.kamax.mxisd.exception.BadRequestException;
import io.kamax.mxisd.exception.NotAllowedException;
import io.kamax.mxisd.exception.RemoteHomeServerException;
import io.kamax.mxisd.exception.SessionNotValidatedException;
import io.kamax.mxisd.exception.SessionUnknownException;
import io.kamax.mxisd.lookup.SingleLookupReply;
import io.kamax.mxisd.lookup.SingleLookupRequest;
import io.kamax.mxisd.lookup.ThreePidValidation;
import io.kamax.mxisd.matrix.HomeserverFederationResolver;
import io.kamax.mxisd.notification.NotificationManager;
import io.kamax.mxisd.storage.IStorage;
import io.kamax.mxisd.storage.dao.IThreePidSessionDao;
import io.kamax.mxisd.threepid.session.ThreePidSession;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Calendar;
import java.util.Optional;
import static io.kamax.mxisd.config.SessionConfig.Policy.PolicyTemplate;
public class SessionManager {
private static final Logger log = LoggerFactory.getLogger(SessionManager.class);
@@ -55,17 +71,26 @@ public class SessionManager {
private MatrixConfig mxCfg;
private IStorage storage;
private NotificationManager notifMgr;
private HomeserverFederationResolver resolver;
private CloseableHttpClient client;
private SignatureManager signatureManager;
public SessionManager(
SessionConfig cfg,
MatrixConfig mxCfg,
IStorage storage,
NotificationManager notifMgr
SessionConfig cfg,
MatrixConfig mxCfg,
IStorage storage,
NotificationManager notifMgr,
HomeserverFederationResolver resolver,
CloseableHttpClient client,
SignatureManager signatureManager
) {
this.cfg = cfg;
this.mxCfg = mxCfg;
this.storage = storage;
this.notifMgr = notifMgr;
this.resolver = resolver;
this.client = client;
this.signatureManager = signatureManager;
}
private ThreePidSession getSession(String sid, String secret) {
@@ -98,7 +123,8 @@ public class SessionManager {
ThreePidSession session = new ThreePidSession(dao.get());
log.info("We already have a session for {}: {}", tpid, session.getId());
if (session.getAttempt() < attempt) {
log.info("Received attempt {} is greater than stored attempt {}, sending validation communication", attempt, session.getAttempt());
log.info("Received attempt {} is greater than stored attempt {}, sending validation communication", attempt,
session.getAttempt());
notifMgr.sendForValidation(session);
log.info("Sent validation notification to {}", tpid);
session.increaseAttempt();
@@ -166,7 +192,7 @@ public class SessionManager {
}
log.info("Session {}: Binding of {}:{} to Matrix ID {} is accepted",
session.getId(), session.getThreePid().getMedium(), session.getThreePid().getAddress(), mxid.getId());
session.getId(), session.getThreePid().getMedium(), session.getThreePid().getAddress(), mxid.getId());
SingleLookupRequest request = new SingleLookupRequest();
request.setType(session.getThreePid().getMedium());
@@ -174,7 +200,7 @@ public class SessionManager {
return new SingleLookupReply(request, mxid);
}
public void unbind(JsonObject reqData) {
public void unbind(String auth, JsonObject reqData) {
_MatrixID mxid;
try {
mxid = MatrixID.asAcceptable(GsonUtil.getStringOrThrow(reqData, "mxid"));
@@ -186,6 +212,128 @@ public class SessionManager {
String secret = GsonUtil.getStringOrNull(reqData, "client_secret");
ThreePid tpid = GsonUtil.get().fromJson(GsonUtil.getObj(reqData, "threepid"), ThreePid.class);
if (StringUtils.isNotBlank(sid) && StringUtils.isNotBlank(secret)) {
checkSession(sid, secret, tpid, mxid);
} else if (StringUtils.isNotBlank(auth)) {
checkAuthorization(auth, reqData);
} else {
throw new NotAllowedException("Unable to validate request");
}
// TODO make invalid all 3PID with specified medium and address.
}
private void checkAuthorization(String auth, JsonObject reqData) {
if (!auth.startsWith("X-Matrix ")) {
throw new NotAllowedException("Wrong authorization header");
}
if (StringUtils.isBlank(mxCfg.getTrustedIdServer())) {
throw new NotAllowedException("Unable to verify request, missing `matrix.trustedIdServer` variable");
}
String[] params = auth.substring("X-Matrix ".length()).split(",");
String origin = null;
String key = null;
String sig = null;
for (String param : params) {
String[] paramItems = param.split("=");
String paramKey = paramItems[0];
String paramValue = paramItems[1];
switch (paramKey) {
case "origin":
origin = removeQuotes(paramValue);
break;
case "key":
key = removeQuotes(paramValue);
break;
case "sig":
sig = removeQuotes(paramValue);
break;
default:
log.error("Unknown parameter: {}", param);
throw new BadRequestException("Authorization with unknown parameter");
}
}
if (StringUtils.isBlank(origin) || StringUtils.isBlank(key) || StringUtils.isBlank(sig)) {
log.error("Missing required parameters");
throw new BadRequestException("Missing required header parameters");
}
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("method", "POST");
jsonObject.addProperty("uri", "/_matrix/identity/api/v1/3pid/unbind");
jsonObject.addProperty("origin", origin);
jsonObject.addProperty("destination_is", mxCfg.getTrustedIdServer());
jsonObject.add("content", reqData);
String canonical = MatrixJson.encodeCanonical(jsonObject);
String originUrl = resolver.resolve(origin).toString();
validateServerKey(key, sig, canonical, originUrl);
}
private String removeQuotes(String origin) {
return origin.startsWith("\"") && origin.endsWith("\"") ? origin.substring(1, origin.length() - 1) : origin;
}
private void validateServerKey(String key, String signature, String canonical, String originUrl) {
HttpGet request = new HttpGet(originUrl + "/_matrix/key/v2/server");
log.info("Get keys from the server {}", request.getURI());
try (CloseableHttpResponse response = client.execute(request)) {
int statusCode = response.getStatusLine().getStatusCode();
log.info("Answer code: {}", statusCode);
if (statusCode == 200) {
verifyKey(key, signature, canonical, response);
} else {
throw new RemoteHomeServerException("Unable to fetch server keys.");
}
} catch (IOException e) {
String message = "Unable to get server keys: " + originUrl;
log.error(message, e);
throw new IllegalArgumentException(message);
}
}
private void verifyKey(String key, String signature, String canonical, CloseableHttpResponse response) throws IOException {
final String content = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
log.info("Answer body: {}", content);
final JsonObject responseObject = GsonUtil.parseObj(content);
final long validUntilTs = GsonUtil.getLong(responseObject, "valid_until_ts");
final Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(validUntilTs);
if (calendar.before(Calendar.getInstance())) {
final String msg = "Key is expired";
log.error(msg);
throw new RemoteHomeServerException(msg);
}
final JsonObject verifyKeys = GsonUtil.getObj(responseObject, "verify_keys");
final JsonObject keyObject = GsonUtil.getObj(verifyKeys, key);
final String publicKey = GsonUtil.getStringOrNull(keyObject, "key");
if (StringUtils.isBlank(publicKey)) {
throw new RemoteHomeServerException("Missing server key.");
}
EdDSANamedCurveSpec ed25519CurveSpec = EdDSANamedCurveTable.ED_25519_CURVE_SPEC;
EdDSAPublicKeySpec publicKeySpec = new EdDSAPublicKeySpec(Base64.getDecoder().decode(publicKey), ed25519CurveSpec);
EdDSAPublicKey dsaPublicKey = new EdDSAPublicKey(publicKeySpec);
final boolean verificationResult = signatureManager.verify(dsaPublicKey, signature, canonical.getBytes(StandardCharsets.UTF_8));
log.info("Verification result: {}", verificationResult);
if (!verificationResult) {
throw new RemoteHomeServerException("Unable to verify request.");
}
log.info("Request was authorized.");
}
private void checkSession(String sid, String secret, ThreePid tpid, _MatrixID mxid) {
// We ensure the session was validated
ThreePidSession session = getSessionIfValidated(sid, secret);
@@ -199,8 +347,6 @@ public class SessionManager {
throw new NotAllowedException("Only Matrix IDs from domain " + mxCfg.getDomain() + " can be unbound");
}
log.info("Session {}: Unbinding of {}:{} to Matrix ID {} is accepted",
session.getId(), session.getThreePid().getMedium(), session.getThreePid().getAddress(), mxid.getId());
log.info("Request was authorized.");
}
}

View File

@@ -79,9 +79,9 @@ public abstract class GenericTemplateNotificationGenerator extends PlaceholderNo
}
@Override
public String getForFraudulentUnbind(ThreePid tpid) {
log.info("Generating notification content for fraudulent unbind");
return populateForFraudulentUndind(tpid, getTemplateContent(cfg.getSession().getUnbind().getFraudulent()));
public String getForNotificationUnbind(ThreePid tpid) {
log.info("Generating notification content for unbind");
return populateForNotificationUndind(tpid, getTemplateContent(cfg.getSession().getUnbind().getNotification()));
}
}

View File

@@ -37,6 +37,6 @@ public interface NotificationGenerator {
String getForValidation(IThreePidSession session);
String getForFraudulentUnbind(ThreePid tpid);
String getForNotificationUnbind(ThreePid tpid);
}

View File

@@ -127,7 +127,7 @@ public abstract class PlaceholderNotificationGenerator {
.replace("%NEXT_URL%", validationLink);
}
protected String populateForFraudulentUndind(ThreePid tpid, String input) {
protected String populateForNotificationUndind(ThreePid tpid, String input) {
return populateForCommon(tpid, input);
}

View File

@@ -73,8 +73,8 @@ public abstract class GenericNotificationHandler<A extends ThreePidConnector, B
}
@Override
public void sendForFraudulentUnbind(ThreePid tpid) {
send(connector, tpid.getAddress(), generator.getForFraudulentUnbind(tpid));
public void sendForUnbind(ThreePid tpid) {
send(connector, tpid.getAddress(), generator.getForNotificationUnbind(tpid));
}
}

View File

@@ -129,10 +129,10 @@ public class EmailSendGridNotificationHandler extends PlaceholderNotificationGen
}
@Override
public void sendForFraudulentUnbind(ThreePid tpid) {
EmailTemplate template = cfg.getTemplates().getSession().getUnbind().getFraudulent();
public void sendForUnbind(ThreePid tpid) {
EmailTemplate template = cfg.getTemplates().getSession().getUnbind();
if (StringUtils.isAllBlank(template.getBody().getText(), template.getBody().getHtml())) {
throw new FeatureNotAvailable("No template has been configured for fraudulent unbind notifications");
throw new FeatureNotAvailable("No template has been configured for unbind notifications");
}
Email email = getEmail();

View File

@@ -1,135 +0,0 @@
Subject: IMPORTANT - %DOMAIN% Matrix Identity Server - Unauthorized 3PID unbind blocked
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ"
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ
Content-Type: text/plain; charset=UTF-8
Content-Disposition: inline
Hi,
**THIS IS IMPORTANT, PLEASE READ CAREFULLY**.
If you are the system administrator of the Matrix installation, read the second section.
This is a notification email that a possibly unauthorized entity has attempted to alter your
3PIDs (email, phone numbers, etc.) settings. The request was denied and no change has been made.
This is so you are aware of a possible failure in case you just tried to remove a 3PID from your account.
If you do not understand this email, please forward it to your System administrator.
-----------
As the system administrator:
If you are using synapse as a Homeserver, this is a known issue related to MSC1194 [1] and abuse of separation of concerns.
As a privacy-centric product and to protect your privacy, the request was actively blocked. We have written a more detailed
explanation on our Privacy wiki page [2] (Direct link [3]) so you can fully grasp the impact for you and your users.
We have open an issue [4] on the synapse repos to reflect the related privacy concerns and GDPR violation(s) and would
appreciate if you could comment on it or simply adds a thumbs up so the concerns are finally dealt with by the synapse dev team.
If you are using another Homeserver or this came following no action from your own users, then you have been the target
of an unbind attack from a rogue entity which was blocked. You may want to check your logs to see the exact source of
the attack and take relevant actions following your policy.
If you would like to disable these notifications, please see the 3PID sessions configuration documentation [5].
Thanks,
%DOMAIN_PRETTY% Admins
---
[1] https://github.com/matrix-org/matrix-doc/issues/1194
[2] https://github.com/kamax-matrix/mxisd/wiki/mxisd-and-your-privacy
[3] https://github.com/kamax-matrix/mxisd/wiki/mxisd-and-your-privacy#msc1194-synapse-and-impacts-on-your-privacy
[4] https://github.com/matrix-org/synapse/issues/4540
[5] https://github.com/kamax-matrix/mxisd/blob/master/docs/threepids/session/session.md#configuration
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ
Content-Type: multipart/related;
boundary="M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR";
type="text/html"
--M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR
Content-Type: text/html; charset=UTF-8
Content-Disposition: inline
<!doctype html>
<html lang="en">
<head>
<style type="text/css">
body {
margin: 0px;
}
pre, code {
word-break: break-word;
white-space: pre-wrap;
}
#page {
font-family: 'Open Sans', Helvetica, Arial, Sans-Serif;
font-color: #454545;
font-size: 12pt;
width: 100%%;
padding: 20px;
}
#inner {
width: 640px;
}
</style>
</head>
<body>
<table id="page">
<tr>
<td> </td>
<td id="inner">
<p>Hi,</p>
<p><b>THIS IS IMPORTANT, PLEASE READ CAREFULLY</b>.<br/>
If you are the system administrator of the Matrix installation, read the second section.</p>
<p>This is a notification email that a possibly unauthorized entity has attempted to alter your
3PIDs (email, phone numbers, etc.) settings. The request was denied and no change has been made.</p>
<p>This is so you are aware of a possible failure in case you just tried to remove a 3PID from your account.</p>
<p>If you do not understand this email, please forward it to your System administrator.</p>
<hr>
<p>As the system administrator:</p>
<p>If you are using synapse as a Homeserver, this is a known issue related to <a href="https://github.com/matrix-org/matrix-doc/issues/1194">MSC1194</a>
and abuse of separation of concerns. As a privacy-centric product and to protect your privacy, the request was actively
blocked. We have written a more detailed explanation on our <a href="https://github.com/kamax-matrix/mxisd/wiki/mxisd-and-your-privacy">Privacy wiki page</a>
(<a href="https://github.com/kamax-matrix/mxisd/wiki/mxisd-and-your-privacy#msc1194-synapse-and-impacts-on-your-privacy">Direct link to section</a>)
so you can fully grasp the impact for you and your users.</p>
<p>We have open an issue on the synapse repos to reflect the related privacy concerns and GDPR violation(s) and would
appreciate if you could comment on it or simply adds a thumbs up so the concerns are finally dealt with by the synapse dev team.<br/>
Issue: <a href="https://github.com/matrix-org/synapse/issues/4540">https://github.com/matrix-org/synapse/issues/4540</a></p>
<p>If you are using another Homeserver or this came following no action from your own users, then you have been the target
of an unbind attack from a rogue entity which was blocked. You may want to check your logs to see the exact source of
the attack and take relevant actions following your policy.</p>
<p>If you would like to disable these notifications, please see the
<a href="https://github.com/kamax-matrix/mxisd/blob/master/docs/threepids/session/session.md#configuration">3PID sessions configuration documentation.</a></p>
<p>Thanks,</p>
<p>%DOMAIN_PRETTY% Admins</p>
</td>
<td> </td>
</tr>
</table>
</body>
</html>
--M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR--
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ--

View File

@@ -0,0 +1,87 @@
Subject: Unbind 3PID
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ"
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ
Content-Type: text/plain; charset=UTF-8
Content-Disposition: inline
Hello there!
You or a server on your behalf requested to unbind your email.
If it was really you who made this request, you can click on the following link to
complete the unbinding your email address:
%VALIDATION_LINK%
If you didn't make this request, you can safely disregard this email.
%DOMAIN_PRETTY% Admins
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ
Content-Type: multipart/related;
boundary="M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR";
type="text/html"
--M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR
Content-Type: text/html; charset=UTF-8
Content-Disposition: inline
<!doctype html>
<html lang="en">
<head>
<style type="text/css">
body {
margin: 0px;
}
pre, code {
word-break: break-word;
white-space: pre-wrap;
}
#page {
font-family: 'Open Sans', Helvetica, Arial, Sans-Serif;
font-color: #454545;
font-size: 12pt;
width: 100%%;
padding: 20px;
}
#inner {
width: 640px;
}
.notif_link a, .footer a {
color: #76CFA6 ! important;
}
</style>
</head>
<body>
<table id="page">
<tr>
<td></td>
<td id="inner">
<p>Hello there!</p>
<p>You or a server on your behalf requested to unbind your email.</p>
<p>If it was really you who made this request, you can click on the following link to
complete the unbinding your email address:</p>
<p><a href="%VALIDATION_LINK%">Complete email unbinding</a></p>
<p>If you didn't make this request, you can safely disregard this email.</p>
<p>%DOMAIN_PRETTY% Admins</p>
</td>
<td></td>
</tr>
</table>
</body>
</html>
--M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR--
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ--

View File

@@ -0,0 +1,87 @@
Subject: Your Matrix Unbinding Token
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ"
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ
Content-Type: text/plain; charset=UTF-8
Content-Disposition: inline
Hello there!
You or a server on your behalf requested to unbind your email.
If it was really you who made this request, you can click on the following link to
complete the unbinding your email address:
%VALIDATION_LINK%
If you didn't make this request, you can safely disregard this email.
%DOMAIN_PRETTY% Admins
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ
Content-Type: multipart/related;
boundary="M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR";
type="text/html"
--M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR
Content-Type: text/html; charset=UTF-8
Content-Disposition: inline
<!doctype html>
<html lang="en">
<head>
<style type="text/css">
body {
margin: 0px;
}
pre, code {
word-break: break-word;
white-space: pre-wrap;
}
#page {
font-family: 'Open Sans', Helvetica, Arial, Sans-Serif;
font-color: #454545;
font-size: 12pt;
width: 100%%;
padding: 20px;
}
#inner {
width: 640px;
}
.notif_link a, .footer a {
color: #76CFA6 ! important;
}
</style>
</head>
<body>
<table id="page">
<tr>
<td></td>
<td id="inner">
<p>Hello there!</p>
<p>You or a server on your behalf requested to unbind your email.</p>
<p>If it was really you who made this request, you can click on the following link to
complete the unbinding your email address:</p>
<p><a href="%VALIDATION_LINK%">Complete email verification</a></p>
<p>If you didn't make this request, you can safely disregard this email.</p>
<p>%DOMAIN_PRETTY% Admins</p>
</td>
<td></td>
</tr>
</table>
</body>
</html>
--M3yzHl5YZehm9v4bAM8sKEdcOoVnRnKR--
--7REaIwWQCioQ6NaBlAQlg8ztbUQj6PKJ--

View File

@@ -1 +0,0 @@
INFORMATIONAL ONLY - Someone attempted to change your Matrix 3PIDs, with a potential data leak. Please contact your system administrator.

View File

@@ -0,0 +1 @@
Your Matrix 3PID was unbinding.

View File

@@ -0,0 +1 @@
Your Matrix token is %VALIDATION_TOKEN%