From 03d9cb557bd9d3fc6542ac02f704174514f0f5c0 Mon Sep 17 00:00:00 2001 From: Sahil Hindwani Date: Mon, 22 Jun 2020 11:26:51 +0530 Subject: [PATCH] PR-125: Use of keytab when requestNewKerberosTicket is true. --- .../cloudsoft/winrm4j/client/WinRmClient.java | 35 ++++++++++--------- .../winrm4j/client/WinRmClientBuilder.java | 22 ++++++++++++ .../io/cloudsoft/winrm4j/winrm/WinRmTool.java | 26 ++++++++++++-- 3 files changed, 65 insertions(+), 18 deletions(-) diff --git a/client/src/main/java/io/cloudsoft/winrm4j/client/WinRmClient.java b/client/src/main/java/io/cloudsoft/winrm4j/client/WinRmClient.java index 136075ca..5d0da094 100644 --- a/client/src/main/java/io/cloudsoft/winrm4j/client/WinRmClient.java +++ b/client/src/main/java/io/cloudsoft/winrm4j/client/WinRmClient.java @@ -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 * @@ -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; @@ -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 authSchemeRegistry = RegistryBuilder.create() @@ -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() { public GSSCredential run() { try { @@ -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) { @@ -412,7 +409,7 @@ private static Subject kerberosLogin(String username, String password) { } /** - * Configuration for Kerberos login.
+ * Configuration for Kerberos login using username and password or keytab.
* 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 @@ -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 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 diff --git a/client/src/main/java/io/cloudsoft/winrm4j/client/WinRmClientBuilder.java b/client/src/main/java/io/cloudsoft/winrm4j/client/WinRmClientBuilder.java index 3ad33f99..81b79153 100644 --- a/client/src/main/java/io/cloudsoft/winrm4j/client/WinRmClientBuilder.java +++ b/client/src/main/java/io/cloudsoft/winrm4j/client/WinRmClientBuilder.java @@ -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"))); @@ -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 */ diff --git a/winrm4j/src/main/java/io/cloudsoft/winrm4j/winrm/WinRmTool.java b/winrm4j/src/main/java/io/cloudsoft/winrm4j/winrm/WinRmTool.java index 1a144095..164d8611 100644 --- a/winrm4j/src/main/java/io/cloudsoft/winrm4j/winrm/WinRmTool.java +++ b/winrm4j/src/main/java/io/cloudsoft/winrm4j/winrm/WinRmTool.java @@ -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; @@ -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+)$"); @@ -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 @@ -198,7 +212,7 @@ private WinRmTool(String address, String domain, String username, boolean disableCertificateChecks, String workingDirectory, Map 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; @@ -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; } /** @@ -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();