KerberosConnection createKerberosUtility(ConnectionConfig config) { final String principal = config.kerberosPrincipal(); if (null != principal) { return new KerberosConnection(principal, config.kerberosKeytab()); } return null; }
/** * Performs a kerberos login, possibly logging out first. * * @param prevContext The LoginContext from the previous login, or null * @param conf JAAS Configuration object * @param subject The JAAS Subject * @return The context and subject from the login * @throws LoginException If the login failed. */ Entry<LoginContext, Subject> login(LoginContext prevContext, Configuration conf, Subject subject) throws LoginException { // Is synchronized by the caller // If a context was provided, perform a logout first if (null != prevContext) { prevContext.logout(); } // Create a LoginContext given the Configuration and Subject LoginContext loginContext = createLoginContext(conf); // Invoke the login loginContext.login(); // Get the Subject from the context and verify it's non-null (null would imply failure) Subject loggedInSubject = loginContext.getSubject(); if (null == loggedInSubject) { throw new RuntimeException("Failed to perform Kerberos login"); } // Send it back to the caller to use with launchRenewalThread return new AbstractMap.SimpleEntry<>(loginContext, loggedInSubject); }
/** * Perform a Kerberos login and launch a daemon thread to periodically perfrom renewals of that * Kerberos login. Exceptions are intentionally caught and rethrown as unchecked exceptions as * there is nothing Avatica itself can do if the Kerberos login fails. * * @throws RuntimeException If the Kerberos login fails */ public synchronized void login() { final Entry<LoginContext, Subject> securityMaterial = performKerberosLogin(); subject = securityMaterial.getValue(); // Launch a thread to periodically perform renewals final Entry<RenewalTask, Thread> renewalMaterial = createRenewalThread( securityMaterial.getKey(), subject, KerberosConnection.RENEWAL_PERIOD); renewalTask = renewalMaterial.getKey(); renewalThread = renewalMaterial.getValue(); renewalThread.start(); }
@Override public AppConfigurationEntry[] getAppConfigurationEntry(String name) { Map<String, String> options = new HashMap<String, String>(); options.put("principal", principal); options.put("refreshKrb5Config", "true"); if (KerberosConnection.isIbmJava()) { options.put("useKeytab", keytab); options.put("credsType", "both"); } else { options.put("keyTab", keytab); options.put("useKeyTab", "true"); options.put("isInitiator", "true"); options.put("doNotPrompt", "true"); options.put("storeKey", "true"); } LOG.debug("JAAS Configuration for client keytab-based Kerberos login: {}", options); return new AppConfigurationEntry[] {new AppConfigurationEntry( KerberosConnection.getKrb5LoginModuleName(), AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options)}; } }
@Test public void previousContextLoggedOut() throws Exception { KerberosConnection krbUtil = mock(KerberosConnection.class); Subject subject = new Subject(); Subject loggedInSubject = new Subject(); Configuration conf = mock(Configuration.class); LoginContext originalContext = mock(LoginContext.class); LoginContext context = mock(LoginContext.class); // Call the real login(LoginContext, Configuration, Subject) method when(krbUtil.login(any(LoginContext.class), any(Configuration.class), any(Subject.class))) .thenCallRealMethod(); // Return a fake LoginContext when(krbUtil.createLoginContext(conf)).thenReturn(context); // Return a fake Subject from that fake LoginContext when(context.getSubject()).thenReturn(loggedInSubject); Entry<LoginContext, Subject> pair = krbUtil.login(originalContext, conf, subject); // Verify we get the fake LoginContext and Subject assertEquals(context, pair.getKey()); assertEquals(loggedInSubject, pair.getValue()); verify(originalContext).logout(); // login should be called on the LoginContext verify(context).login(); }
@Test public void testThreadConfiguration() { KerberosConnection krbUtil = new KerberosConnection("foo", new File("/bar.keytab")); Subject subject = new Subject(); LoginContext context = Mockito.mock(LoginContext.class); Entry<RenewalTask, Thread> entry = krbUtil.createRenewalThread(context, subject, 10); assertNotNull("RenewalTask should not be null", entry.getKey()); Thread t = entry.getValue(); assertTrue("Thread name should contain 'Avatica', but is '" + t.getName() + "'", t.getName().contains("Avatica")); assertTrue(t.isDaemon()); assertNotNull(t.getUncaughtExceptionHandler()); }
/** * Logout and log back in with the Kerberos identity. */ void renew() { try { // Lock on the instance of KerberosUtil synchronized (utilInstance) { Entry<LoginContext, Subject> pair = utilInstance.login(context, conf, subject); context = pair.getKey(); subject = pair.getValue(); } } catch (Exception e) { throw new RuntimeException("Failed to perform kerberos login"); } }
@Override public byte[] send(final byte[] request) { return Subject.doAs(kerberosUtil.getSubject(), new PrivilegedAction<byte[]>() { @Override public byte[] run() { return wrapped.send(request); } }); } }
public void close() throws SQLException { if (!closed) { closed = true; // Per specification, if onConnectionClose throws, this method will throw // a SQLException, but statement will still be closed. try { meta.closeConnection(handle); driver.handler.onConnectionClose(this); if (null != kerberosConnection) { kerberosConnection.stopRenewalThread(); } } catch (RuntimeException e) { throw HELPER.createException("While closing connection", e); } } }
KerberosTicket activeTicket = null; for (KerberosTicket ticket : tickets) { if (isTGSPrincipal(ticket.getServer())) { activeTicket = ticket; break;
@Override public AppConfigurationEntry[] getAppConfigurationEntry(String name) { Map<String, String> options = new HashMap<String, String>(); options.put("principal", principal); options.put("refreshKrb5Config", "true"); if (KerberosConnection.isIbmJava()) { options.put("useKeytab", keytab); options.put("credsType", "both"); } else { options.put("keyTab", keytab); options.put("useKeyTab", "true"); options.put("isInitiator", "false"); options.put("doNotPrompt", "true"); options.put("storeKey", "true"); } LOG.debug("JAAS Configuration for server keytab-based Kerberos login: {}", options); return new AppConfigurationEntry[] {new AppConfigurationEntry( KerberosConnection.getKrb5LoginModuleName(), AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options)}; } }
@Test public void noPreviousContextOnLogin() throws Exception { KerberosConnection krbUtil = mock(KerberosConnection.class); Subject subject = new Subject(); Subject loggedInSubject = new Subject(); Configuration conf = mock(Configuration.class); LoginContext context = mock(LoginContext.class); // Call the real login(LoginContext, Configuration, Subject) method when(krbUtil.login(nullable(LoginContext.class), any(Configuration.class), any(Subject.class))) .thenCallRealMethod(); // Return a fake LoginContext when(krbUtil.createLoginContext(conf)).thenReturn(context); // Return a fake Subject from that fake LoginContext when(context.getSubject()).thenReturn(loggedInSubject); Entry<LoginContext, Subject> pair = krbUtil.login(null, conf, subject); // Verify we get the fake LoginContext and Subject assertEquals(context, pair.getKey()); assertEquals(loggedInSubject, pair.getValue()); // login should be called on the LoginContext verify(context).login(); }
@Test public void testThreadConfiguration() { KerberosConnection krbUtil = new KerberosConnection("foo", new File("/bar.keytab")); Subject subject = new Subject(); LoginContext context = Mockito.mock(LoginContext.class); Entry<RenewalTask, Thread> entry = krbUtil.createRenewalThread(context, subject, 10); assertNotNull("RenewalTask should not be null", entry.getKey()); Thread t = entry.getValue(); assertTrue("Thread name should contain 'Avatica', but is '" + t.getName() + "'", t.getName().contains("Avatica")); assertTrue(t.isDaemon()); assertNotNull(t.getUncaughtExceptionHandler()); }
/** * Logout and log back in with the Kerberos identity. */ void renew() { try { // Lock on the instance of KerberosUtil synchronized (utilInstance) { Entry<LoginContext, Subject> pair = utilInstance.login(context, conf, subject); context = pair.getKey(); subject = pair.getValue(); } } catch (Exception e) { throw new RuntimeException("Failed to perform kerberos login"); } }
@Override public byte[] send(final byte[] request) { return Subject.doAs(kerberosUtil.getSubject(), new PrivilegedAction<byte[]>() { @Override public byte[] run() { return wrapped.send(request); } }); } }
public void close() throws SQLException { if (!closed) { closed = true; // Per specification, if onConnectionClose throws, this method will throw // a SQLException, but statement will still be closed. try { meta.closeConnection(handle); driver.handler.onConnectionClose(this); if (null != kerberosConnection) { kerberosConnection.stopRenewalThread(); } } catch (RuntimeException e) { throw HELPER.createException("While closing connection", e); } } }
KerberosTicket activeTicket = null; for (KerberosTicket ticket : tickets) { if (isTGSPrincipal(ticket.getServer())) { activeTicket = ticket; break;
@Override public AppConfigurationEntry[] getAppConfigurationEntry(String name) { Map<String, String> options = new HashMap<String, String>(); options.put("principal", principal); options.put("refreshKrb5Config", "true"); if (KerberosConnection.isIbmJava()) { options.put("useKeytab", keytab); options.put("credsType", "both"); } else { options.put("keyTab", keytab); options.put("useKeyTab", "true"); options.put("isInitiator", "false"); options.put("doNotPrompt", "true"); options.put("storeKey", "true"); } LOG.debug("JAAS Configuration for server keytab-based Kerberos login: {}", options); return new AppConfigurationEntry[] {new AppConfigurationEntry( KerberosConnection.getKrb5LoginModuleName(), AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options)}; } }
@Test public void noPreviousContextOnLogin() throws Exception { KerberosConnection krbUtil = mock(KerberosConnection.class); Subject subject = new Subject(); Subject loggedInSubject = new Subject(); Configuration conf = mock(Configuration.class); LoginContext context = mock(LoginContext.class); // Call the real login(LoginContext, Configuration, Subject) method when(krbUtil.login(nullable(LoginContext.class), any(Configuration.class), any(Subject.class))) .thenCallRealMethod(); // Return a fake LoginContext when(krbUtil.createLoginContext(conf)).thenReturn(context); // Return a fake Subject from that fake LoginContext when(context.getSubject()).thenReturn(loggedInSubject); Entry<LoginContext, Subject> pair = krbUtil.login(null, conf, subject); // Verify we get the fake LoginContext and Subject assertEquals(context, pair.getKey()); assertEquals(loggedInSubject, pair.getValue()); // login should be called on the LoginContext verify(context).login(); }
/** * Performs a Kerberos login given the {@code principal} and {@code keytab}. * * @return The {@code Subject} and {@code LoginContext} from the successful login. * @throws RuntimeException if the login failed */ Entry<LoginContext, Subject> performKerberosLogin() { // Loosely based on Apache Kerby's JaasKrbUtil class // Synchronized by the caller // Create a KerberosPrincipal given the principal. final Set<Principal> principals = new HashSet<Principal>(); principals.add(new KerberosPrincipal(principal)); final Subject subject = new Subject(false, principals, new HashSet<Object>(), new HashSet<Object>()); try { return login(null, jaasConf, subject); } catch (Exception e) { throw new RuntimeException("Failed to perform Kerberos login"); } }