mirror of
https://github.com/moparisthebest/davmail
synced 2025-01-07 03:38:05 -05:00
Improve KerberosHelper implementation, prepare credential delegation support
git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@2068 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
parent
608a07a364
commit
32dd469942
@ -40,12 +40,14 @@ import java.security.Security;
|
|||||||
public class KerberosHelper {
|
public class KerberosHelper {
|
||||||
protected static final Logger LOGGER = Logger.getLogger(KerberosHelper.class);
|
protected static final Logger LOGGER = Logger.getLogger(KerberosHelper.class);
|
||||||
protected static final Object LOCK = new Object();
|
protected static final Object LOCK = new Object();
|
||||||
protected static KerberosCallbackHandler kerberosCallbackHandler;
|
protected static final KerberosCallbackHandler KERBEROS_CALLBACK_HANDLER;
|
||||||
protected static LoginContext loginContext;
|
protected static LoginContext clientLoginContext;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
// Load Jaas configuration from class
|
||||||
Security.setProperty("login.configuration.provider", "davmail.http.KerberosLoginConfiguration");
|
Security.setProperty("login.configuration.provider", "davmail.http.KerberosLoginConfiguration");
|
||||||
kerberosCallbackHandler = new KerberosCallbackHandler();
|
// Kerberos callback handler singleton
|
||||||
|
KERBEROS_CALLBACK_HANDLER = new KerberosCallbackHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class KerberosCallbackHandler implements CallbackHandler {
|
protected static class KerberosCallbackHandler implements CallbackHandler {
|
||||||
@ -55,11 +57,6 @@ public class KerberosHelper {
|
|||||||
protected KerberosCallbackHandler() {
|
protected KerberosCallbackHandler() {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected KerberosCallbackHandler(String principal, String password) {
|
|
||||||
this.principal = principal;
|
|
||||||
this.password = password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
|
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
|
||||||
for (int i = 0; i < callbacks.length; i++) {
|
for (int i = 0; i < callbacks.length; i++) {
|
||||||
if (callbacks[i] instanceof NameCallback) {
|
if (callbacks[i] instanceof NameCallback) {
|
||||||
@ -74,8 +71,8 @@ public class KerberosHelper {
|
|||||||
// TODO: get username and password from dialog
|
// TODO: get username and password from dialog
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final NameCallback nameCallback = (NameCallback) callbacks[i];
|
((NameCallback) callbacks[i]).setName(principal);
|
||||||
nameCallback.setName(principal);
|
|
||||||
} else if (callbacks[i] instanceof PasswordCallback) {
|
} else if (callbacks[i] instanceof PasswordCallback) {
|
||||||
if (password == null) {
|
if (password == null) {
|
||||||
// if we get there kerberos token is missing or invalid
|
// if we get there kerberos token is missing or invalid
|
||||||
@ -86,8 +83,8 @@ public class KerberosHelper {
|
|||||||
password = inReader.readLine();
|
password = inReader.readLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final PasswordCallback passCallback = (PasswordCallback) callbacks[i];
|
((PasswordCallback) callbacks[i]).setPassword(password.toCharArray());
|
||||||
passCallback.setPassword(password.toCharArray());
|
|
||||||
} else {
|
} else {
|
||||||
throw new UnsupportedCallbackException(callbacks[i]);
|
throw new UnsupportedCallbackException(callbacks[i]);
|
||||||
}
|
}
|
||||||
@ -95,12 +92,39 @@ public class KerberosHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] getToken(final String host, final byte[] token) throws GSSException, LoginException {
|
/**
|
||||||
LOGGER.debug("KerberosHelper.getToken " + host + " " + token.length + " bytes token");
|
* Get response Kerberos token for host with provided token.
|
||||||
|
*
|
||||||
|
* @param host target host
|
||||||
|
* @param token input token
|
||||||
|
* @return response token
|
||||||
|
* @throws GSSException on error
|
||||||
|
* @throws LoginException on error
|
||||||
|
*/
|
||||||
|
public static byte[] initSecurityContext(final String host, final byte[] token) throws GSSException, LoginException {
|
||||||
|
return initSecurityContext(host, null, token);
|
||||||
|
}
|
||||||
|
|
||||||
LoginContext loginContext = login();
|
/**
|
||||||
|
* Get response Kerberos token for host with provided token, use client provided delegation credentials.
|
||||||
|
* Used to authenticate with target host on a gateway server with client credentials,
|
||||||
|
* gateway must have its own principal authorized for delegation
|
||||||
|
*
|
||||||
|
* @param host target host
|
||||||
|
* @param delegatedCredentials client delegated credentials
|
||||||
|
* @param token input token
|
||||||
|
* @return response token
|
||||||
|
* @throws GSSException on error
|
||||||
|
* @throws LoginException on error
|
||||||
|
*/
|
||||||
|
public static byte[] initSecurityContext(final String host, final GSSCredential delegatedCredentials, final byte[] token) throws GSSException, LoginException {
|
||||||
|
LOGGER.debug("KerberosHelper.initSecurityContext " + host + " " + token.length + " bytes token");
|
||||||
|
|
||||||
Object result = Subject.doAs(loginContext.getSubject(), new PrivilegedAction() {
|
// TODO: handle ticket expiration ?
|
||||||
|
// create client login context
|
||||||
|
clientLogin();
|
||||||
|
|
||||||
|
Object result = Subject.doAs(clientLoginContext.getSubject(), new PrivilegedAction() {
|
||||||
|
|
||||||
public Object run() {
|
public Object run() {
|
||||||
Object result;
|
Object result;
|
||||||
@ -110,10 +134,11 @@ public class KerberosHelper {
|
|||||||
// Kerberos v5 OID
|
// Kerberos v5 OID
|
||||||
Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
|
Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
|
||||||
|
|
||||||
GSSContext context = manager.createContext(serverName, krb5Oid, null,
|
GSSContext context = manager.createContext(serverName, krb5Oid, delegatedCredentials,
|
||||||
GSSContext.DEFAULT_LIFETIME);
|
GSSContext.DEFAULT_LIFETIME);
|
||||||
|
|
||||||
//context.requestMutualAuth(true);
|
//context.requestMutualAuth(true);
|
||||||
|
// TODO: used by IIS to pass token to Exchange ?
|
||||||
context.requestCredDeleg(true);
|
context.requestCredDeleg(true);
|
||||||
|
|
||||||
result = context.initSecContext(token, 0, token.length);
|
result = context.initSecContext(token, 0, token.length);
|
||||||
@ -124,40 +149,37 @@ public class KerberosHelper {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (result instanceof GSSException) {
|
if (result instanceof GSSException) {
|
||||||
|
// TODO: manage error codes
|
||||||
|
LOGGER.info("KerberosHelper.initSecurityContext exception code " + ((GSSException) result).getMajor() + " message" + ((GSSException) result).getMessage());
|
||||||
|
|
||||||
throw (GSSException) result;
|
throw (GSSException) result;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGGER.debug("KerberosHelper.getToken return " + ((byte[]) result).length + " bytes token");
|
LOGGER.debug("KerberosHelper.initSecurityContext return " + ((byte[]) result).length + " bytes token");
|
||||||
return (byte[]) result;
|
return (byte[]) result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setCredentials(String principal, String password) {
|
|
||||||
kerberosCallbackHandler = new KerberosCallbackHandler(principal, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create client Kerberos context.
|
* Create client Kerberos context.
|
||||||
*
|
*
|
||||||
* @return Login context
|
|
||||||
* @throws LoginException on error
|
* @throws LoginException on error
|
||||||
*/
|
*/
|
||||||
public static LoginContext login() throws LoginException {
|
public static void clientLogin() throws LoginException {
|
||||||
synchronized (LOCK) {
|
synchronized (LOCK) {
|
||||||
if (loginContext == null) {
|
if (clientLoginContext == null) {
|
||||||
final LoginContext localLoginContext = new LoginContext("spnego-client", kerberosCallbackHandler);
|
final LoginContext localLoginContext = new LoginContext("spnego-client", KERBEROS_CALLBACK_HANDLER);
|
||||||
localLoginContext.login();
|
localLoginContext.login();
|
||||||
loginContext = localLoginContext;
|
clientLoginContext = localLoginContext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return loginContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create server side Kerberos login context for provided credentials
|
* Create server side Kerberos login context for provided credentials.
|
||||||
*
|
*
|
||||||
* @param principal server principal
|
* @param principal server principal
|
||||||
* @param password server passsword
|
* @param password server passsword
|
||||||
* @return LoginContext
|
* @return LoginContext server login context
|
||||||
* @throws LoginException on error
|
* @throws LoginException on error
|
||||||
*/
|
*/
|
||||||
public static LoginContext serverLogin(final String principal, final String password) throws LoginException {
|
public static LoginContext serverLogin(final String principal, final String password) throws LoginException {
|
||||||
@ -182,14 +204,26 @@ public class KerberosHelper {
|
|||||||
return serverLoginContext;
|
return serverLoginContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains server Kerberos context information in server mode.
|
||||||
|
*/
|
||||||
public static class SecurityContext {
|
public static class SecurityContext {
|
||||||
// returned token
|
/**
|
||||||
|
* response token
|
||||||
|
*/
|
||||||
public byte[] token;
|
public byte[] token;
|
||||||
|
/**
|
||||||
|
* authenticated principal
|
||||||
|
*/
|
||||||
public String principal;
|
public String principal;
|
||||||
|
/**
|
||||||
|
* client delegated credential
|
||||||
|
*/
|
||||||
|
public GSSCredential clientCredential;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle Kerberos token in server login context
|
* Check client provided Kerberos token in server login context
|
||||||
*
|
*
|
||||||
* @param serverLoginContext server login context
|
* @param serverLoginContext server login context
|
||||||
* @param token Kerberos client token
|
* @param token Kerberos client token
|
||||||
@ -207,17 +241,22 @@ public class KerberosHelper {
|
|||||||
|
|
||||||
// get server credentials from context
|
// get server credentials from context
|
||||||
Oid krb5oid = new Oid("1.2.840.113554.1.2.2");
|
Oid krb5oid = new Oid("1.2.840.113554.1.2.2");
|
||||||
GSSCredential serverCreds = manager.createCredential(null,
|
GSSCredential serverCreds = manager.createCredential(null/* use name from login context*/,
|
||||||
GSSCredential.DEFAULT_LIFETIME,
|
GSSCredential.DEFAULT_LIFETIME,
|
||||||
krb5oid,
|
krb5oid,
|
||||||
GSSCredential.ACCEPT_ONLY);
|
GSSCredential.ACCEPT_ONLY/* server mode */);
|
||||||
|
|
||||||
GSSContext context = manager.createContext(serverCreds);
|
GSSContext context = manager.createContext(serverCreds);
|
||||||
|
|
||||||
securityContext.token = context.acceptSecContext(token, 0, token.length);
|
securityContext.token = context.acceptSecContext(token, 0, token.length);
|
||||||
if (context.isEstablished()) {
|
if (context.isEstablished()) {
|
||||||
securityContext.principal = context.getSrcName().toString();
|
securityContext.principal = context.getSrcName().toString();
|
||||||
LOGGER.debug("Authenticated user: " + securityContext.principal);
|
LOGGER.debug("Authenticated user: " + securityContext.principal);
|
||||||
|
if (!context.getCredDelegState()) {
|
||||||
|
LOGGER.debug("Credentials can not be delegated");
|
||||||
|
} else {
|
||||||
|
// Get client delegated credentials from context (gateway mode)
|
||||||
|
securityContext.clientCredential = context.getDelegCred();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
result = securityContext;
|
result = securityContext;
|
||||||
} catch (GSSException e) {
|
} catch (GSSException e) {
|
||||||
@ -227,6 +266,8 @@ public class KerberosHelper {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (result instanceof GSSException) {
|
if (result instanceof GSSException) {
|
||||||
|
// TODO: manage error codes
|
||||||
|
LOGGER.info("KerberosHelper.acceptSecurityContext exception code " + ((GSSException) result).getMajor() + " message" + ((GSSException) result).getMessage());
|
||||||
throw (GSSException) result;
|
throw (GSSException) result;
|
||||||
}
|
}
|
||||||
return (SecurityContext) result;
|
return (SecurityContext) result;
|
||||||
|
@ -187,11 +187,11 @@ public class SpNegoScheme implements AuthScheme {
|
|||||||
try {
|
try {
|
||||||
if (this.state == INITIATED || this.state == FAILED) {
|
if (this.state == INITIATED || this.state == FAILED) {
|
||||||
// send initial token to server
|
// send initial token to server
|
||||||
response = EncodingUtil.getAsciiString(Base64.encodeBase64(KerberosHelper.getToken(host, new byte[0])));
|
response = EncodingUtil.getAsciiString(Base64.encodeBase64(KerberosHelper.initSecurityContext(host, new byte[0])));
|
||||||
this.state = TYPE1_MSG_GENERATED;
|
this.state = TYPE1_MSG_GENERATED;
|
||||||
} else {
|
} else {
|
||||||
// send challenge response
|
// send challenge response
|
||||||
response = EncodingUtil.getAsciiString(Base64.encodeBase64(KerberosHelper.getToken(host, serverToken)));
|
response = EncodingUtil.getAsciiString(Base64.encodeBase64(KerberosHelper.initSecurityContext(host, serverToken)));
|
||||||
this.state = TYPE3_MSG_GENERATED;
|
this.state = TYPE3_MSG_GENERATED;
|
||||||
}
|
}
|
||||||
} catch (GSSException gsse) {
|
} catch (GSSException gsse) {
|
||||||
|
Loading…
Reference in New Issue
Block a user