Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR-125: Support Use of keytab when requestNewKerberosTicket is true. #126

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 19 additions & 16 deletions client/src/main/java/io/cloudsoft/winrm4j/client/WinRmClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,6 @@ public class WinRmClient implements AutoCloseable {
*/
private static final String KERBEROS_OID = "1.2.840.113554.1.2.2";

/**
* Default JAAS configuration for Kerberos authentication.
*/
private static final Configuration JAAS_KERB_LOGIN_CONF = new KerberosJaasConfiguration();

/**
* Create a WinRmClient builder
*
Expand Down Expand Up @@ -250,6 +245,8 @@ private static void initializeClientAndService(WinRm winrm, WinRmClientBuilder b
String username = builder.username;
String password = builder.password;
String domain = builder.domain;
String keyTabFilePath = builder.keyTabFilePath;
boolean useKeyTab = builder.useKeyTab;
boolean disableCertificateChecks = builder.disableCertificateChecks;
HostnameVerifier hostnameVerifier = builder.hostnameVerifier;
SSLSocketFactory sslSocketFactory = builder.sslSocketFactory;
Expand Down Expand Up @@ -300,7 +297,7 @@ private static void initializeClientAndService(WinRm winrm, WinRmClientBuilder b
* in order to be used by the HttpAuthenticator to generate the Spnego token.
*/
Credentials creds = authenticationScheme == AuthSchemes.KERBEROS && builder.requestNewKerberosTicket
? getKerberosCreds(username, password)
? getKerberosCreds(username, password, keyTabFilePath, useKeyTab)
: new NTCredentials(username, password, null, domain);

Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
Expand Down Expand Up @@ -365,12 +362,12 @@ public X509Certificate[] getAcceptedIssuers() {
* @param password password of the user account
* @return credentials wrapping the TGT which will be used for obtaining the SPNego token
*/
private static KerberosCredentials getKerberosCreds(String username, String password) {
private static KerberosCredentials getKerberosCreds(String username, String password, String keyTabFilePath, boolean useKeyTab) {
// If the Kerberos Realm is in uppercases (which is the norm) and the domain in the UPN is in lowercases
// a KrbException: "Message stream modified" is thrown. To avoid this exception we force the UPN in uppercases
// Maybe this should be customizable with a parameter?
String canonizedUsername = username.trim().toUpperCase();
Subject subject = kerberosLogin(canonizedUsername, password);
String canonizedUsername = username.trim();
Subject subject = kerberosLogin(canonizedUsername, password, useKeyTab, keyTabFilePath);
GSSCredential userCred = Subject.doAs(subject, new PrivilegedAction<GSSCredential>() {
public GSSCredential run() {
try {
Expand All @@ -396,11 +393,11 @@ public GSSCredential run() {
* @param password password of the user account
* @return subject of the authenticated user
*/
private static Subject kerberosLogin(String username, String password) {
private static Subject kerberosLogin(String username, String password, boolean useKeyTab, String keyTabPath) {
CallbackHandler callbackHandler = new NamePasswordCallbackHandler(username, password);
Subject subject;
try {
LoginContext lc = new LoginContext("", null, callbackHandler, JAAS_KERB_LOGIN_CONF);
LoginContext lc = new LoginContext("", null, callbackHandler, new KerberosJaasConfiguration(useKeyTab, keyTabPath, username));
lc.login();
subject = lc.getSubject();
} catch (LoginException e) {
Expand All @@ -412,7 +409,7 @@ private static Subject kerberosLogin(String username, String password) {
}

/**
* Configuration for Kerberos login.<br>
* Configuration for Kerberos login using username and password or keytab.<br>
* When this configuration is used (instead of the static JAAS config file) the purpose is to obtain a new TGT from
* the AS for the credentials provided to the {@link WinRmClientBuilder} and not to use an existing TGT from the
* cache. Thus this configuration disable the cache and the prompt in order to force the use of the credentials
Expand All @@ -421,14 +418,20 @@ private static Subject kerberosLogin(String username, String password) {
private static class KerberosJaasConfiguration extends Configuration {
private final AppConfigurationEntry[] appConfigurationEntries;

KerberosJaasConfiguration() {
KerberosJaasConfiguration(boolean useKeyTab, String keyTabFilePath, String username) {
Map<String, String> options = new HashMap<>();
options.put("doNoPrompt", "true");
options.put("client", "true");
options.put("isInitiator", "true");
options.put("useTicketCache", "false");
appConfigurationEntries = new AppConfigurationEntry[] { new AppConfigurationEntry(
"com.sun.security.auth.module.Krb5LoginModule", LoginModuleControlFlag.REQUIRED, options) };
options.put("isInitiator", "true");

if (useKeyTab) {
options.put("useKeyTab", "true");
options.put("keyTab", keyTabFilePath);
options.put("principal", username);
}
appConfigurationEntries = new AppConfigurationEntry[]{new AppConfigurationEntry(
"com.sun.security.auth.module.Krb5LoginModule", LoginModuleControlFlag.REQUIRED, options)};
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ public class WinRmClientBuilder {

protected SSLContext sslContext;
protected boolean requestNewKerberosTicket;
protected boolean useKeyTab;
protected String keyTabFilePath;

WinRmClientBuilder(String endpoint) {
this(toUrlUnchecked(WinRmClient.checkNotNull(endpoint, "endpoint")));
Expand Down Expand Up @@ -271,6 +273,26 @@ public WinRmClientBuilder requestNewKerberosTicket(boolean requestNewKerberosTic
return this;
}

/**
* Set this parameter to {@code true} for requesting from the KDC a fresh Kerberos TGT with credentials set to the builder.
* In this case the configuration defined in the JAAS configuration file will be ignored.
* By default this parameter is set to {@code false}.
*/
public WinRmClientBuilder useKeyTab(boolean useKeyTab) {
this.useKeyTab = useKeyTab;
return this;
}

/**
* Set this parameter to {@code true} for requesting from the KDC a fresh Kerberos TGT with credentials set to the builder.
* In this case the configuration defined in the JAAS configuration file will be ignored.
* By default this parameter is set to {@code false}.
*/
public WinRmClientBuilder keyTabFilePath(String keyTabPath) {
this.keyTabFilePath = keyTabPath;
return this;
}

/**
* Create a WinRmClient
*/
Expand Down
26 changes: 24 additions & 2 deletions winrm4j/src/main/java/io/cloudsoft/winrm4j/winrm/WinRmTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ public class WinRmTool {
private final SSLContext sslContext;
private final WinRmClientContext context;
private final boolean requestNewKerberosTicket;
private final boolean useKeyTab;
private final String keyTabFilePath;

public static class Builder {
private String authenticationScheme = AuthSchemes.NTLM;
Expand All @@ -74,6 +76,8 @@ public static class Builder {
private SSLContext sslContext;
private WinRmClientContext context;
private boolean requestNewKerberosTicket;
private boolean useKeyTab;
private String keyTabFilePath;

private static final Pattern matchPort = Pattern.compile(".*:(\\d+)$");

Expand Down Expand Up @@ -153,12 +157,22 @@ public Builder requestNewKerberosTicket(boolean requestNewKerberosTicket) {
return this;
}

public Builder useKeyTab(boolean useKeyTab) {
this.useKeyTab = useKeyTab;
return this;
}

public Builder useKeyTab(String keyTabFilePath) {
this.keyTabFilePath = keyTabFilePath;
return this;
}

public WinRmTool build() {
return new WinRmTool(getEndpointUrl(address, useHttps, port),
domain, username, password, authenticationScheme,
disableCertificateChecks, workingDirectory,
environment, hostnameVerifier, sslSocketFactory, sslContext,
context, requestNewKerberosTicket);
context, requestNewKerberosTicket, useKeyTab, keyTabFilePath);
}

// TODO remove arguments when method WinRmTool.connect() is removed
Expand Down Expand Up @@ -198,7 +212,7 @@ private WinRmTool(String address, String domain, String username,
boolean disableCertificateChecks, String workingDirectory,
Map<String, String> environment, HostnameVerifier hostnameVerifier,
SSLSocketFactory sslSocketFactory, SSLContext sslContext, WinRmClientContext context,
boolean requestNewKerberosTicket) {
boolean requestNewKerberosTicket, boolean useKeyTab, String keytabFilePath) {
this.disableCertificateChecks = disableCertificateChecks;
this.address = address;
this.domain = domain;
Expand All @@ -212,6 +226,8 @@ private WinRmTool(String address, String domain, String username,
this.sslContext = sslContext;
this.context = context;
this.requestNewKerberosTicket = requestNewKerberosTicket;
this.useKeyTab=useKeyTab;
this.keyTabFilePath=keytabFilePath;
}

/**
Expand Down Expand Up @@ -339,6 +355,12 @@ public WinRmToolResponse executeCommand(String command) {
if (requestNewKerberosTicket) {
builder.requestNewKerberosTicket(requestNewKerberosTicket);
}
if (useKeyTab) {
builder.useKeyTab(true);
}
if (keyTabFilePath != null) {
builder.keyTabFilePath(keyTabFilePath);
}

StringWriter out = new StringWriter();
StringWriter err = new StringWriter();
Expand Down