Implementing Kerberos authentication with Javamail - imap

There is an older thread that seems to be the only relevant discussion I have been able to find.
I am trying to implement Kerberos with Javamail (over IMAP) and I have gotten my self thoroughly confused on exactly what is to be done with mail.imap.sasl.mechanisms. Assume I give the value "GSS-API" but am kind of lost where to go from there. I notice that Javamail has an class IMAPSaslAuthernticator. It seems to me that this is what is needed but I can find precious little documentation on where or how to use it.
Any ideas?
NOTE: I wanted to post more code for my question, but according the site directions, full posts are only for answers. So, I have edited the code I originally posted question.
Below is the real meat. For now, once I pass this point I get the Message[] from the server and print the size to console.
SSL/TLS security is required so it is enabled below. In this example certificates are managed by a trusted keystore in Java.
private Folder folder;
private Session session;
private Store store;
public boolean connectToKerberosMail() {
if (folder != null && folder.isOpen()) {
return true;
}
Properties properties = new Properties();
properties.setProperty("mail.debug", "true");
properties.put("mail.imaps.connectiontimeout",600000);
properties.put("mail.imaps.timeout",601000);
properties.put("mail.imaps.fetchsize", 65000);
properties.put("mail.imaps.starttls.enable", "true");
properties.put("mail.imaps.starttls.required", "false");
properties.put("mail.imaps.sasl.enable","true");
properties.put("mail.imaps.sasl.mechanisms","GSSAPI");
properties.put("mail.imaps.sasl.authorizationid",<user>);
properties.put("mail.imaps.sasl.realm",<realm>);
System.setProperty( "sun.security.krb5.debug", "true");
System.setProperty( "java.security.krb5.realm",<realm>);
System.setProperty( "java.security.krb5.kdc", <ip-address>);
System.setProperty( "java.security.auth.login.config", "jaas.conf");
System.setProperty( "javax.security.auth.useSubjectCredsOnly", "false");
try {
session = Session.getInstance(properties);
} catch (Exception e) {
session = null;
return false;
}
session.setDebug(true);
URLName url = new URLName("imaps", <host>, <port>, "", <user>, <pass>);
store = new IMAPSSLStore(session, url);
try {
store.connect();
} catch (Exception e) {
e.printStackTrace();
store = null;
session = null;
return false;
}
return openFolder();
}
My jaas.conf file is as follows (the ticket cache was acquired from kinit):
com.sun.security.jgss.initiate {
com.sun.security.auth.module.Krb5LoginModule required
principal="<principal>"
ticketCache="<cache-path>"
doNotPrompt="true"
useTicketCache="true"
debug="true";
};
com.sun.security.jgss.accept {
com.sun.security.auth.module.Krb5LoginModule required
principal="<principal>"
ticketCache="<cache-path>"
doNotPrompt="true"
useTicketCache="true"
debug="true";
};
I recently posted here the output but noticed that some of my properties where designated "imap" instead of "imaps". So I am doing more testing before posintg output incase it changes.
In the mean time is what I have above correct? From what I understand I have to enable imap for the imap connection, startTLS for the TLS/SSL, and sasl for kerberos. But maybe something is overriding the another?

While not 100% the way there yet i made some discoveries. LOGIN was happening with the protocol in the NamedURL was "imap". I changed it to "imaps".
However, it look like javamail takes the protocol and host uses them to contruct the principal. protocol/host#realm? so I was applying to principal imaps/host#REALM which didnt exist so failed on a non-matching pricipals error.
So, we added this new principal to the servers and got past this.
But authentication is still failing. In the kerberos log I was approved and sent a ticket for accessing the mail. But I do not see it in my ticket cache (using klist) only the first ticket for accessing kerberos (I got from using kinit).

It seems that I say this in every response. I don't know how to get the word out....
You almost certainly want to change Session.getDefaultInstance() to Session.getInstance(), although that's probably not the source of your problems.
Anyway, what does the protocol trace show when you run your program? (emailSession.setDebug(true);)
I don't know enough about Kerberos, and especially how Kerberos works as a SASL mechanism, but aren't you going to have to specify some sort of password? Or can it get the appropriate Kerberos ticket without asking you to prove who you are?

Related

Override error code on AuthorizationCodeProvider.Create

This question is about the implementation of the Authorization Code flow using Owin in Asp.net Wep Api.
I was trying to handle some error that might happen on my AuthorizationCode code creation. Apparently I can't redirect my self to the Client Redirect URI with he correct error code which is "server_error"
The following is my code :
private static void CreateAuthorizationCode(AuthenticationTokenCreateContext context)
{
try
{
//Some Code to create and save the AuthorizationCode that can throw an Exception
context.SetToken(code);
}
catch (Exception ex)
{
logger.Fatal(ex);
var redirectUri = GetRedirectUri();
var redirectLocation = string.Format("{0}?code={1}", redirectUri, "server_error");
context.Response.Redirect(redirectLocation);
}
}
But I get redirected by the framework to the redirect Uri with https://redirecturi?error=unsupported_response_type !
Is this a normal behavior ? Or maybe there is any other way to handle those kind of scenario and set by myself the error code !?
PS : I created an issue in Github about that : https://github.com/aspnet/Security/issues/375 no answer so far !
Thank you.
Is this a normal behavior ? Or maybe there is any other way to handle those kind of scenario that I'm missing?
Normal, I dunno. But expected, definitely: when using an IAuthenticationTokenProvider, you're not supposed to alter the HTTP response.
Why there is not way to set by myself the error using the AuthenticationTokenCreateContext object like context.SetError("my_error") ?
Unlike the ValidateAuthorizeRequest notification, it hasn't been designed to allow you to return an error.
Sadly, there's no way to return a server_error response from an IAuthenticationTokenProvider, since OAuthAuthorizationServerHandler will always use unsupported_response_type if you don't provide an authorization code: https://github.com/jchannon/katanaproject/blob/master/src/Microsoft.Owin.Security.OAuth/OAuthAuthorizationServerHandler.cs#L204
FYI, this is something we fixed recently in AspNet.Security.OpenIdConnect.Server (a fork of the OAuth2 authorization server shipped with Katana 3): https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/issues/112#issuecomment-125040925. If your custom code returns a null authorization code, a server_error response will be automatically returned to the client application.

Https: Hide controller method from http

Ok, so we have the RequireHttpsAttribute that we can use to ensure that a controller/controller method can only be called over SSL. In the case that we try to hit the method over HTTP, the server issues a 302 to the HTTPS version of the same controller (method).
This implies to my users that it is acceptable to issue the first request insecurely in the first place. I don't feel that this is acceptable. Before I trot out an attribute that issues a 404/500 status code in the case that the HTTP version is hit, does such an attribute already exist?
Before I trot out an attribute that issues a 404/500 status code in
the case that the HTTP version is hit, does such an attribute already
exist?
No, such attribute doesn't exist out of the box.
If the simply act of requesting the page using HTTP is not compromising any user data, I'd say the redirect should be enough and a perfect approach for your scenario. Why bother user with things we can take care of?
This implies to my users that it is acceptable to issue the first
request insecurely in the first place. I don't feel that this is
acceptable. Before I trot out an attribute that issues a 404/500
status code in the case that the HTTP version is hit, does such an
attribute already exist?
If you don't want your application to work at all for these URLs using http:// instead of https://, don't serve anything at all (404 or no connection).
Note that it's ultimately the user's responsibility to check that SSL/TLS is used (and used correctly with a valid certificate). Make sure the links to those address use https:// indeed, and that the users expect https:// to be used, at least for the start page. You could consider using HSTS if their browser support it (or possibly permanent redirects to the entry point that would be cached).
From another comment:
I don't want any info about the url leaked in any way to any third parties
Once the request has been made using this http:// URL from the client, there's little point doing anything on the server. It's too late: an eavesdropper could have seen the request. (If your own page doesn't link to external websites, they wouldn't see that address in the referrer either.)
Even if your server doesn't even listen on the plain HTTP port, an active MITM attacker (or more simply, a proxy) could potentially listen to that request and get the URL, without it even reaching your server.
Again: make sure your users expect https:// to be used, and once they're on a secure page, make sure your links/form actions to other sections of your site all use https://.
So for reference, here's my new attribute:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,
Inherited = true,
AllowMultiple = false)]
public class HttpsOnlyAttribute : FilterAttribute, IAuthorizationFilter
{
private readonly bool disableInDebug;
public HttpsOnlyAttribute(bool disableInDebug = false)
{
this.disableInDebug = disableInDebug;
}
public virtual void OnAuthorization(AuthorizationContext filterContext)
{
#if DEBUG
if (disableInDebug) return;
#endif
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
var context = filterContext.HttpContext;
var request = context.Request;
var isSecure = request.IsSecureConnection;
if (!isSecure)
{
throw new HttpException(404, "Not found");
}
}
}

WIF- ID1014: The signature is not valid. The data may have been tampered with

I've been using WIF to authenticate our new website, the STS is based upon the starter-sts implementation.
To enable this to work correctly on out load balanced environment I've used the following in the global.asax to override the default certificate behaviour.
void onServiceConfigurationCreated(object sender, ServiceConfigurationCreatedEventArgs e)
{
List<CookieTransform> sessionTransforms = new List<CookieTransform>(new CookieTransform[]
{
new DeflateCookieTransform(),
new RsaEncryptionCookieTransform(e.ServiceConfiguration.ServiceCertificate),
new RsaSignatureCookieTransform(e.ServiceConfiguration.ServiceCertificate)
});
SessionSecurityTokenHandler sessionHandler = new SessionSecurityTokenHandler(sessionTransforms.AsReadOnly());
e.ServiceConfiguration.SecurityTokenHandlers.AddOrReplace(sessionHandler);
}
This is all working just find and people have been successfully using the system, however every now and then we get a blast of :
ID1014: The signature is not valid. The data may have been tampered with.
in the event logs, so I switched on WIF tracing and saw the following mentioned in the log.
ID1074: A CryptographicException occurred when attempting to encrypt the cookie using the ProtectedData API (see inner exception for details). If you are using IIS 7.5, this could be due to the loadUserProfile setting on the Application Pool being set to false.
I have a feeling this is leading me down a dark alley as I thought because I'd changed the implementation to use RSA this shouldn't affect me.
Any ideas to help me?
The browser cookies are encrypted with "old" mechanism - DPAPI.
Therefore, when the server tries to decrypt the cookies, it fails - your code use RSA now, not DPAPI.
As a workaround, clear the browser cache, and the application will start running as expected.
I changed the implementation to amend the timeout in the ontokencreated method. This prevents the reissue.
protected override void OnSessionSecurityTokenCreated(Microsoft.IdentityModel.Web.SessionSecurityTokenCreatedEventArgs args)
{
args.SessionToken = FederatedAuthentication.SessionAuthenticationModule.CreateSessionSecurityToken(
args.SessionToken.ClaimsPrincipal,
args.SessionToken.Context,
DateTime.UtcNow,
DateTime.UtcNow.AddDays(365),
true
);
//base.OnSessionSecurityTokenCreated(args);
}
Did you try setting the loadUserProfile option to true? Does the problem still occur?
(Select the Application pool in IIS and then click "Advanced Settings" on the right. "Load User Profile" is in the "Process Model" section).
The intermittent occurrence of your error, combined with the DPAPI exception showing up in your traces suggests to me that you aren't actually overriding the cookie transform, and your service is still using DPAPI.
This might be a long shot, but in your code snippet I noticed your method override "onServiceConfigurationCreated" starts with a lower case o. Such a typo would indeed prevent you from properly overriding default WIF behavior.

grails - spring-security-core secure-channel causing redirect loop (on Heroku)

I'm using spring-security-core and have setup the secure-channel capabilities, which work fine on my development machine. I've got the following in Config.groovy
grails.plugins.springsecurity.secureChannel.definition = [
'/order/checkout': 'REQUIRES_SECURE_CHANNEL',
'/order/paymentComplete': 'REQUIRES_INSECURE_CHANNEL'
]
Also, deploying to Heroku the associated order processing works fine, as long as I comment out the above lines. As soon as I put them back in, I get:
I see many requests come in on the server, and the Firebug net view shows:
I've got the PiggyBack SSL added on to Heroku, and I'm able to specify an https://... address to navigate to other parts of the site, in which case the browser stays in SSL mode. But if I access the
https:/www.momentumnow.co/order/checkout
address directly, I get the same redirect loop problem. Do you know what the problem is or how I can debug this further. If the latter, would you please update the comment area, and I will respond with updates to the problem area. Thanks
PiggyBack SSL documentation indicates:
"Piggyback SSL will allow you to use https://yourapp.heroku.com, since it uses the *.heroku.com certification. You don't need to buy or configure a certificate, it just works. https://yourcustomdomain.com will work, but it will produce a warning in the browser."
I'll probably switch to another mode as I add a certificate, however that does not seem to be the problem, based on the previous statement.
On the server, I get:
You need to fix the values for the ports since they default to 8080 and 8443. See the section on Channel Security in the docs - http://grails-plugins.github.com/grails-spring-security-core/docs/manual/ - about the grails.plugins.springsecurity.portMapper.httpPort and grails.plugins.springsecurity.portMapper.httpsPort config attributes.
For anyone else stumbling into this (as I did) the problem is that your app doesn't actually receive the request as HTTPS. Rather, Heroku replaces the HTTPS with a "X-Forwarded-Proto" header. Spring-security's HTTPS redirection is then putting you into an infinite redirect loop because it always detects the request as HTTP.
You can write your own SecureChannelProcessor to deal with this:
public class HerokuSecureChannelProcessor extends SecureChannelProcessor {
#Override
public void decide(FilterInvocation invocation, Collection<ConfigAttribute> config)
throws IOException, ServletException {
Assert.isTrue((invocation != null) && (config != null),
"Nulls cannot be provided");
for (ConfigAttribute attribute : config) {
if (supports(attribute)) {
String header = invocation.getHttpRequest().getHeader("X-Forwarded-Proto");
if(header == null){
// proceed normally
if (!invocation.getHttpRequest().isSecure()) {
getEntryPoint().commence(invocation.getRequest(), invocation.getResponse());
}
} else {
// use heroku header instead
if("http".equals(header)) {
getEntryPoint().commence(invocation.getRequest(), invocation.getResponse());
}
}
}
}
}
}

JavaMail SMTP-host error

I have a Grails-application that sends emails. The mailserver has no SMTP-authentication, so it requires "POP before SMTP", which means that I need to authenticate against the POP-account before sending through SMTP. Most often it works, but then once in a while, the mailserver is not picked up from the properties, and it tries to connect to "localhost" instead. Here is the properties:
Properties props = new Properties();
props.setProperty("mail.store.protocol", "pop3")
props.setProperty("mail.pop3.host", "mail.xxxxx.com")
props.setProperty("mail.pop3.port", "110")
props.setProperty("mail.smtp.host", "mail.xxxxx.com")
props.setProperty("mail.smtp.port", "25")
props.setProperty("mail.smtp.sendpartial", "true")
props.setProperty("mail.pop3.socketFactory.port", "110")
props.setProperty("mail.pop3.socketFactory.class","javax.net.SocketFactory")
props.setProperty("mail.pop3.socketFactory.fallback", "false")
Transport t = null
def store
try {
URLName url = new URLName("pop3", "mail.xxxxxxx.com", 110,
"INBOX", "username", "password");
Session session = Session.getDefaultInstance(props, null)
store = session.getStore(url)
store.connect("mail.xxxxxx.com", "username", "password")
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress("xxxx#xxxxxx.com"));
message.setRecipients(Message.RecipientType.TO,
InternetAddress.parse(toAddress, true));
message.setSubject(mailTitle);
message.setContent(messageBody, "text/html");
message.setSentDate(new Date());
t = session.getTransport("smtp")
t.connect()
t.send(message)
return true
}
catch (AddressException e) {
e.printStackTrace()
return false
}
catch (MessagingException e) {
e.printStackTrace()
return false
}
finally {
store?.close()
}
More often than not, this works. But when a Quartz Job is doing the sending, the mailserver entry from the Properties is not honored and it uses "localhost" instead and then fails to send the emails.
I can connect with Telnet and send with the attributes mentioned.
Could it be a timeout issue? According to what I've read in docs, the timeouts are "infinite" as default, so that "should" not be the problem.
Could it be a performance issue? I've created a "dummy app", which - more or less - does the same, but does not issue the sending from a Grails service, but directly from a controller. That one works all the time, but that app is always idleing.
I'm not using the Mail Plugin for Grails, since I couldn't see that it could handle the "pop before smtp"-paradigm.
Thanks in advance.
Solution: It looks as though the solution is to put the "mail.smtp.localhost"-value to the same value as for "mail.smtp.host". Not one single mail has failed since I put that property in. I don't think it was an obvious property to set and an ignorance from my point of view, nevertheless, I hope this will help someone else in the future.
So, my properties are as follows:
Properties props = new Properties();
props.setProperty("mail.store.protocol", "pop3")
props.setProperty("mail.pop3.host", "mail.xxxxxxx.com")
props.setProperty("mail.pop3.port", "110")
props.setProperty("mail.smtp.localhost", "mail.xxxxxxx.com")
props.setProperty("mail.smtp.host", "mail.xxxxxxx.com")
props.setProperty("mail.smtp.port", "25")
props.setProperty("mail.smtp.sendpartial", "true")
props.setProperty("mail.pop3.socketFactory.port", "110")
props.setProperty("mail.pop3.socketFactory.class","javax.net.SocketFactory")
props.setProperty("mail.pop3.socketFactory.fallback", "false")
Setting the value of "mail.smtp.host" to the property "mail.smtp.localhost", not only made the errors go away, the entire routine became a lot faster.
Thank you for your efforts!
props.setProperty("mail.transport.protocol", "smtp");
props.setProperty("mail.smtp.port", "25");
props.setProperty("mail.host", "a.b.com");
props.setProperty("mail.smtp.timeout", "10000");
Above works

Resources