|
@@ -22,11 +22,13 @@
|
|
|
package password.pwm.svc.email;
|
|
|
|
|
|
import com.sun.mail.smtp.SMTPSendFailedException;
|
|
|
+import com.sun.mail.util.MailSSLSocketFactory;
|
|
|
import password.pwm.AppProperty;
|
|
|
import password.pwm.PwmConstants;
|
|
|
import password.pwm.bean.EmailItemBean;
|
|
|
import password.pwm.config.Configuration;
|
|
|
import password.pwm.config.PwmSetting;
|
|
|
+import password.pwm.config.option.SmtpServerType;
|
|
|
import password.pwm.config.profile.EmailServerProfile;
|
|
|
import password.pwm.error.PwmError;
|
|
|
import password.pwm.error.PwmUnrecoverableException;
|
|
@@ -36,6 +38,7 @@ import password.pwm.util.java.JavaHelper;
|
|
|
import password.pwm.util.java.StringUtil;
|
|
|
import password.pwm.util.logging.PwmLogger;
|
|
|
import password.pwm.util.macro.MacroMachine;
|
|
|
+import password.pwm.util.secure.X509Utils;
|
|
|
|
|
|
import javax.mail.Message;
|
|
|
import javax.mail.MessagingException;
|
|
@@ -45,10 +48,13 @@ import javax.mail.internet.InternetAddress;
|
|
|
import javax.mail.internet.MimeBodyPart;
|
|
|
import javax.mail.internet.MimeMessage;
|
|
|
import javax.mail.internet.MimeMultipart;
|
|
|
+import javax.net.ssl.TrustManager;
|
|
|
import java.io.IOException;
|
|
|
import java.io.UnsupportedEncodingException;
|
|
|
+import java.security.cert.X509Certificate;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Collection;
|
|
|
+import java.util.Collections;
|
|
|
import java.util.Date;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
@@ -61,6 +67,7 @@ public class EmailServerUtil
|
|
|
private static final PwmLogger LOGGER = PwmLogger.forClass( EmailServerUtil.class );
|
|
|
|
|
|
static List<EmailServer> makeEmailServersMap( final Configuration configuration )
|
|
|
+ throws PwmUnrecoverableException
|
|
|
{
|
|
|
final List<EmailServer> returnObj = new ArrayList<>( );
|
|
|
|
|
@@ -68,57 +75,128 @@ public class EmailServerUtil
|
|
|
|
|
|
for ( final EmailServerProfile profile : profiles )
|
|
|
{
|
|
|
- final String id = profile.getIdentifier();
|
|
|
- final String address = profile.readSettingAsString( PwmSetting.EMAIL_SERVER_ADDRESS );
|
|
|
- final int port = (int) profile.readSettingAsLong( PwmSetting.EMAIL_SERVER_PORT );
|
|
|
- final String username = profile.readSettingAsString( PwmSetting.EMAIL_USERNAME );
|
|
|
- final PasswordData password = profile.readSettingAsPassword( PwmSetting.EMAIL_PASSWORD );
|
|
|
- if ( !StringUtil.isEmpty( address )
|
|
|
- && port > 0
|
|
|
- )
|
|
|
- {
|
|
|
- final Properties properties = makeJavaMailProps( configuration, address, port );
|
|
|
- final javax.mail.Session session = javax.mail.Session.getInstance( properties, null );
|
|
|
- final EmailServer emailServer = EmailServer.builder()
|
|
|
- .id( id )
|
|
|
- .host( address )
|
|
|
- .port( port )
|
|
|
- .username( username )
|
|
|
- .password( password )
|
|
|
- .javaMailProps( properties )
|
|
|
- .session( session )
|
|
|
- .build();
|
|
|
- returnObj.add( emailServer );
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- LOGGER.warn( "discarding incompletely configured email address for smtp server profile " + id );
|
|
|
- }
|
|
|
+ final TrustManager[] trustManager = trustManagerForProfile( configuration, profile );
|
|
|
+
|
|
|
+ final Optional<EmailServer> emailServer = makeEmailServer( configuration, profile, trustManager );
|
|
|
+
|
|
|
+ emailServer.ifPresent( returnObj::add );
|
|
|
}
|
|
|
|
|
|
return returnObj;
|
|
|
}
|
|
|
|
|
|
+ private static Optional<EmailServer> makeEmailServer(
|
|
|
+ final Configuration configuration,
|
|
|
+ final EmailServerProfile profile,
|
|
|
+ final TrustManager[] trustManagers
|
|
|
+ )
|
|
|
+ throws PwmUnrecoverableException
|
|
|
+ {
|
|
|
+ final String id = profile.getIdentifier();
|
|
|
+ final String address = profile.readSettingAsString( PwmSetting.EMAIL_SERVER_ADDRESS );
|
|
|
+ final int port = (int) profile.readSettingAsLong( PwmSetting.EMAIL_SERVER_PORT );
|
|
|
+ final String username = profile.readSettingAsString( PwmSetting.EMAIL_USERNAME );
|
|
|
+ final PasswordData password = profile.readSettingAsPassword( PwmSetting.EMAIL_PASSWORD );
|
|
|
+
|
|
|
+ final SmtpServerType smtpServerType = profile.readSettingAsEnum( PwmSetting.EMAIL_SERVER_TYPE, SmtpServerType.class );
|
|
|
+ if ( !StringUtil.isEmpty( address )
|
|
|
+ && port > 0
|
|
|
+ )
|
|
|
+ {
|
|
|
+ final Properties properties = makeJavaMailProps( configuration, profile, trustManagers );
|
|
|
+ final javax.mail.Session session = javax.mail.Session.getInstance( properties, null );
|
|
|
+ return Optional.of( EmailServer.builder()
|
|
|
+ .id( id )
|
|
|
+ .host( address )
|
|
|
+ .port( port )
|
|
|
+ .username( username )
|
|
|
+ .password( password )
|
|
|
+ .javaMailProps( properties )
|
|
|
+ .session( session )
|
|
|
+ .type( smtpServerType )
|
|
|
+ .build() );
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ LOGGER.warn( "discarding incompletely configured email address for smtp server profile " + id );
|
|
|
+ }
|
|
|
+
|
|
|
+ return Optional.empty();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static TrustManager[] trustManagerForProfile( final Configuration configuration, final EmailServerProfile emailServerProfile )
|
|
|
+ throws PwmUnrecoverableException
|
|
|
+ {
|
|
|
+ final List<X509Certificate> configuredCerts = emailServerProfile.readSettingAsCertificate( PwmSetting.EMAIL_SERVER_CERTS );
|
|
|
+ if ( JavaHelper.isEmpty( configuredCerts ) )
|
|
|
+ {
|
|
|
+ return X509Utils.getDefaultJavaTrustManager( configuration );
|
|
|
+ }
|
|
|
+ final TrustManager certMatchingTrustManager = new X509Utils.CertMatchingTrustManager( configuration, configuredCerts );
|
|
|
+ return new TrustManager[]
|
|
|
+ {
|
|
|
+ certMatchingTrustManager,
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
private static Properties makeJavaMailProps(
|
|
|
final Configuration config,
|
|
|
- final String host,
|
|
|
- final int port
|
|
|
+ final EmailServerProfile profile,
|
|
|
+ final TrustManager[] trustManager
|
|
|
)
|
|
|
+ throws PwmUnrecoverableException
|
|
|
{
|
|
|
//Create a properties item to start setting up the mail
|
|
|
- final Properties props = new Properties();
|
|
|
+ final Properties properties = new Properties();
|
|
|
|
|
|
//Specify the desired SMTP server
|
|
|
- props.put( "mail.smtp.host", host );
|
|
|
+ final String address = profile.readSettingAsString( PwmSetting.EMAIL_SERVER_ADDRESS );
|
|
|
+ properties.put( "mail.smtp.host", address );
|
|
|
|
|
|
//Specify SMTP server port
|
|
|
- props.put( "mail.smtp.port", port );
|
|
|
+ final int port = (int) profile.readSettingAsLong( PwmSetting.EMAIL_SERVER_PORT );
|
|
|
+ properties.put( "mail.smtp.port", port );
|
|
|
+ properties.put( "mail.smtp.socketFactory.port", port );
|
|
|
+
|
|
|
+ //set connection properties
|
|
|
+ properties.put( "mail.smtp.connectiontimeout", JavaHelper.silentParseInt( config.readAppProperty( AppProperty.SMTP_IO_CONNECT_TIMEOUT ), 10_000 ) );
|
|
|
+ properties.put( "mail.smtp.timeout", JavaHelper.silentParseInt( config.readAppProperty( AppProperty.SMTP_IO_CONNECT_TIMEOUT ), 30_000 ) );
|
|
|
+
|
|
|
+ properties.put( "mail.smtp.sendpartial", true );
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ final SmtpServerType smtpServerType = profile.readSettingAsEnum( PwmSetting.EMAIL_SERVER_TYPE, SmtpServerType.class );
|
|
|
+ if ( smtpServerType == SmtpServerType.SMTP )
|
|
|
+ {
|
|
|
+ return properties;
|
|
|
+ }
|
|
|
+
|
|
|
+ final MailSSLSocketFactory mailSSLSocketFactory = new MailSSLSocketFactory();
|
|
|
+ mailSSLSocketFactory.setTrustManagers( trustManager );
|
|
|
+
|
|
|
+ properties.put( "mail.smtp.ssl.enable", true );
|
|
|
+ properties.put( "mail.smtp.ssl.checkserveridentity", true );
|
|
|
+ properties.put( "mail.smtp.socketFactory.fallback", false );
|
|
|
+ properties.put( "mail.smtp.ssl.socketFactory", mailSSLSocketFactory );
|
|
|
+ properties.put( "mail.smtp.ssl.socketFactory.port", port );
|
|
|
+
|
|
|
+ final boolean useStartTls = smtpServerType == SmtpServerType.START_TLS;
|
|
|
+ properties.put( "mail.smtp.starttls.enable", useStartTls );
|
|
|
+ properties.put( "mail.smtp.starttls.required", useStartTls );
|
|
|
+ }
|
|
|
+ catch ( Exception e )
|
|
|
+ {
|
|
|
+ final String msg = "unable to create message transport properties: " + e.getMessage();
|
|
|
+ throw new PwmUnrecoverableException( PwmError.CONFIG_FORMAT_ERROR, msg );
|
|
|
+ }
|
|
|
|
|
|
//Specify configured advanced settings.
|
|
|
final Map<String, String> advancedSettingValues = StringUtil.convertStringListToNameValuePair( config.readSettingAsStringArray( PwmSetting.EMAIL_ADVANCED_SETTINGS ), "=" );
|
|
|
- props.putAll( advancedSettingValues );
|
|
|
+ properties.putAll( advancedSettingValues );
|
|
|
|
|
|
- return props;
|
|
|
+ return properties;
|
|
|
}
|
|
|
|
|
|
private static InternetAddress makeInternetAddress( final String input )
|
|
@@ -279,10 +357,10 @@ public class EmailServerUtil
|
|
|
throws MessagingException, PwmUnrecoverableException
|
|
|
{
|
|
|
// Login to SMTP server first if both username and password is given
|
|
|
- final Transport transport = server.getSession().getTransport( "smtp" );
|
|
|
-
|
|
|
final boolean authenticated = !StringUtil.isEmpty( server.getUsername() ) && server.getPassword() != null;
|
|
|
|
|
|
+ final Transport transport = server.getSession().getTransport( );
|
|
|
+
|
|
|
if ( authenticated )
|
|
|
{
|
|
|
// create a new Session object for the message
|
|
@@ -303,4 +381,33 @@ public class EmailServerUtil
|
|
|
return transport;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ public static List<X509Certificate> readCertificates( final Configuration configuration, final String profile )
|
|
|
+ throws PwmUnrecoverableException
|
|
|
+ {
|
|
|
+ final EmailServerProfile emailServerProfile = configuration.getEmailServerProfiles().get( profile );
|
|
|
+ final X509Utils.CertReaderTrustManager certReaderTm = new X509Utils.CertReaderTrustManager( X509Utils.ReadCertificateFlag.ReadOnlyRootCA );
|
|
|
+ final TrustManager[] trustManagers = new TrustManager[]
|
|
|
+ {
|
|
|
+ certReaderTm,
|
|
|
+ };
|
|
|
+ final Optional<EmailServer> emailServer = makeEmailServer( configuration, emailServerProfile, trustManagers );
|
|
|
+ if ( emailServer.isPresent() )
|
|
|
+ {
|
|
|
+ try ( Transport transport = makeSmtpTransport( emailServer.get() ); )
|
|
|
+ {
|
|
|
+ return certReaderTm.getCertificates();
|
|
|
+ }
|
|
|
+ catch ( Exception e )
|
|
|
+ {
|
|
|
+ final String exceptionMessage = JavaHelper.readHostileExceptionMessage( e );
|
|
|
+ final String errorMsg = "error connecting to secure server while reading SMTP certificates: " + exceptionMessage;
|
|
|
+ LOGGER.debug( () -> errorMsg );
|
|
|
+ throw new PwmUnrecoverableException( PwmError.ERROR_SERVICE_UNREACHABLE, errorMsg );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+
|
|
|
}
|