Created
April 7, 2016 07:04
-
-
Save ambud/220d676e804b00891b012a7bd8bbd630 to your computer and use it in GitHub Desktop.
Code snippet to get Kerberos authentication working in your Java app http://theprogrammerway.blogspot.com/2013/05/java-krb5loginmodule-keytab-mystery.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** Copyright 2013 Ambud Sharma | |
* | |
* 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. | |
*/ | |
import java.security.PrivilegedAction; | |
import java.util.Hashtable; | |
import java.util.logging.Level; | |
import java.util.logging.Logger; | |
import java.util.regex.Pattern; | |
import javax.naming.Context; | |
import javax.naming.NamingEnumeration; | |
import javax.naming.NamingException; | |
import javax.naming.directory.Attributes; | |
import javax.naming.directory.SearchControls; | |
import javax.naming.directory.SearchResult; | |
import javax.naming.ldap.InitialLdapContext; | |
import javax.security.auth.Subject; | |
import javax.security.auth.login.LoginContext; | |
import javax.security.auth.login.LoginException; | |
/** | |
* Provides LDAP/AD Integration | |
* | |
* @author Ambud Sharma | |
*/ | |
public class LDAPEngine { | |
public static final String MANAGED_BY = "managedBy"; | |
public static final String CN = "cn"; | |
private static final String KRB5CCNAME = "KRB5CCNAME"; | |
public static final String DISTINGUISHED_NAME = "distinguishedName"; | |
public static final String OBJECT_SID = "objectSid"; | |
public static final String SAM_ACCOUNT_NAME = "sAMAccountName"; | |
private static final Logger logger = Logger.getLogger(LDAPEngine.class.getCanonicalName()); | |
private static String SELECTED_LOGIN_CONTEXT = null; | |
private static InitialLdapContext dirContext; | |
private static Hashtable<String,String> env = new Hashtable<String,String>(); //reason to initialize outside doesn't throw any exceptions => safe to initialize inline!! | |
private static LoginContext lc=null; | |
private static String getAttr(String userName,String returnAttr){ | |
System.out.println("Looking up:"+userName); | |
String name=null; | |
try { | |
dirContext=new InitialLdapContext(env, null); | |
} catch (NamingException ex) { | |
Logger.getLogger(LDAPEngine.class.getName()).log(Level.SEVERE, null, ex); | |
} | |
if(dirContext!=null){ | |
if(userName!=null){ | |
String Filter="(&(objectclass=*)(sAMAccountName="+userName+"))"; | |
try { | |
SearchControls ctrl = new SearchControls(); | |
ctrl.setSearchScope(SearchControls.SUBTREE_SCOPE); | |
ctrl.setDerefLinkFlag(false); | |
ctrl.setCountLimit(1); | |
ctrl.setTimeLimit(20000); | |
NamingEnumeration<SearchResult> ans=dirContext.search( | |
System.getProperty("ldap.ou.user"), Filter,ctrl); | |
while(ans.hasMore()){ | |
SearchResult result=ans.next(); | |
Attributes attributes=result.getAttributes(); | |
try{ | |
name=attributes.get(returnAttr).get().toString(); | |
name=(Pattern.compile("\\[.*?\\]")).matcher(name).replaceAll("").trim(); | |
name=name.split(",")[1]+" "+name.split(",")[0]; | |
name=name.trim(); | |
} | |
catch(Exception e){ | |
System.err.println(e.getMessage()); | |
} | |
} | |
} catch (NamingException ex) { | |
Logger.getLogger(LDAPEngine.class.getName()).log(Level.SEVERE, null, ex); | |
} | |
} | |
} | |
return name; | |
} | |
private static String getAttrAsString(String searchStr,String searchCri,String returnAttr){ | |
String name=null; | |
try { | |
dirContext=new InitialLdapContext(env, null); | |
} catch (NamingException ex) { | |
Logger.getLogger(LDAPEngine.class.getName()).log(Level.SEVERE, null, ex); | |
} | |
if(dirContext!=null){ | |
if(searchStr!=null){ | |
String Filter="(&(objectclass=*)("+searchCri+"="+searchStr+"))"; | |
try { | |
SearchControls ctrl = new SearchControls(); | |
ctrl.setSearchScope(SearchControls.SUBTREE_SCOPE); | |
ctrl.setDerefLinkFlag(false); | |
ctrl.setCountLimit(1); | |
ctrl.setTimeLimit(20000); | |
NamingEnumeration<SearchResult> ans=dirContext.search( | |
System.getProperty("ldap.ou.computer"), Filter,ctrl); | |
while(ans.hasMore()){ | |
SearchResult result=ans.next(); | |
Attributes attributes=result.getAttributes(); | |
try{ | |
if(returnAttr.equalsIgnoreCase(OBJECT_SID)){ | |
byte[] data=(byte[]) attributes.get(returnAttr).get(); | |
name=getSIDAsString(data); | |
}else{ | |
name=attributes.get(returnAttr).get().toString(); | |
} | |
name=name.trim(); | |
} | |
catch(Exception e){ | |
System.err.println(e.getMessage()); | |
} | |
} | |
} catch (Exception ex) { | |
Logger.getLogger(LDAPEngine.class.getName()).log(Level.SEVERE, null, ex); | |
} | |
} | |
} | |
try{ | |
dirContext.close(); | |
}catch(Exception e){} | |
return name; | |
} | |
private static String getAttrAsStringComputers(String searchStr,String searchCri,String returnAttr){ | |
String name=null; | |
try { | |
dirContext=new InitialLdapContext(env, null); | |
} catch (NamingException ex) { | |
Logger.getLogger(LDAPEngine.class.getName()).log(Level.SEVERE, null, ex); | |
} | |
if(dirContext!=null){ | |
if(searchStr!=null){ | |
String Filter="(&(objectclass=*)("+searchCri+"="+searchStr+"))"; | |
try { | |
SearchControls ctrl = new SearchControls(); | |
ctrl.setSearchScope(SearchControls.SUBTREE_SCOPE); | |
ctrl.setDerefLinkFlag(false); | |
ctrl.setCountLimit(1); | |
ctrl.setTimeLimit(20000); | |
NamingEnumeration<SearchResult> ans=dirContext.search( | |
System.getProperty("ldap.ou.computer"), Filter,ctrl); | |
while(ans.hasMore()){ | |
SearchResult result=ans.next(); | |
Attributes attributes=result.getAttributes(); | |
try{ | |
if(returnAttr.equalsIgnoreCase(OBJECT_SID)){ | |
byte[] data=(byte[]) attributes.get(returnAttr).get(); | |
name=getSIDAsString(data); | |
}else{ | |
name=attributes.get(returnAttr).get().toString(); | |
} | |
name=name.trim(); | |
} | |
catch(Exception e){ | |
// e.printStackTrace(); | |
} | |
} | |
} catch (NamingException ex) { | |
Logger.getLogger(LDAPEngine.class.getName()).log(Level.SEVERE, null, ex); | |
} | |
} | |
} | |
try{ | |
dirContext.close(); | |
}catch(Exception e){;} | |
return name; | |
} | |
public static String managedByLookupForComputer(final String computerCN) throws LoginException{ | |
LoginContext lc = new LoginContext(SELECTED_LOGIN_CONTEXT); | |
// "login" by executing the LoginModule | |
lc.login(); | |
// get the logged-in Subject from the LoginModule | |
Subject subject = lc.getSubject(); | |
return Subject.doAs(subject, new PrivilegedAction<String>() { | |
@Override | |
public String run() { | |
String ou=getAttrAsStringComputers(computerCN.toUpperCase(), CN, DISTINGUISHED_NAME); | |
ou=ou.substring(ou.indexOf("OU")); | |
return managedByLookupForOU(ou); | |
} | |
}); | |
} | |
private static String managedByLookupForOU(String ouDN){ | |
String managedByString=getAttrAsStringComputers(ouDN, DISTINGUISHED_NAME, MANAGED_BY); | |
if(managedByString!=null){ | |
return managedByString; | |
}else{ | |
if(ouDN.contains("OU")){ | |
if(ouDN.startsWith("OU")){ | |
ouDN=ouDN.substring(ouDN.indexOf("OU", 1)); | |
}else{ | |
ouDN=ouDN.substring(ouDN.indexOf("OU")); | |
} | |
return managedByLookupForOU(ouDN); | |
}else{ | |
return null; | |
} | |
} | |
} | |
public static String getAttributeAsPrivAction(final String uID, final String criteria, final String returnAttr, final boolean isComputer){ | |
try{ | |
LoginContext lc = new LoginContext(SELECTED_LOGIN_CONTEXT); | |
// "login" by executing the LoginModule | |
lc.login(); | |
// get the logged-in Subject from the LoginModule | |
Subject subject = lc.getSubject(); | |
String val=Subject.doAs(subject, new PrivilegedAction<String>() { | |
@Override | |
public String run() { | |
if(!isComputer){ | |
String valString=getAttrAsString(uID, criteria, returnAttr); | |
return valString; | |
}else{ | |
String valString=getAttrAsStringComputers(uID, criteria, returnAttr); | |
return valString; | |
} | |
} | |
}); | |
return val; | |
}catch(Exception e){ | |
e.printStackTrace(); | |
return "NOT FOUND"; | |
} | |
} | |
public static String getSIDAsString(byte[] SID) { | |
// Add the 'S' prefix | |
StringBuilder strSID = new StringBuilder("S-"); | |
// bytes[0] : in the array is the version (must be 1 but might | |
// change in the future) | |
strSID.append(SID[0]).append('-'); | |
// bytes[2..7] : the Authority | |
StringBuilder tmpBuff = new StringBuilder(); | |
for (int t=2; t<=7; t++) { | |
String hexString = Integer.toHexString(SID[t] & 0xFF); | |
tmpBuff.append(hexString); | |
} | |
strSID.append(Long.parseLong(tmpBuff.toString(),16)); | |
// bytes[1] : the sub authorities count | |
int count = SID[1]; | |
// bytes[8..end] : the sub authorities (these are Integers - notice | |
// the endian) | |
for (int i = 0; i < count; i++) { | |
int currSubAuthOffset = i*4; | |
tmpBuff.setLength(0); | |
tmpBuff.append(String.format("%02X%02X%02X%02X", | |
(SID[11 + currSubAuthOffset]& 0xFF), | |
(SID[10 + currSubAuthOffset]& 0xFF), | |
(SID[9 + currSubAuthOffset]& 0xFF), | |
(SID[8 + currSubAuthOffset]& 0xFF))); | |
strSID.append('-').append(Long.parseLong(tmpBuff.toString(), 16)); | |
} | |
// That's it - we have the SID | |
return strSID.toString(); | |
} | |
public static String getNameFromUserName(final String userName){ | |
try{ | |
LoginContext lc = new LoginContext(SELECTED_LOGIN_CONTEXT); | |
// "login" by executing the LoginModule | |
lc.login(); | |
// get the logged-in Subject from the LoginModule | |
Subject subject = lc.getSubject(); | |
String val=Subject.doAs(subject, new PrivilegedAction<String>() { | |
@Override | |
public String run() { | |
return getAttr(userName,"displayName"); | |
} | |
}); | |
return val; | |
}catch(Exception e){ | |
e.printStackTrace(); | |
return "NOT FOUND"; | |
} | |
} | |
public static String getManagedByFromUserName(final String computerName){ | |
try{ | |
LoginContext lc = new LoginContext(SELECTED_LOGIN_CONTEXT); | |
// "login" by executing the LoginModule | |
lc.login(); | |
// get the logged-in Subject from the LoginModule | |
Subject subject = lc.getSubject(); | |
final String ou=Subject.doAs(subject, new PrivilegedAction<String>() { | |
@Override | |
public String run() { | |
String dn= getAttrAsStringComputers(computerName.toUpperCase(), CN,DISTINGUISHED_NAME); | |
dn=dn.substring(dn.indexOf("OU=")); | |
return dn; | |
} | |
}); | |
final String managedBy=Subject.doAs(subject, new PrivilegedAction<String>() { | |
@Override | |
public String run() { | |
return getAttrAsStringComputers(ou, DISTINGUISHED_NAME,MANAGED_BY); | |
} | |
}); | |
String username=Subject.doAs(subject, new PrivilegedAction<String>() { | |
@Override | |
public String run() { | |
return getAttrAsString(managedBy, DISTINGUISHED_NAME, SAM_ACCOUNT_NAME); | |
} | |
}); | |
return username; | |
}catch(Exception e){ | |
e.printStackTrace(); | |
return "NOT FOUND"; | |
} | |
} | |
public static String getSidFromUserName(final String userName) throws LoginException{ | |
if(lc==null){ | |
lc = new LoginContext(LDAPEngine.SELECTED_LOGIN_CONTEXT); | |
lc.login(); | |
} | |
// get the logged-in Subject from the LoginModule | |
Subject subject = lc.getSubject(); | |
String val=Subject.doAs(subject, new PrivilegedAction<String>() { | |
@Override | |
public String run() { | |
return getAttrAsString(userName, SAM_ACCOUNT_NAME, OBJECT_SID); | |
} | |
}); | |
return val; | |
} | |
public static String getEmailFromDN(final String userDN){ | |
if(lc==null){ | |
try{ | |
lc = new LoginContext(LDAPEngine.SELECTED_LOGIN_CONTEXT); | |
// "login" by executing the LoginModule | |
lc.login(); | |
}catch(Exception e){ | |
} | |
} | |
// get the logged-in Subject from the LoginModule | |
Subject subject = lc.getSubject(); | |
String val=Subject.doAs(subject, new PrivilegedAction<String>() { | |
@Override | |
public String run() { | |
return getAttrAsString(userDN, DISTINGUISHED_NAME, SAM_ACCOUNT_NAME); | |
} | |
}); | |
return val; | |
} | |
public void contextDestroyed() { | |
} | |
public void contextInitialized() { | |
logger.info("Starting LDAPEngine initialization"); | |
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); | |
env.put(Context.PROVIDER_URL, System.getProperty("ldap.connection.url")); | |
env.put("java.naming.ldap.attributes.binary", OBJECT_SID); | |
env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI"); | |
System.setProperty("java.security.auth.login.config", LDAPEngine.class.getResource("marauder.conf").getPath()); | |
if(System.getProperty("os.name").toLowerCase().contains("linux")){ | |
if(System.getenv(KRB5CCNAME)!=null){ | |
System.setProperty(KRB5CCNAME, System.getenv(KRB5CCNAME)); | |
}else{ | |
throw new Error("Linux without Kerberos detected! System cannot be initialized without Kerberos"); | |
} | |
SELECTED_LOGIN_CONTEXT = "LinuxSSOContext"; | |
}else{ | |
SELECTED_LOGIN_CONTEXT = "WindowsSSOContext"; | |
} | |
logger.info("Selected Login Context is:"+SELECTED_LOGIN_CONTEXT); | |
try { | |
logger.info("Testing LDAP Util:"+getSidFromUserName(System.getProperty("ldap.test.username"))); | |
} catch (LoginException e) { | |
throw new Error(e.getMessage()); | |
} | |
} | |
public static void main(String[] args) throws LoginException{ | |
Logger logger = Logger.getAnonymousLogger(); | |
String KRB5CCNAME="KRB5CCNAME"; | |
String SELECTED_LOGIN_CONTEXT="SELECTED_LOGIN_CONTEXT"; | |
Hashtable<String, String> env = new Hashtable<String, String>(); | |
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); | |
env.put(Context.PROVIDER_URL, "ldap://your id"); | |
env.put("java.naming.ldap.attributes.binary", "objectSID"); | |
env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI"); | |
System.setProperty("java.security.auth.login.config", "marauder.conf"); | |
System.setProperty("java.security.krb5.realm", "HOME.INFO"); | |
System.setProperty("java.security.krb5.kdc", "your ip"); | |
if(System.getProperty("os.name").toLowerCase().contains("linux")){ | |
SELECTED_LOGIN_CONTEXT = "LinuxSSOContext"; | |
}else{ | |
SELECTED_LOGIN_CONTEXT = "WindowsSSOContext"; | |
} | |
logger.info("Selected Login Context is:"+SELECTED_LOGIN_CONTEXT); | |
LoginContext ctx = new LoginContext(SELECTED_LOGIN_CONTEXT); | |
ctx.login(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
LinuxSSOContext | |
{ | |
com.sun.security.auth.module.Krb5LoginModule required | |
useKeyTab="true" | |
principal="[email protected]" | |
storeKey="true" | |
debug="true" | |
doNotPrompt="true"; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment