Search code examples
javaldapjndi

Retrieving sidHistory from LDAP with Java


I can retrieve objectSID and many other attributes without error, but not sidHistory (I need sidHistory to see which account in domain A corresponds to an account in domain B).

Here's the code that works for most attributes, including objectSID:

void dumpCSV(Attributes attrs, String[] displayList, Logger lg)  {
    // Assume we're only dealing with single valued attributes (for now)
    StringBuilder sb = new StringBuilder();
    for (String attName : displayList)  {
        String name = attName.trim().toLowerCase();
        Attribute att = attrs.get(name);
        if (sb.length() > 0)
            sb.append(",");
        if (att != null)  {
            String v = "?";
            try  {
                if ((name.equals("objectsid")) || (name.equals("sidhistory")))
                    v = binString(att);
                else  {
                    v = (String) att.get();
                    if (name.equals("pwdlastset") || name.equals("lastlogontimestamp") || name.equals("lastlogon") || name.equals("accountexpires"))
                        v = TickConverter.tickDate(v);
                }
                sb.append(Logger.tidyString(v));
            } catch (NamingException e)  {
                System.err.println("NamingException, " + e);
                return;
            }
        }
    }
        lg.logln(sb.toString());
    }
}

static String binString(Attribute att)  {
    try  {
        byte bin[] = (byte[]) att.get();
        return decodeSID(bin);
    } catch (NamingException e)  {
        System.err.println("NamingException, " + e);
        return "?";
    }
}

// taken from http://www.adamretter.org.uk/blog/entries/LDAPTest.java, in turn borrowed from Oracle docs
public static String decodeSID(byte[] sid) {
    final StringBuilder strSid = new StringBuilder("S-");

    // get version
    final int revision = sid[0];
    strSid.append(Integer.toString(revision));

    //next byte is the count of sub-authorities
    final int countSubAuths = sid[1] & 0xFF;

    //get the authority
    long authority = 0;
    //String rid = "";
    for(int i = 2; i <= 7; i++) {
       authority |= ((long)sid[i]) << (8 * (5 - (i - 2)));
    }
    strSid.append("-");
    strSid.append(Long.toHexString(authority));

    //iterate all the sub-auths
    int offset = 8;
    int size = 4; //4 bytes for each sub auth
    for(int j = 0; j < countSubAuths; j++) {
        long subAuthority = 0;
        for(int k = 0; k < size; k++) {
            subAuthority |= (long)(sid[offset + k] & 0xFF) << (8 * k);
        }

        strSid.append("-");
        strSid.append(subAuthority);

        offset += size;
    }

    return strSid.toString();    
}

If I try to retrieve sidHistory using this, tyhe value I get is "?".

Even if I use a namingEnumeration, as I think I probably should, I get "Exception in thread "AWT-EventQueue-0" java.util.NoSuchElementException: Vector Enumeration", probably because I am trying to save it to the wrong typoe (and I've tried a few different types).

snippet is :

String v;
NamingEnumeration nenum = att.getAll();
while (nenum.hasMore())  {
    v = "";
    if (name.equals("objectsid"))  {
        v = binString(att);
        nenum.next();
    } else if (name.equals("sidhistory"))  {
         nenum.next();
         String[] vv = ((String[]) nenum.next());
         v = vv[0];
    } else
    v = (String) nenum.next();
    if (name.equals("pwdlastset") || name.equals("lastlogontimestamp") || name.equals("lastlogon") || name.equals("accountexpires"))
        v = TickConverter.tickDate(v);
    lg.logln(name + "=" + Logger.tidyString(v));
}

Solution

  • We used some code similar to: We note we saw it at http://tomcatspnegoad.sourceforge.net/xref/net/sf/michaelo/tomcat/realm/ActiveDirectoryRealm.html#L566

    ...
    Attribute sidHistory = roleAttributes.get("sIDHistory;binary");
    List<String> sidHistoryStrings = new LinkedList<String>();
    if (sidHistory != null) 
    {
      NamingEnumeration<?> sidHistoryEnum = sidHistory.getAll();
      while (sidHistoryEnum.hasMore()) 
      {
        byte[] sidHistoryBytes = (byte[]) sidHistoryEnum.next();
        sidHistoryStrings.add(new Sid(sidHistoryBytes).toString());
      }
    ...
    }
    

    sidHistory is multi-valued and binary (octetString) is what cause most people headaches.

    Hope this helps.