Support SMTP over SSL/TLS

If sendemail.smtpEncryption is set to 'ssl' or 'tls' we now enable
the proper encryption on top of the socket, permitting the client
to use encryption when talking with the relay SMTP server.
This might be necessary to protect the username/password used to
authenticate prior to sending a message.

Bug: issue 300
Change-Id: Idecb20326261cbc8951e2ff469b95ea7ee83e48c
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2009-11-02 10:30:48 -08:00
parent bd51ebb9c4
commit 6e9a83f6ca
3 changed files with 85 additions and 2 deletions

View File

@@ -760,7 +760,21 @@ By default, 127.0.0.1 (aka localhost).
+
Port number of the SMTP server in sendemail.smtpserver.
+
By default, 25.
By default, 25, or 465 if smtpEncryption is 'ssl'.
[[sendemail.smtpEncryption]]sendemail.smtpEncryption::
+
Specify the encryption to use, either 'ssl' or 'tls'.
+
By default, 'none', indicating no encryption is used.
[[sendemail.sslVerify]]sendemail.sslVerify::
+
If false and sendemail.smtpEncryption is 'ssl' or 'tls', Gerrit
will not verify the server certificate when it connects to send
an email message.
+
By default, true, requiring the certificate to be verified.
[[sendemail.smtpUser]]sendemail.smtpUser::
+

View File

@@ -15,6 +15,7 @@
package com.google.gerrit.server.mail;
import com.google.gerrit.pgm.Version;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -34,12 +35,18 @@ import java.util.Map;
/** Sends email via a nearby SMTP server. */
@Singleton
public class SmtpEmailSender implements EmailSender {
public static enum Encryption {
NONE, SSL, TLS;
}
private final boolean enabled;
private String smtpHost;
private int smtpPort;
private String smtpUser;
private String smtpPass;
private Encryption smtpEncryption;
private boolean sslVerify;
private String[] allowrcpt;
@Inject
@@ -50,7 +57,26 @@ public class SmtpEmailSender implements EmailSender {
if (smtpHost == null) {
smtpHost = "127.0.0.1";
}
smtpPort = cfg.getInt("sendemail", null, "smtpserverport", 25);
smtpEncryption =
ConfigUtil.getEnum(cfg, "sendemail", null, "smtpencryption",
Encryption.NONE);
sslVerify = cfg.getBoolean("sendemail", null, "sslverify", true);
final int defaultPort;
switch (smtpEncryption) {
case SSL:
defaultPort = 465;
break;
case NONE:
case TLS:
default:
defaultPort = 25;
break;
}
smtpPort = cfg.getInt("sendemail", null, "smtpserverport", defaultPort);
smtpUser = cfg.getString("sendemail", null, "smtpuser");
smtpPass = cfg.getString("sendemail", null, "smtpuserpass");
allowrcpt = cfg.getStringList("sendemail", null, "allowrcpt");
@@ -136,6 +162,11 @@ public class SmtpEmailSender implements EmailSender {
private SMTPClient open() throws EmailException {
final AuthSMTPClient client = new AuthSMTPClient("UTF-8");
client.setAllowRcpt(allowrcpt);
if (smtpEncryption == Encryption.SSL) {
client.enableSSL(sslVerify);
}
try {
client.connect(smtpHost, smtpPort);
if (!SMTPReply.isPositiveCompletion(client.getReplyCode())) {
@@ -145,6 +176,17 @@ public class SmtpEmailSender implements EmailSender {
String e = client.getReplyString();
throw new EmailException("SMTP server rejected login: " + e);
}
if (smtpEncryption == Encryption.TLS) {
if (!client.startTLS(smtpHost, smtpPort, sslVerify)) {
throw new EmailException("SMTP server does not support TLS");
}
if (!client.login()) {
String e = client.getReplyString();
throw new EmailException("SMTP server rejected login: " + e);
}
}
if (smtpUser != null && !client.auth(smtpUser, smtpPass)) {
String e = client.getReplyString();
throw new EmailException("SMTP server rejected auth: " + e);

View File

@@ -14,12 +14,15 @@
package org.apache.commons.net.smtp;
import com.google.gerrit.server.ioutil.BlindSSLSocketFactory;
import org.eclipse.jgit.util.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
@@ -29,6 +32,7 @@ import java.util.Set;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLSocketFactory;
public class AuthSMTPClient extends SMTPClient {
private static final Logger log =
@@ -41,6 +45,29 @@ public class AuthSMTPClient extends SMTPClient {
super(charset);
}
public void enableSSL(final boolean verify) {
_socketFactory_ = sslFactory(verify);
}
public boolean startTLS(final String hostname, final int port,
final boolean verify) throws SocketException, IOException {
if (sendCommand("STARTTLS") != 220) {
return false;
}
_socket_ = sslFactory(verify).createSocket(_socket_, hostname, port, true);
_connectAction_();
return true;
}
private static SSLSocketFactory sslFactory(final boolean verify) {
if (verify) {
return (SSLSocketFactory) SSLSocketFactory.getDefault();
} else {
return (SSLSocketFactory) BlindSSLSocketFactory.getDefault();
}
}
public void setAllowRcpt(final String[] allowed) {
if (allowed != null && allowed.length > 0) {
if (allowedRcptTo == null) {