Troubleshooting an External LDAP Application (SAP GRC)

0 Likes

I was working with a client that has SAP GRC (Governance, Risk, and Compliance) module implemented and they were having trouble getting it to use the eDirectory tree we set up for authentication. Of course, when in doubt, blame the easier to contact people, so I had to figure out what was wrong.



Ever tried working with SAP support? Let's leave it at that.



In this case, SAP GRC can use multiple authentication sources, if it wants too, There are a number of configuration options for things like object class for users, user name, manager attribute, and what the call a UniqueLDAPKey.



The definition of a UniqueLDAPKey is not completely clear, but the documentation shows examples for various LDAP directories like eDirectory, SunOne, Tivoli and Active Directory. (Amusingly Oracle's OID is NOT listed).



From the examples you can infer that they mean to use uniqueLDAPKey as the attribute that takes the value of the manager attribute to then look up the managers information like name and email. This way it can show the users information and the managers information on the same page.



The problem is that for Active Directory and SunOne they suggest entryDN, and for Tivoli and eDirectory they recommend uid. Now following this logic, entryDN is a way of searching for a user by DN, in a filter, as opposed to by an entry level search for the DN.



That makes sense for SunOne and Active Directory. I do not know Tivoli well enough to comment, but for eDirectory, uid makes no sense whatsoever. After all, manager stores the full distinguished name of the manager object. Well technically it returns the full name. It actually stores an object ID in the directory itself.



However, because this was not working, the SAP group asked me to populate the equivalent of the uid (uniqeID in eDirectory name space) of a users manager, on each user object.



This is of course not optimal, as in theory even SAP GRC does not claim to need this. It ignores the power of a referential attribute, and wastes space in the database. However, it was a simple enough thing to add. Then of course they wanted the managers first name, last name and email, which takes it to another level. (Of what, I shall not say).



Doing this task was fairly easy, using a toolkit rule, like in this series:










I might write the rule I generated up, since it is potentially useful, and if nothing else, an interesting use of the above approach.



Foolishly I did not try to test before doing, and took their word for what the issue really was. Once we started testing with the new data on the users, the real issues became clear. I suppose the lesson is trust but verify. Sometimes a different set of eyes can see a different solution.



One really nice feature to consider when using eDirectory as an LDAP server is that the trace facility is very useful for diagnosing this kind of problem. If you can get to Dstrace, in any of its many incarnations, (http://www.novell.com/communities/node/4418/the-many-faces-dstrace ) you can enable the LDAP flag to show LDAP events in the trace. You might have to go in with iManager to the LDAP Server object and on the trace tab enable more stuff to show up to get the same results as I show below, but that is a one time change, per server.



When you first select a user in SAP GRC's interface, you see a query something like:




10:19:01 B68BEBA0 LDAP: (10.1.1.42:55133)(0x0002:0x63) Search request:
base: "ou=people,o=acme,dc=com"
scope:2 dereference:3 sizelimit:1 timelimit:0 attrsonly:0
filter: "(&(objectClass=inetorgperson)(acme7DigitName=gxc1234))"
no attributes
10:19:01 B68BEBA0 LDAP: (10.1.1.42:55133)(0x0002:0x63) Empty attribute list implies all user attributes
10:19:01 B68BEBA0 LDAP: (10.1.1.42:55133)(0x0002:0x63) Sending search result entry "cn=gxc1234,ou=UNK,ou=People,o=acme,dc=com" to connection 0xa07e6c0



You will note that the scope=2 which means Subtree search, and this returns the users full DN of:

cn=gxc1234,ou=UNK,ou=People,o=acme,dc=com



(The Sending search result entry, with a value, means success!)



Thus the standard LDAP contextless approach worked. Use the base DN you are given (the people.acme.com) and search for a unique string, in a subtree search.



We have a manager attribute available and it is in DN format.

(LDAP would see it as: cn=abc1234,ou=UNK,ou=People,o=acme,dc=com)



We also have a acmeManagerUserID that stores the uid or CN of the manager user as a string. Thus for our example it would simply hold the value abc1234. Here we tried using the uid style approach with the documentation defined approach for eDirectory though using the CN attribute, since uniqueID (uid in LDAP parlance) holds a different value in this tree.




10:19:01 B61B7BA0 LDAP: (10.1.1.42:41823)(0x0002:0x63) Search request:
base: "ou=people,o=acme,dc=com"
scope:1 dereference:3 sizelimit:0 timelimit:0 attrsonly:0
filter: "(&(cn=abc1234))"
attribute: "inetorgperson"
attribute: "acmeLocationName"
attribute: "givenName"
attribute: "sn"
attribute: "acmeManagerEmail"
attribute: "ou"
attribute: "acme7DigitName"
attribute: "mail"
attribute: "acmeManagerUserID"
attribute: "uid"
10:19:01 B61B7BA0 LDAP: (10.1.1.42:41823)(0x0002:0x63) Sending operation result 0:"":"" to connection 0xa07e480



(The Sending operation result 0:"":"" means failure, or more correctly that no results were found)



This was so close! It almost worked! The ALMOST issue is two part, because the search is only scope=1 which is this container (the base: of ou=people,o=acme,dc=com) and needs to really be a subtree search. This is a structured, not flat LDAP tree, and the default search should pretty much be subtree all the time. If it is a flat tree, there is no penalty, since it is the same as a flat tree base search. In a structured tree, it would actually work then.




We also tried using entryDN as UniqueLDAPKey, which also ALMOST works...




09:58:19 B60B6BA0 LDAP: (10.1.1.42:41118)(0x0002:0x63) Search request:
base: "ou=people,o=acme,dc=com"
scope:1 dereference:3 sizelimit:0 timelimit:0 attrsonly:0
filter: "(&(entrydn=cn=abc1234,ou=UNK,ou=People,o=acme,dc=com))"
attribute: "inetorgperson"
attribute: "acmeLocationName"
attribute: "givenName"
attribute: "sn"
attribute: "acmeManagerEmail"
attribute: "ou"
attribute: "acme7DigitName"
attribute: "mail"
attribute: "manager"
attribute: "entrydn"
09:58:19 B60B6BA0 LDAP: (10.1.1.42:41118)(0x0002:0x63) Sending operation result 0:"":"" to connection 0xa07e000



Here it took the manager attribute, and looked for entrydn=managerATTrValue and that too was so close. But again scope=1 so this just won't work. I double checked that with an LDAP Browser, and pasted the filter string I saw in Dstrace into my LDAP browser, and queried with a base query for entryDN and it failed (and trace showed a scope of 1) and then with a subtree version, and it worked, and trace showed a scope of 2.



Interestingly there is an additional query that tries to find the filter of just the manager users full DN, but with all the fully qualified bits stripped out which is basically a nonsense query. I am not aware of a single LDAP implementation, in which that would return a value.




09:43:23 B60B6BA0 LDAP: (10.1.1.42:60995)(0x0002:0x63) Search request:
base: "dc=com"
scope:1 dereference:3 sizelimit:0 timelimit:0 attrsonly:0
filter: "(cn=abc1234,UNK,People,acme)"
no attributes



Now, to make this one work, it should instead use the DN of the manager, as the base: value, and then even with a scope: 1, that would actually work.



They strip off the ou=, o=, and dc= which is bizarre. I know we do not store this exact data string in our LDAP, so it has to have been calculated by GRC's LDAP plugin.



In this manner, it makes even LESS sense, since this is even less compliant, that what I originally read it as:

filter: "(cn=abc1234,ou=UNK,ou=People,o=acme,dc=com)"



A query from GRC in the format of:




09:43:23 B60B6BA0 LDAP: (10.1.1.42:60995)(0x0002:0x63) Search request:
base: "cn=abc1234,ou=UNK,ou=People,o=acme,dc=com"
scope:1 dereference:3 sizelimit:0 timelimit:0 attrsonly:0
filter:
no attributes



would actually work with scope of 1, since it is effectively an entry query and needs no scoping. But of course that is NOT what it does.



We passed this issue on to SAP support, and finally after being escalated to someone who knew this aspect of the product, they quickly told us, Oh yes, you are running SP5? This is fixed in SP8. (We initially were told it was fixed in SP1). That took about two weeks to get an answer from SAP. Another two weeks to actually do the SP10 upgrade. If you thought Netware, SLES, or even Windows SP's were hard to get to do, even in a development lab, you ain't seen nothing, when it comes to SAP service packs.



I got called back to look at it, now that the SP10 patch was applied.



After the update to SAP GRC SPS 10, based on advice from SAP support, the search results look like this.



First the search for the user continues to work, as it did before, though we just set base DN to dc=com, since no one actually resides a the people,acme.com level, it would never matter having that as a base.



We set uniqueLDAPKey to entryDN, and Manager attribute to manager and got this trace back.




08:33:17 8BA75BA0 LDAP: (10.1.1.42:36377)(0x0003:0x63) DoSearch on connection 0xa176480
08:33:17 8BA75BA0 LDAP: (10.1.1.42:36377)(0x0003:0x63) Search request:
base: "dc=com"
scope:1 dereference:3 sizelimit:0 timelimit:0 attrsonly:0
filter: "(&(entryDN=cn=abc1234,ou=UNK,ou=People,o=acme,dc=com))"
attribute: "inetorgperson"
attribute: "acmeLocationName"
attribute: "givenName"
attribute: "sn"
attribute: "acmeManagerEmail"
attribute: "entryDN"
attribute: "ou"
attribute: "acme7DigitName"
attribute: "mail"
attribute: "manager"
attribute: "entryDN"
08:33:17 8BA75BA0 LDAP: (10.1.1.42:36377)(0x0003:0x63) Sending operation result 0:"":"" to connection 0xa176480
08:33:17 B72C8BA0 LDAP: (10.1.1.42:35600)(0x0002:0x63) DoSearch on connection 0xa096900
08:33:17 B72C8BA0 LDAP: (10.1.1.42:35600)(0x0002:0x63) Search request:
base: "ou=UNK,ou=People,o=acme,dc=com"
scope:1 dereference:3 sizelimit:0 timelimit:0 attrsonly:0
filter: "(cn=abc1234)"
no attributes
08:33:17 B72C8BA0 LDAP: (10.1.1.42:35600)(0x0002:0x63) Empty attribute list implies all user attributes
08:33:17 B72C8BA0 LDAP: (10.1.1.42:35600)(0x0002:0x63) Sending search result entry "cn=abc1234,ou=UNK,ou=People,o=acme,dc=com" to connection 0xa096900



The first part of the search, to find the user continues to work as before and show no new information of interest. Thus all that is shown is the next queries for manager information.



Let's take this apart trace sample apart.




base: "dc=com"
scope:1 dereference:3 sizelimit:0 timelimit:0 attrsonly:0
filter: "(&(entryDN=cn=abc1234,ou=UNK,ou=People,o=acme,dc=com))"
(List of attributes redacted)



We see a search from the search base of dc=com, with a scope of 1 again (which means direct descendants of this container (dc=com) which will never work, as nothing is directly below dc=com, the users exist in a regional tree structure under ou=People,o=acme,dc=com and no users are at the root of that container.)



Now the filter of (&(entryDN=cn=abc1234,ou=UNK,ou=People,o=acme,dc=com)) shows us that the system found the manager attribute, which is a full DN value, read it, and reused it correctly, and would have worked if they had just set scope to 2 to do a subtree search (as demonstrated with a simple LDAP browser in our first pass through this issue).



We see that the search fails in this line:



08:33:17 8BA75BA0 LDAP: (10.1.1.42:36377)(0x0003:0x63) Sending operation result 0:"":"" to connection 0xa176480



Sending an empty result means no result was found.



Now finally the SAP GRC LDAP side does something fairly smart. It then tries a better query:




08:33:17 B72C8BA0 LDAP: (10.1.1.42:35600)(0x0002:0x63) Search request:
base: "ou=UNK,ou=People,o=acme,dc=com"
scope:1 dereference:3 sizelimit:0 timelimit:0 attrsonly:0
filter: "(cn=abc1234)"
no attributes



This time it sets the base for its search to ou=UNK,ou=People,o=acme,dc=com which it parsed out of the DN of the manager which was:

cn=abc1234,ou=UNK,ou=People,o=acme,dc=com



And then searches for the cn=abc1234.



This will work, with scope of 1, since the base DN does actually contain the abc1234 object, so it queries correctly.



Now in the first query, we saw a specified attribute list of:




attribute: "inetorgperson"
attribute: "acmeLocationName"
attribute: "givenName"
attribute: "sn"
attribute: "acmeManagerEmail"
attribute: "entryDN"
attribute: "ou"
attribute: "acme7DigitName"
attribute: "mail"
attribute: "manager"
attribute: "entryDN"



But that query failed, and on this second query that does seem to succeed, as we see in the results in the trace:




08:33:17 B72C8BA0 LDAP: (10.1.1.42:35600)(0x0002:0x63) Empty attribute list implies all user attributes
08:33:17 B72C8BA0 LDAP: (10.1.1.42:35600)(0x0002:0x63) Sending search result entry "cn=abc1234,ou=UNK,ou=People,o=acme,dc=com" to connection 0xa096900



It sends the search results of the correct user back to SAP GRC. However, the previous line, of "Empty attribute list implies all user attributes" means that instead of just asking for 11 attributes of whom 2 are errors. (inetorgperson is the value you would find in an objectClass attribute, which means it is never the NAME of an attribute. The second is that it asks for entryDN twice, which is odd as well) it gets all attributes of the object. Now in the tree we are using, which is an authentication tree, this is not terrible, as the user has only the minimal set of attributes, but it is very inefficient from a design perspective. Regardless, it returns the requested attributes that we saw in the first query attempt (that exist, and are not really duplicated) so the data should be returned.



Unfortunately we cannot get the LDAP trace to show us the returned data with our current tools. But that usually makes sense as it would bring the server to its knees trying to log to a file all the many thousands of requests a second or minute it might get.



At this point, SAP have not really fixed the root problem (Scope of 1 instead of 2 on the first query) but they have a second back up query that is inefficient but looks to be working. However, it is not returning the data into the interface.



At this point it is hard to know the internal workings of SAP to see what it is doing with the returned data. If there is logging available from the SAP side, we could try and make further progress.



While I do not have a solution yet to the problem, it does now look to be related to an SAP side issue. I would have preferred to have solved this, and post a complete solution, but I think the process of walking through what an LDAP client is trying to ask eDirectory can be helpful.



I have looked through several LDAP consuming applications, to see what they are looking for, and I am often amazed at how strange and odd the queries they choose to make appear.



What is really nice about eDirectory is the ease with which you can watch at least one side of the conversation and get a much better insight into what is going on.



When I find out more details from the SAP folk, I will update the article with a possible resolution.



Labels:

How To-Best Practice
Comment List
Related
Recommended