Announcement Announcement Module
Collapse
No announcement yet.
objectSID and LdapTemplate Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • objectSID and LdapTemplate

    Hi,

    I am using LdapTemplate (spring-ldap-core/1.3.0.RELEASE).

    How can I read the value of the attribute "objectSID" using LdapTemplate?
    How can I find a user given his "objectSID" using LdapTemplate?

    Thank you for your help.
    J.-Cl.

  • #2
    The objectSid seems to be a systemOnly attribute. You don't mention any specific problems, so assuming that the attribute is readable, you could probably do something like this:

    Code:
    public class LdapPersonDao implements PersonDao {
       private LdapTemplate ldapTemplate;
       ...
       public String getObjectSidForPerson(DistinguishedName dn) {
          return ldapTemplate.lookup(
             dn, 
             new AbstractContextMapper() {
                public Object doMapFromContext(DirContextOperations ctx) {
                   return ctx.getStringAttribute("objectSid");
                }
             });
       }
    
       public List getPersonsByObjectSid(String sid) {
          AndFilter filter = new AndFilter();
          filter.and(new EqualsFilter("objectclass", "person"));
          filter.and(new EqualsFilter("objectSid", sid));
          return ldapTemplate.search("", filter.encode(), new PersonContextMapper());
       }
    }
    Code:
    public class PersonContextMapper extends AbstractContextMapper {
       public Object doMapFromContext(DirContextOperations ctx) {
          Person p = new Person();
          p.setFullName(context.getStringAttribute("cn"));
          p.setLastName(context.getStringAttribute("sn"));
          p.setDescription(context.getStringAttribute("description"));
          p.setDn(context.getDn());
          return p;
       }
    }

    Comment


    • #3
      objectSid - Format of the String

      @ulsa
      The objectSid seems to be a systemOnly attribute. You don't mention any specific problems, so assuming that the attribute is readable...
      Thank you for your code snippet. It will help me.

      I still have the following question:
      The value of the attribute 'objectSid' is a binary object. The parameter of '
      Code:
      public List getPersonsByObjectSid(String sid)
      ' is a String.

      How does this String look like? Is it Base64 Encoded?

      Thank you for your advise.

      Comment


      • #4
        Hi,
        I found this link: http://www.pcreview.co.uk/forums/thread-1458615.php

        So I can implement the search.

        The question now is: How can I convert a String like S-1-5-21-2562418665-3218585558-1813906818-1576 to its binary representation (in this case: {01,05,00,00,00,00,00,05,15,00,00,00,e9,67,bb,98,d 6,b7,d7,bf,82,05,1e,6c,28,06,00,00})

        Comment


        • #5
          I'm considering adding Java 1.4-compatible SID converters to LdapUtils. Meanwhile, try this code. You'll need the ArrayUtils in Commons Lang.

          Code:
          	/**
          	 * Converts a String SID to its binary representation, according to the
          	 * algorithm described <a
          	 * href="http://blogs.msdn.com/oldnewthing/archive/2004/03/15/89753.aspx"
          	 * >here</a>. 
          	 * 
          	 * @param sid SID in readable format
          	 * @return Binary version of the given sid
          	 * @see LdapUtils#convertBinarySidToString(byte[])
          	 * @since 1.3.1
          	 */
          	public static byte[] convertStringSidToBinary(String string) {
          		String[] parts = string.split("-");
          		byte sidRevision = (byte) Integer.parseInt(parts[1]);
          		int subAuthCount = parts.length - 3;
          		
          		byte[] sid = new byte[] {sidRevision, (byte) subAuthCount};
          		sid = ArrayUtils.addAll(sid, numberToBytes(parts[2], 6, true));
          		for (int i = 0; i < subAuthCount; i++) {
          			sid = ArrayUtils.addAll(sid, numberToBytes(parts[3 + i], 4, false));
          		}
          		return sid;
          	}
          
          	/**
          	 * Converts the given number to a binary representation of the specified
          	 * length and "endian-ness".
          	 * 
          	 * @param number String with number to convert
          	 * @param length How long the resulting binary array should be
          	 * @param bigEndian <code>true</code> if big endian (5=0005), or
          	 * <code>false</code> if little endian (5=5000)
          	 * @return byte array containing the binary result in the given order
          	 */
          	static byte[] numberToBytes(String number, int length, boolean bigEndian) {
          		BigInteger bi = new BigInteger(number);
          		byte[] bytes = bi.toByteArray();
          		int remaining = length - bytes.length;
          		if (remaining < 0) {
          			bytes = ArrayUtils.subarray(bytes, -remaining, bytes.length);
          		} else {
          			byte[] fill = new byte[remaining];
          			bytes = ArrayUtils.addAll(fill, bytes);
          		}
          		if (!bigEndian) {
          			ArrayUtils.reverse(bytes);
          		}
          		return bytes;
          	}
          Unit tests for the interested:

          Code:
          	/**
          	 * Hand-crafted SID.
          	 */
          	public void testConvertHandCraftedStringSidToBinary() throws Exception {
          		byte[] expectedSid = { (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x00,
          				(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x05,
          				(byte) 0x15, (byte) 0x00, (byte) 0x00, (byte) 0x00,
          				(byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
          				(byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00,
          				(byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00,
          				(byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
          		byte[] result = LdapUtils.convertStringSidToBinary("S-1-5-21-1-2-3-4");
          		assertTrue("incorrect length of array", ArrayUtils.isSameLength(expectedSid, result));
          		for (int i = 0; i < result.length; i++) {
          			assertEquals("i=" + i + ",", expectedSid[i], result[i]);
          		}
          	}
          
          	/**
          	 * Example SID from "http://www.pcreview.co.uk/forums/thread-1458615.php".
          	 */
          	public void testConvertStringSidToBinary() throws Exception {
          		byte[] expectedSid = { (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x00,
          				(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x05,
          				(byte) 0x15, (byte) 0x00, (byte) 0x00, (byte) 0x00,
          				(byte) 0xe9, (byte) 0x67, (byte) 0xbb, (byte) 0x98,
          				(byte) 0xd6, (byte) 0xb7, (byte) 0xd7, (byte) 0xbf,
          				(byte) 0x82, (byte) 0x05, (byte) 0x1e, (byte) 0x6c,
          				(byte) 0x28, (byte) 0x06, (byte) 0x00, (byte) 0x00 };
          		byte[] result = LdapUtils.convertStringSidToBinary("S-1-5-21-2562418665-3218585558-1813906818-1576");
          		assertTrue("incorrect length of array", ArrayUtils.isSameLength(expectedSid, result));
          		for (int i = 0; i < result.length; i++) {
          			assertEquals("i=" + i + ",", expectedSid[i], result[i]);
          		}
          	}

          Comment


          • #6
            Added two methods to LdapUtils:

            Code:
            	public static String convertBinarySidToString(byte[] sid);
            	public static byte[] convertStringSidToBinary(String string);
            It's available in trunk. I would appreciate if anyone else could verify them.

            Comment


            • #7
              Originally posted by ulsa View Post
              Added two methods to LdapUtils:

              Code:
              	public static String convertBinarySidToString(byte[] sid);
              	public static byte[] convertStringSidToBinary(String string);
              It's available in trunk. I would appreciate if anyone else could verify them.
              Thank you very much.

              Yet another Problem:
              How can I use the result of convertStringSidToBinary(String string) with an EqualsFilter?
              The EqualsFilter calls in its constructor hierarchy LdapEncoder.filterEncode what replaces all \\ by \\5c

              Comment


              • #8
                Could you try specifying a single backslash and two digits, like "\15" instead of "\\15"?

                Comment


                • #9
                  Originally posted by ulsa View Post
                  Could you try specifying a single backslash and two digits, like "\15" instead of "\\15"?
                  We have to use '\\' to obtain a single backslash (escape rules under Java). Therefore your solution can not be used. May be the implementation of a Binary Filter would be useful in this case (sorry for not helping you more than this but I'm short of time).

                  Comment


                  • #10
                    We have to use '\\' to obtain a single backslash (escape rules under Java).
                    The Java language specification (section 3.10.6) says an octal escape sequence inside a literal String shall consist of a backslash followed by: OctalDigit | OctalDigit OctalDigit | ZeroToThree OctalDigit OctalDigit. It's perfectly valid to write a Java string like this:

                    Code:
                    String binary = "\15\15\15\11";

                    Comment


                    • #11
                      Let's say we want to write a filter for the following SID, here represented as a byte[]:

                      Code:
                      0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00,
                      0xe9, 0x67, 0xbb, 0x98, 0xd6, 0xb7, 0xd7, 0xbf, 0x82, 0x05, 0x1e, 0x6c,
                      0x28, 0x06, 0x00, 0x00
                      A Java String representation using Unicode escaping is:

                      Code:
                      "\u0001\u0005\u0000\u0000\u0000\u0000\u0000\u0005\u0015\u0000\u0000\u0000\u00e9\u0067\u00bb\u0098\u00d6\u00b7\u00d7\u00bf\u0082\u0005\u001e\u006c\u0028\u0006\u0000\u0000"
                      If you run this through LdapEncoder.filterEncode, you'll get:

                      Code:
                      \00\00\00\00\00\00\00\00gַ׿l\28\00\00
                      Now, ignoring the fact that this is pretty much unreadable, the question is: does this represent the binary data in the server? Do you get a hit when you search using a filter like:

                      Code:
                      "(objectSID=\u0001\u0005\u0000\u0000\u0000\u0000\u0000\u0005\u0015\u0000\u0000\u0000\u00e9\u0067\u00bb\u0098\u00d6\u00b7\u00d7\u00bf\u0082\u0005\u001e\u006c\u0028\u0006\u0000\u0000)"
                      If the answer is no, we need to find out where in the escaping chain the data gets corrupted.

                      If the answer is yes, then we might benefit---mainly from a readability standpoint---from escaping more control characters than just NUL. Here is the same output where I've escaped all characters up to 0x1f:

                      Code:
                      \01\05\00\00\00\00\00\05\15\00\00\00gַ׿\05\1el\28\06\00\00
                      It's slightly more readable, but still impossible to correlate to the original byte[]. So we probably need:
                      1. a Filter that takes a byte[] and constructs a Unicode-escaped String
                      2. a way of printing a Filter containing binary data in a readable way

                      Comment


                      • #12
                        convertBinarySidToString problems

                        I am trying to use Spring LDAP and specifically convertBinarySidToString to get objectSids in Strings (and then parse out the last bit to get the primary group ID).

                        However, I am getting really strange results. I am getting the same objectSID string for many groups, and it's not even the right SID. Also, some groups ARE getting the right objectSID.

                        Here's my code:


                        Code:
                        public class LdapGroup {
                        	private String[] members;
                        	private String dn;
                        	
                        	public void setMembers(String[] members) {
                        		this.members = members;
                        	}
                        	public String[] getMembers() {
                        		return this.members;
                        	}
                        	public void setDn(String dn) {
                        		this.dn = dn;
                        	}
                        	public String getDn () {
                        		return this.dn;
                        	}
                        }
                        
                        //now in a different part of the code
                        
                        ldapGroups = new HashMap<String,LdapGroup>();
                        List groups = ldapTemplate.search("", "(objectclass=group)", new GroupContextMapper());
                        Iterator itr = groups.iterator();
                        while (itr.hasNext()) {
                        	LdapGroup grp = (LdapGroup) itr.next();
                                ldapGroups.put(grp.getDn(), grp);
                        }
                        
                        
                        //the custom ContextMapper that assigns the results to the LdapGroup object
                        
                        private static class GroupContextMapper implements ContextMapper {
                            public Object mapFromContext(Object ctx) {
                               DirContextAdapter context = (DirContextAdapter)ctx;
                               LdapGroup g = new LdapGroup();
                               Attributes a = context.getAttributes();
                        
                               //byte[] binSid = null;
                                                
                               String binSid = null;
                               String sid = null;
                               String dn = null;
                        
                               try {
                        	  binSid = (String) a.get("objectSid").get();	//I was previously trying to cast this to (byte[]) but it was complaining about doing the cast...			
                                  sid = LdapUtils.convertBinarySidToString(binSid.getBytes());
                               } catch (NamingException e) {
                          	// TODO Auto-generated catch block
                         	  e.printStackTrace();
                               }
                        			
                               g.setMembers(context.getStringAttributes("member"));
                        		    
                               g.setDn(context.getStringAttribute("distinguishedName"));			
                               System.out.println("GROUP: " + g.getDn() + ", Sid: " + sid);
                            
                               return g;
                           }
                        }
                        Then I get results like this:
                        Code:
                        GROUP: CN=HelpServicesGroup,CN=Users,DC=domain,DC=com, Sid: S-1-5-21-834519023-4022190063-37797311-3183472488
                        GROUP: CN=TelnetClients,CN=Users,DC=domain,DC=com, Sid: S-1-5-21-834519023-4022190063-37797311-3183472488
                        GROUP: CN=Administrators,CN=Builtin,DC=domain,DC=com, Sid: S-1-5-32-544
                        GROUP: CN=Users,CN=Builtin,DC=domain,DC=com, Sid: S-1-5-32-545
                        GROUP: CN=Guests,CN=Builtin,DC=domain,DC=com, Sid: S-1-5-32-546
                        GROUP: CN=Print Operators,CN=Builtin,DC=domain,DC=com, Sid: S-1-5-32-550
                        GROUP: CN=Backup Operators,CN=Builtin,DC=domain,DC=com, Sid: S-1-5-32-551
                        GROUP: CN=Replicator,CN=Builtin,DC=domain,DC=com, Sid: S-1-5-32-552
                        GROUP: CN=Remote Desktop Users,CN=Builtin,DC=domain,DC=com, Sid: S-1-5-32-555
                        GROUP: CN=Network Configuration Operators,CN=Builtin,DC=domain,DC=com, Sid: S-1-5-32-556
                        GROUP: CN=Performance Monitor Users,CN=Builtin,DC=domain,DC=com, Sid: S-1-5-32-558
                        GROUP: CN=Performance Log Users,CN=Builtin,DC=domain,DC=com, Sid: S-1-5-32-559
                        GROUP: CN=Distributed COM Users,CN=Builtin,DC=domain,DC=com, Sid: S-1-5-32-562
                        GROUP: CN=Domain Computers,CN=Users,DC=domain,DC=com, Sid: S-1-5-21-834519023-4022190063-37797311-3183472488
                        GROUP: CN=Domain Controllers,CN=Users,DC=domain,DC=com, Sid: S-1-5-21-834519023-4022190063-37797311-3183472488
                        GROUP: CN=Schema Admins,CN=Users,DC=domain,DC=com, Sid: S-1-5-21-834519023-4022190063-37797311-3183472488
                        GROUP: CN=Enterprise Admins,CN=Users,DC=domain,DC=com, Sid: S-1-5-21-834519023-4022190063-37797311-3183472488
                        GROUP: CN=Cert Publishers,CN=Users,DC=domain,DC=com, Sid: S-1-5-21-834519023-4022190063-37797311-3183472488
                        For some reason it looks like the builtin groups are working correctly and the domain groups are not. Note that the objectSID is identical for all the domain groups. Also, I compared this to results viewed from using Microsoft's LDP tool and these domain group results are just wrong. I can't see why the code above would come up with the SAME WRONG objectSid for every domain group but different (and correct) objectSids for the builtin groups.

                        Thoughts?

                        Comment


                        • #13
                          RE: objectSid and ldapTemplate

                          OK, I think I figured this out. I discovered this post:
                          http://forum.springsource.org/showthread.php?t=55874

                          about binary attributes in general. The advice there made me think about adding the property mentioned into my Bean config file and it worked. This is the contextSource bean I defined (with the baseEnvironmentProperties property being what I just added to make this work):
                          Code:
                          <bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
                                  <property name="url" value="ldap://ldapserver.domain.com:389" />
                                  <property name="base" value="dc=domain,dc=com" />
                                  <property name="userDn" value="cn=binduser,cn=Users,dc=domain,dc=com" />
                                  <property name="password" value="bindpwd"/>
                                  <property name="baseEnvironmentProperties">
                                      <map>
                                      <entry key="java.naming.ldap.attributes.binary">
                                          <value>objectSid</value>
                                      </entry>
                                      </map>
                                  </property>
                              </bean>
                          Now it appears as though all the SIDs are coming back right. I still need to do a thorough check, though.

                          Comment

                          Working...
                          X