All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.orientechnologies.security.kerberos.OKerberosAuthenticator Maven / Gradle / Ivy

/**
 * Copyright 2010-2016 OrientDB LTD (http://orientdb.com)
 * 

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *

* For more information: http://www.orientdb.com */ package com.orientechnologies.security.kerberos; import com.orientechnologies.common.log.OLogManager; import com.orientechnologies.common.parser.OSystemVariableResolver; import com.orientechnologies.orient.core.metadata.schema.OType; import com.orientechnologies.orient.core.record.impl.ODocument; import com.orientechnologies.orient.core.security.kerberos.OKrb5ClientLoginModuleConfig; import com.orientechnologies.orient.server.OServer; import com.orientechnologies.orient.server.config.OServerConfigurationManager; import com.orientechnologies.orient.server.network.protocol.http.OHttpUtils; import com.orientechnologies.orient.server.security.OSecurityAuthenticatorAbstract; import com.orientechnologies.orient.server.security.OSecurityAuthenticatorException; import javax.security.auth.Subject; import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginContext; import java.util.Base64; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; // Temporary, for Java 7 support. //import sun.misc.BASE64Decoder; /** * Implements the Kerberos authenticator module. * * @author S. Colin Leister */ public class OKerberosAuthenticator extends OSecurityAuthenticatorAbstract { private final String KERBEROS_PLUGIN_VERSION = "0.15"; private final long TicketRelayExpiration = 600000L; // 10 minutes, do not change private final ConcurrentHashMap _TicketRelayMap = new ConcurrentHashMap(); private String _Client_CCName = System.getenv("KRB5CCNAME"); private String _Client_KTName = System.getenv("KRB5_CLIENT_KTNAME"); private String _Client_Principal; private boolean _Client_UseTicketCache = false; private int _Client_Period = 300; // Default to 5 hours (300 minutes). private Timer _RenewalTimer; // Timer used to renew the LDAP client service ticket. private String _Krb5_Config = System.getenv("KRB5_CONFIG"); private String _Service_KTName = System.getenv("KRB5_KTNAME"); private String _Service_Principal; private String _SPNEGO_KTName = System.getenv("KRB5_KTNAME"); private String _SPNEGO_Principal; private Object _AuthenticateSync = new Object(); private Subject _Client_Subject; // Used in dbImport() for communicating with LDAP. private Subject _Service_Subject; // Used in authenticate() for decrypting service tickets from binary clients. private Subject _SPNEGO_Subject; // Used in authenticate() for decrypting service tickets from REST clients. private Timer _ExpirationTimer; /*** * OSecurityAuthenticator Interface ***/ // Called once the Server is running. public void active() { ExpirationTask task = new ExpirationTask(); _ExpirationTimer = new Timer(true); _ExpirationTimer.scheduleAtFixedRate(task, 30000, TicketRelayExpiration); // Wait 30 seconds before starting RenewalTask renewalTask = new RenewalTask(); _RenewalTimer = new Timer(true); _RenewalTimer.scheduleAtFixedRate(renewalTask, _Client_Period * 1000 * 60, _Client_Period * 1000 * 60); // Wait 30 seconds before starting OLogManager.instance().info(this, "OrientDB Kerberos Version: " + KERBEROS_PLUGIN_VERSION); OLogManager.instance().info(this, "***********************************************"); OLogManager.instance().info(this, "** OrientDB Kerberos Authenticator Is Active **"); OLogManager.instance().info(this, "***********************************************"); } // OSecurityAuthenticator // Kerberos magic happens here. public String authenticate(final String username, final String password) { // username will contain either the principal or be null. // password will contain either a Kerberos 5 service ticket or a SPNEGO ticket. String principal = null; try { if (isDebug()) { OLogManager.instance().info(this, "** Authenticating username: %s", username); if (OKerberosLibrary.isServiceTicket(password)) OLogManager.instance().info(this, "** Authenticating password: SERVICE TICKET"); else { OLogManager.instance().info(this, "** Authenticating password: %s", password); } } if (password != null) { if (OKerberosLibrary.isServiceTicket(password)) { // We can't call OKerberosLibrary.authenticate() twice with the same service ticket. // If we do, the call to context.acceptSecContext() will think it's a replay attack. // OServer.openDatabase() will end up calling this method twice if its call to database.open() fails. // So, we store the hash of the service ticket and the principal retrieved from the service ticket in an TicketItem, // and we use a HashMap to store the ticket for five minutes. // If this authenticate() is called more than once, we retrieve the TicketItem for the username, and we compare // the service ticket's hash code. If they match, we return the principal. TicketItem ti = getTicket(Integer.toString(password.hashCode())); if (ti != null && ti.getHashCode() == password.hashCode()) { if (isDebug()) OLogManager.instance().info(this, "OKerberosAuthenticator.authenticate() TicketHash and password Hash are equal, return principal: " + ti .getPrincipal()); if (isDebug()) OLogManager.instance().info(this, "OKerberosAuthenticator.authenticate() principal: " + ti.getPrincipal()); principal = ti.getPrincipal(); } else { byte[] ticket = Base64.getDecoder().decode(password.getBytes("UTF8")); // Temporary, for Java 7 support. // byte[] ticket = new BASE64Decoder().decodeBuffer(password); // byte [] ticket = java.util.Base64.getDecoder().decode(password); // principal = OKerberosLibrary.authenticate(_Service_Subject, _Service_Principal, username, ticket); try { synchronized (_AuthenticateSync) { if (OKerberosLibrary.isSPNegoTicket(ticket)) { principal = OKerberosLibrary.getSPNegoSource(_SPNEGO_Subject, _SPNEGO_Principal, ticket); } else { principal = OKerberosLibrary.getKerberosSource(_Service_Subject, _Service_Principal, ticket); } } } catch (Exception e) { OLogManager.instance().error(this, "OKerberosAuthenticator.authenticate() Exception: ", e); } if (isDebug()) OLogManager.instance() .info(this, "OKerberosAuthenticator.authenticate() OKerberosLibrary.authenticate() returned " + principal); // OLogManager.instance().info(this, "OKerberosAuthenticator.authenticate() addTicket hashCode: " + password.hashCode()); // null is an acceptable principal to store so that subsequent calls using the same ticket will immediately return null addTicket(Integer.toString(password.hashCode()), password.hashCode(), principal); } } } } catch (Exception ex) { OLogManager.instance().debug(this, "OKerberosAuthenticator.authenticate() Exception: ", ex); } return principal; } // OSecurityAuthenticator public void config(final OServer oServer, final OServerConfigurationManager serverCfg, final ODocument kerbConfig) { super.config(oServer, serverCfg, kerbConfig); if (kerbConfig.containsField("krb5_config")) { _Krb5_Config = OSystemVariableResolver.resolveSystemVariables((String) kerbConfig.field("krb5_config")); OLogManager.instance().info(this, "Krb5Config = " + _Krb5_Config); } // service if (kerbConfig.containsField("service")) { ODocument serviceDoc = kerbConfig.field("service"); if (serviceDoc.containsField("ktname")) { _Service_KTName = OSystemVariableResolver.resolveSystemVariables((String) serviceDoc.field("ktname")); OLogManager.instance().info(this, "Svc ktname = " + _Service_KTName); } if (serviceDoc.containsField("principal")) { _Service_Principal = serviceDoc.field("principal"); OLogManager.instance().info(this, "Svc princ = " + _Service_Principal); } } // SPNEGO if (kerbConfig.containsField("spnego")) { ODocument spnegoDoc = kerbConfig.field("spnego"); if (spnegoDoc.containsField("ktname")) { _SPNEGO_KTName = OSystemVariableResolver.resolveSystemVariables((String) spnegoDoc.field("ktname")); OLogManager.instance().info(this, "SPNEGO ktname = " + _SPNEGO_KTName); } if (spnegoDoc.containsField("principal")) { _SPNEGO_Principal = spnegoDoc.field("principal"); OLogManager.instance().info(this, "SPNEGO princ = " + _SPNEGO_Principal); } } // client if (kerbConfig.containsField("client")) { ODocument clientDoc = kerbConfig.field("client"); if (clientDoc.containsField("useTicketCache")) { _Client_UseTicketCache = (Boolean) clientDoc.field("useTicketCache", OType.BOOLEAN); OLogManager.instance().info(this, "Client useTicketCache = " + _Client_UseTicketCache); } if (clientDoc.containsField("principal")) { _Client_Principal = clientDoc.field("principal"); OLogManager.instance().info(this, "Client princ = " + _Client_Principal); } if (clientDoc.containsField("ccname")) { _Client_CCName = OSystemVariableResolver.resolveSystemVariables((String) clientDoc.field("ccname")); OLogManager.instance().info(this, "Client ccname = " + _Client_CCName); } if (clientDoc.containsField("ktname")) { _Client_KTName = OSystemVariableResolver.resolveSystemVariables((String) clientDoc.field("ktname")); OLogManager.instance().info(this, "Client ktname = " + _Client_KTName); } if (clientDoc.containsField("renewalPeriod")) { _Client_Period = clientDoc.field("renewalPeriod"); } } // Initialize Kerberos initializeKerberos(); synchronized (_AuthenticateSync) { createServiceSubject(); createSpnegoSubject(); } createClientSubject(); } // OSecurityAuthenticator // Called on removal of the authenticator. public void dispose() { if (_ExpirationTimer != null) { _ExpirationTimer.cancel(); _ExpirationTimer = null; } if (_RenewalTimer != null) { _RenewalTimer.cancel(); _RenewalTimer = null; } synchronized (_TicketRelayMap) { _TicketRelayMap.clear(); } } // OSecurityAuthenticator public String getAuthenticationHeader(final String databaseName) { String header = null; // SPNEGO support. // if(databaseName != null) header = "WWW-Authenticate: Negotiate realm=\"OrientDB db-" + databaseName + "\""; // else header = "WWW-Authenticate: Negotiate realm=\"OrientDB Server\""; header = OHttpUtils.HEADER_AUTHENTICATE_NEGOTIATE; //"WWW-Authenticate: Negotiate"; // if(databaseName != null) header = "WWW-Authenticate: Negotiate\nWWW-Authenticate: Basic realm=\"OrientDB db-" + databaseName + "\""; // else header = "WWW-Authenticate: Negotiate\nWWW-Authenticate: Basic realm=\"OrientDB Server\""; return header; } // OSecurityAuthenticator public Subject getClientSubject() { return _Client_Subject; } // OSecurityAuthenticator public boolean isSingleSignOnSupported() { return true; } /*** * Kerberos ***/ private void initializeKerberos() { if (_Krb5_Config == null) throw new OSecurityAuthenticatorException("OKerberosAuthenticator KRB5 Config cannot be null"); System.setProperty("sun.security.krb5.debug", Boolean.toString(isDebug())); System.setProperty("sun.security.spnego.debug", Boolean.toString(isDebug())); System.setProperty("java.security.krb5.conf", _Krb5_Config); System.setProperty("javax.security.auth.useSubjectCredsOnly", "true"); } private void createServiceSubject() { if (_Service_Principal == null) throw new OSecurityAuthenticatorException("OKerberosAuthenticator.createServiceSubject() Service Principal cannot be null"); if (_Service_KTName == null) throw new OSecurityAuthenticatorException("OKerberosAuthenticator.createServiceSubject() Service KeyTab cannot be null"); try { Configuration cfg = new OKrb5LoginModuleConfig(_Service_Principal, _Service_KTName); OLogManager.instance().info(this, "createServiceSubject() Service Principal: " + _Service_Principal); LoginContext lc = new LoginContext("ignore", null, null, cfg); lc.login(); _Service_Subject = lc.getSubject(); if (_Service_Subject != null) { OKerberosLibrary.checkNativeJGSS(_Service_Subject, _Service_Principal, false); OLogManager.instance().info(this, "** Created Kerberos Service Subject **"); } } catch (Exception ex) { OLogManager.instance().error(this, "createServiceSubject() Exception: ", ex); } if (_Service_Subject == null) throw new OSecurityAuthenticatorException("OKerberosAuthenticator could not create service Subject"); } private void createSpnegoSubject() { if (_SPNEGO_Principal == null) throw new OSecurityAuthenticatorException("OKerberosAuthenticator.createSpnegoSubject() SPNEGO Principal cannot be null"); if (_SPNEGO_KTName == null) throw new OSecurityAuthenticatorException("OKerberosAuthenticator.createSpnegoSubject() SPNEGO KeyTab cannot be null"); try { Configuration cfg = new OKrb5LoginModuleConfig(_SPNEGO_Principal, _SPNEGO_KTName); OLogManager.instance().info(this, "createSpnegoSubject() SPNEGO Principal: " + _SPNEGO_Principal); LoginContext lc = new LoginContext("ignore", null, null, cfg); lc.login(); _SPNEGO_Subject = lc.getSubject(); if (_SPNEGO_Subject != null) { OKerberosLibrary.checkNativeJGSS(_SPNEGO_Subject, _SPNEGO_Principal, false); OLogManager.instance().info(this, "** Created Kerberos SPNEGO Subject **"); } } catch (Exception ex) { OLogManager.instance().error(this, "createSpnegoSubject() Exception: ", ex); } if (_SPNEGO_Subject == null) throw new OSecurityAuthenticatorException("OKerberosAuthenticator could not create SPNEGO Subject"); } private void createClientSubject() { if (_Client_Principal == null) throw new OSecurityAuthenticatorException("OKerberosAuthenticator.createClientSubject() Client Principal cannot be null"); if (_Client_UseTicketCache && _Client_CCName == null) throw new OSecurityAuthenticatorException( "OKerberosAuthenticator.createClientSubject() Client UseTicketCache cannot be true while Credential Cache is null"); if (_Client_CCName == null && _Client_KTName == null) throw new OSecurityAuthenticatorException( "OKerberosAuthenticator.createClientSubject() Client Credential Cache and Client KeyTab cannot both be null"); try { Configuration cfg = new OKrb5ClientLoginModuleConfig(_Client_Principal, _Client_UseTicketCache, _Client_CCName, _Client_KTName); OLogManager.instance().info(this, "createClientSubject() Client Principal: " + _Client_Principal); LoginContext lc = new LoginContext("ignore", null, null, cfg); lc.login(); _Client_Subject = lc.getSubject(); if (_Client_Subject != null) { OKerberosLibrary.checkNativeJGSS(_Client_Subject, _Client_Principal, true); OLogManager.instance().info(this, "** Created Kerberos Client Subject **"); } } catch (Exception ex) { OLogManager.instance().error(this, "createClientSubject() Exception: ", ex); } if (_Client_Subject == null) throw new OSecurityAuthenticatorException("OKerberosAuthenticator could not create client Subject"); } // If the TicketItem already exists for id it is replaced. private void addTicket(String id, int hashCode, String principal) { synchronized (_TicketRelayMap) { _TicketRelayMap.put(id, new TicketItem(hashCode, principal)); } } private TicketItem getTicket(String id) { TicketItem ti = null; synchronized (_TicketRelayMap) { ti = _TicketRelayMap.get(id); } return ti; } private void removeTicket(String id) { synchronized (_TicketRelayMap) { if (_TicketRelayMap.containsKey(id)) { _TicketRelayMap.remove(id); } } } private void checkTicketExpirations() { synchronized (_TicketRelayMap) { long currTime = System.currentTimeMillis(); for (Map.Entry entry : _TicketRelayMap.entrySet()) { if (entry.getValue().hasExpired(currTime)) { // OLogManager.instance().info(this, "~~~~~~~~ checkTicketExpirations() Ticket has expired: " + entry.getValue().getHashCode() + "\n"); _TicketRelayMap.remove(entry.getKey()); } } } } /*** * Ticket Cache ***/ private class TicketItem { private int _HashCode; private String _Principal; private long _Time; public TicketItem(int hashCode, String principal) { _HashCode = hashCode; _Principal = principal; _Time = System.currentTimeMillis(); } public int getHashCode() { return _HashCode; } public String getPrincipal() { return _Principal; } public boolean hasExpired(long currTime) { return (currTime - _Time) >= TicketRelayExpiration; } } private class ExpirationTask extends TimerTask { @Override public void run() { checkTicketExpirations(); } } private class RenewalTask extends TimerTask { @Override public void run() { createClientSubject(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy