Skip to content

Instantly share code, notes, and snippets.

@ambud
Created April 7, 2016 07:04
Show Gist options
  • Save ambud/220d676e804b00891b012a7bd8bbd630 to your computer and use it in GitHub Desktop.
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
/** 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();
}
}
LinuxSSOContext
{
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab="true"
principal="ambud@home.info"
storeKey="true"
debug="true"
doNotPrompt="true";
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment