Skip to content

Commit

Permalink
ZCS-13176:Mail recall zimlet message verfication header
Browse files Browse the repository at this point in the history
  • Loading branch information
shubhamCS03 committed Apr 13, 2023
1 parent 06b49f0 commit 4c5f06f
Show file tree
Hide file tree
Showing 12 changed files with 357 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2869,6 +2869,14 @@ public static TwoFactorAuthSecretEncoding fromString(String s) throws ServiceExc
@ZAttr(id=-1)
public static final String A_registeredAddress = "registeredAddress";

/**
* Can be used in Mail Recall to make it more secure from spoof.
*
* @since ZCS 10.0.0
*/
@ZAttr(id=4097)
public static final String A_secretKeyForMailRecall = "secretKeyForMailRecall";

/**
* RFC2256: last (family) name(s) for which the entity is known by
*/
Expand Down
6 changes: 6 additions & 0 deletions common/src/java/com/zimbra/common/soap/AdminConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -1610,6 +1610,12 @@ public final class AdminConstants {
public static final QName VALIDATE_S3_BUCKET_REACHABLE_REQUEST = QName.get(E_VALIDATE_S3_BUCKET_REACHABLE_REQUEST, NAMESPACE);
public static final QName VALIDATE_S3_BUCKET_REACHABLE_RESPONSE = QName.get(E_VALIDATE_S3_BUCKET_REACHABLE_RESPONSE, NAMESPACE);

// Secret key for mail recall
public static final String E_GENERATE_SECRET_KEY_REQUEST = "GenerateSecretKeyRequest";
public static final String E_GENERATE_SECRET_KEY_RESPONSE = "GenerateSecretKeyResponse";
public static final QName GENERATE_SECRET_KEY_REQUEST = QName.get(E_GENERATE_SECRET_KEY_REQUEST, NAMESPACE);
public static final QName GENERATE_SECRET_KEY_RESPONSE = QName.get(E_GENERATE_SECRET_KEY_RESPONSE, NAMESPACE);

// Removed Zetras zimlet package list
public static final List<String> ZEXTRAS_PACKAGES_LIST = Arrays.asList("com_ng_auth", "com_zextras_zextras",
"com_zextras_client", "com_zimbra_connect_classic", "com_zimbra_connect_modern", "com_zextras_docs",
Expand Down
7 changes: 6 additions & 1 deletion soap/src/java/com/zimbra/soap/JaxbUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;

import com.zimbra.soap.admin.message.GenerateSecretKeyRequest;
import com.zimbra.soap.admin.message.GenerateSecretKeyResponse;
import org.dom4j.Document;
import org.dom4j.Namespace;
import org.dom4j.io.DocumentResult;
Expand Down Expand Up @@ -1167,7 +1169,10 @@ public final class JaxbUtil {
com.zimbra.soap.admin.message.ValidateS3BucketReachableRequest.class,
com.zimbra.soap.admin.message.ValidateS3BucketReachableResponse.class,
com.zimbra.soap.admin.message.EditS3BucketConfigRequest.class,
com.zimbra.soap.admin.message.EditS3BucketConfigResponse.class
com.zimbra.soap.admin.message.EditS3BucketConfigResponse.class,
com.zimbra.soap.admin.message.GenerateSecretKeyRequest.class,
com.zimbra.soap.admin.message.GenerateSecretKeyResponse.class

};

try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Zimbra Collaboration Suite Server
* Copyright (C) 2023 Synacor, Inc.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software Foundation,
* version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
* ***** END LICENSE BLOCK *****
*/

package com.zimbra.soap.admin.message;

import com.zimbra.common.soap.AdminConstants;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

/**
* @zm-api-command-auth-required true
* @zm-api-command-admin-auth-required true
* @zm-api-command-description Create random secret key and update it to LDAP attribute.
*/
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = AdminConstants.E_GENERATE_SECRET_KEY_REQUEST)
@XmlType(propOrder = {})
public class GenerateSecretKeyRequest {

public GenerateSecretKeyRequest() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Zimbra Collaboration Suite Server
* Copyright (C) 2023 Synacor, Inc.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software Foundation,
* version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
* ***** END LICENSE BLOCK *****
*/

package com.zimbra.soap.admin.message;

import com.zimbra.common.soap.AdminConstants;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = AdminConstants.E_GENERATE_SECRET_KEY_RESPONSE)
@XmlType(propOrder = {})
public class GenerateSecretKeyResponse {

public GenerateSecretKeyResponse() {
}
}
5 changes: 4 additions & 1 deletion store/conf/attrs/zimbra-attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10320,7 +10320,7 @@ TODO: delete them permanently from here
<desc>Can be used by an external service to indicate that a trial is set to convert to active at expiration.</desc>
</attr>

<attr id="4094" name="zimbraFeatureMailRecallEnabled" type="boolean" cardinality="single" optionalIn="globalConfig,account,cos,domain" flags="domainInfo,accountInfo,domainInherited,accountCosDomainInherited" since="10.0.0">
<attr id="4094" name="zimbraFeatureMailRecallEnabled" type="boolean" cardinality="single" optionalIn="globalConfig,account,cos,domain" flags="domainInfo,accountInfo,domainInherited,accountCosDomainInherited" callback="GenerateSecretKeyCallback" since="10.0.0">
<globalConfigValue>FALSE</globalConfigValue>
<defaultCOSValue>FALSE</defaultCOSValue>
<desc>Enables the mail recall functionality</desc>
Expand Down Expand Up @@ -10369,4 +10369,7 @@ TODO: delete them permanently from here
<globalConfigValue>TRUE</globalConfigValue>
<desc>whether to enable CountObjectsRequest with userAccount/account type on Zimbra Admin Console</desc>
</attr>
<attr id="4097" name="secretKeyForMailRecall" type="string" cardinality="single" optionalIn="globalConfig" since="10.0.0">
<desc>Secret key used in Mail Recall to make it more secure from spoof.</desc>
</attr>
</attrs>
72 changes: 72 additions & 0 deletions store/src/java/com/zimbra/cs/account/ZAttrConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,78 @@ public Map<String,Object> unsetDescription(Map<String,Object> attrs) {
return attrs;
}

/**
* Can be used in Mail Recall to make it more secure from spoof.
*
* @return secretKeyForMailRecall, or null if unset
*
* @since ZCS 10.0.0
*/
@ZAttr(id=4097)
public String getSecretKeyForMailRecall() {
return getAttr(Provisioning.A_secretKeyForMailRecall, null, true);
}

/**
* Can be used in Mail Recall to make it more secure from spoof.
*
* @param secretKeyForMailRecall new value
* @throws com.zimbra.common.service.ServiceException if error during update
*
* @since ZCS 10.0.0
*/
@ZAttr(id=4097)
public void setSecretKeyForMailRecall(String secretKeyForMailRecall) throws com.zimbra.common.service.ServiceException {
HashMap<String,Object> attrs = new HashMap<String,Object>();
attrs.put(Provisioning.A_secretKeyForMailRecall, secretKeyForMailRecall);
getProvisioning().modifyAttrs(this, attrs);
}

/**
* Can be used in Mail Recall to make it more secure from spoof.
*
* @param secretKeyForMailRecall new value
* @param attrs existing map to populate, or null to create a new map
* @return populated map to pass into Provisioning.modifyAttrs
*
* @since ZCS 10.0.0
*/
@ZAttr(id=4097)
public Map<String,Object> setSecretKeyForMailRecall(String secretKeyForMailRecall, Map<String,Object> attrs) {
if (attrs == null) attrs = new HashMap<String,Object>();
attrs.put(Provisioning.A_secretKeyForMailRecall, secretKeyForMailRecall);
return attrs;
}

/**
* Can be used in Mail Recall to make it more secure from spoof.
*
* @throws com.zimbra.common.service.ServiceException if error during update
*
* @since ZCS 10.0.0
*/
@ZAttr(id=4097)
public void unsetSecretKeyForMailRecall() throws com.zimbra.common.service.ServiceException {
HashMap<String,Object> attrs = new HashMap<String,Object>();
attrs.put(Provisioning.A_secretKeyForMailRecall, "");
getProvisioning().modifyAttrs(this, attrs);
}

/**
* Can be used in Mail Recall to make it more secure from spoof.
*
* @param attrs existing map to populate, or null to create a new map
* @return populated map to pass into Provisioning.modifyAttrs
*
* @since ZCS 10.0.0
*/
@ZAttr(id=4097)
public Map<String,Object> unsetSecretKeyForMailRecall(Map<String,Object> attrs) {
if (attrs == null) attrs = new HashMap<String,Object>();
attrs.put(Provisioning.A_secretKeyForMailRecall, "");
return attrs;
}

/**
* Zimbra access control list
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* ***** BEGIN LICENSE BLOCK *****
* Zimbra Collaboration Suite Server
* Copyright (C) 2023 Synacor, Inc.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software Foundation,
* version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
* ***** END LICENSE BLOCK *****
*/

package com.zimbra.cs.account.callback;

import com.google.common.base.Strings;
import com.zimbra.common.service.ServiceException;
import com.zimbra.cs.account.AttributeCallback;
import com.zimbra.cs.account.Entry;
import com.zimbra.cs.account.Provisioning;
import com.zimbra.cs.account.soap.SoapProvisioning;
import com.zimbra.soap.admin.message.GenerateSecretKeyRequest;

import java.util.Map;

public class GenerateSecretKeyCallback extends AttributeCallback {

@Override
public void preModify(CallbackContext context, String attrName, Object value,
Map attrsToModify, Entry entry) throws ServiceException {
String secretKey = null;
try {
if(value.equals("TRUE")) {
secretKey = Provisioning.getInstance().getConfig().getSecretKeyForMailRecall();
if (Strings.isNullOrEmpty(secretKey)) {
SoapProvisioning sp = SoapProvisioning.getAdminInstance();
sp.invokeJaxb(new GenerateSecretKeyRequest());
}
}
} catch (ServiceException e) {
throw ServiceException.FAILURE("Unable to initialize SecureRandom for mail recall", e);
}
}

@Override
public void postModify(CallbackContext context, String attrName, Entry entry) {
}

}
33 changes: 14 additions & 19 deletions store/src/java/com/zimbra/cs/mailbox/MailSender.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,7 @@
import java.io.InputStream;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import javax.mail.Address;
Expand All @@ -41,6 +31,7 @@
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MailDateFormat;
import javax.mail.internet.MimeMessage;

import com.google.common.base.Joiner;
Expand All @@ -61,13 +52,7 @@
import com.zimbra.common.util.StringUtil;
import com.zimbra.common.util.SystemUtil;
import com.zimbra.common.util.ZimbraLog;
import com.zimbra.cs.account.AccessManager;
import com.zimbra.cs.account.Account;
import com.zimbra.cs.account.AuthToken;
import com.zimbra.cs.account.DataSource;
import com.zimbra.cs.account.Domain;
import com.zimbra.cs.account.Identity;
import com.zimbra.cs.account.Provisioning;
import com.zimbra.cs.account.*;
import com.zimbra.cs.extension.ZimbraExtensionNotification;
import com.zimbra.cs.filter.RuleManager;
import com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException;
Expand All @@ -81,6 +66,7 @@
import com.zimbra.cs.service.FileUploadServlet.Upload;
import com.zimbra.cs.service.UserServlet;
import com.zimbra.cs.service.util.ItemId;
import com.zimbra.cs.service.util.SecretKey;
import com.zimbra.cs.util.AccountUtil;
import com.zimbra.cs.util.AccountUtil.AccountAddressMatcher;
import com.zimbra.cs.util.BuildInfo;
Expand All @@ -91,6 +77,8 @@ public class MailSender {
public static final String MSGTYPE_REPLY = String.valueOf(Flag.toChar(Flag.ID_REPLIED));
public static final String MSGTYPE_FORWARD = String.valueOf(Flag.toChar(Flag.ID_FORWARDED));
private static Map<String, PreSendMailListener> mPreSendMailListeners = new ConcurrentHashMap<String, PreSendMailListener>();
private static final MailDateFormat mailDateFormat = new MailDateFormat();
private static final String X_MESSAGE_VERIFICATION = "X-Zimbra-Message-Verification";

private Boolean mSaveToSent;
private Collection<Upload> mUploads;
Expand Down Expand Up @@ -1015,7 +1003,12 @@ void updateHeaders(MimeMessage mm, Account acct, Account authuser, OperationCont
mm.setFrom(from);
mm.setSender(sender);

mm.setSentDate(new Date());
Date date = new Date();
mm.setSentDate(date);

String value = SecretKey.getMessageVerificationHeaderValue(mm.getMessageID(), mailDateFormat.format(date), from.getAddress());
mm.addHeader(X_MESSAGE_VERIFICATION, value);

if (sender == null) {
Address[] existingReplyTos = mm.getReplyTo();
if (existingReplyTos == null || existingReplyTos.length == 0) {
Expand Down Expand Up @@ -1478,4 +1471,6 @@ public static void unregisterPreSendMailListener(PreSendMailListener listener) {
}
}
}


}
3 changes: 3 additions & 0 deletions store/src/java/com/zimbra/cs/service/admin/AdminService.java
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,9 @@ public void registerHandlers(DocumentDispatcher dispatcher) {

// ContactBackup API
dispatcher.registerHandler(AdminConstants.CONTACT_BACKUP_REQUEST, new ContactBackup());

// GetSecretKey API
dispatcher.registerHandler(AdminConstants.GENERATE_SECRET_KEY_REQUEST, new GenerateSecretKey());
}

/**
Expand Down
Loading

0 comments on commit 4c5f06f

Please sign in to comment.