zakird / pyad

Python Active Directory Tools | *Not actively maintained*
http://zakird.github.io/pyad/
176 stars 72 forks source link

Problem with space inside cn or dn string #116

Open glaterza6 opened 5 years ago

glaterza6 commented 5 years ago

Hi,

I am trying to figure out how to search an user with space inside cn (or dn).

cn1 = 'user1' dn1 = 'CN=user1,OU=Standard Users,OU=New York,DC=test,DC=com' cn2 = 'user2 ' #notice the trailing space dn2 = 'CN=user2 ,OU=Standard Users,OU=New York,DC=test,DC=com' #space after user2

from pyad import pyad

pyad.from_cn( cn1 ) #works well pyad.from_dn( dn1 ) #works well

But for cn2 or dn2:

pyad.from_cn( cn2 ) pyad.from_dn( dn2 )

I receive the following error:

com_error: (-2147352567, 'Eccezione.', (0, 'Active Directory', 'Oggetto inesistente sul server.\r\n', None, 0, -2147016656), None)

I don't know if I have to escape that space, but I don't understand why I got problem for that space and not for example for the spaces in "Standard Users" or "New York". I tried escaping the space with backslash but maybe I'm not doing that correctly. N.B.: I don't have writing permission on the AD for eliminating that space, I just have read permissions.

Any suggestions on how to perform this search? Thank you in advance.

spyoungtech commented 5 years ago

I believe escaping is required for this case. This Microsoft TechNet article may help. So it seems you want to escape it with a literal backslash.

You can try

cn2 = 'user2\\ '
dn2 = 'CN=user2\\ ,OU=Standard Users,OU=New York,DC=test,DC=com'
pyad.from_cn( cn2 )
pyad.from_dn( dn2 )
glaterza6 commented 5 years ago

Thank you for the answer. I tried that way too but it doesn't work.

I also tried to retrieve the distinguishedname querying the domain itself through pyad. First of all I tried this:

q.execute_query(
   attributes = ['distinguishedname' ],
   where_clause = " cn like 'user2%'",
   base_dn = "dc=test,dc=com"
)

But I have the following error (actually I receive the same error for other users, so maybe I'm missing something about 'like' operator):

(-2147352567, 'Exception occurred.', (0, u'Provider', u'One or more errors occurred during processing of command.', None, 1240640, -2147217900), None)

Then I found out that the samaccountname of user2 has been filled without spaces. So I tried:

q.execute_query(
   attributes = ['distinguishedname' ],
   where_clause = "samaccountname = 'user2'",
   base_dn = "dc=test,dc=com"
)

The answer is like you suggested:

'CN=user2\\ ,OU=Standard Users,OU=New York,DC=test,DC=com'

But, as I said at the beginning of this post, if I use:

pyad.from_dn('CN=user2\\ ,OU=Standard Users,OU=New York,DC=test,DC=com')

The following exception raises again:

com_error: (-2147352567, 'Eccezione.', (0, 'Active Directory', 'Oggetto inesistente sul server.\r\n', None, 0, -2147016656), None)

Then, for trying to better understand what happens, I added print( distinguished_name ) after line 81 in adobject.py and print( ads_path ) after line 155 in pyadutils.py. At this point using again:

pyad.from_dn( 'CN=user2\\ ,OU=Standard Users,OU=New York,DC=test,DC=com' )

before the exception is raised, it prints out:

CN=user2\ ,OU=Standard Users,OU=New York,DC=test,DC=com #print function in adobject.py
LDAP://'CN=user2\5c ,OU=Standard Users,OU=New York,DC=test,DC=com #print function in pyadutils.py

Is it possible that the problem stands in the 5c appended after user2? From the article, 5c is the hex representation of the backslash. How can solve this? I'm stuck here now.

spyoungtech commented 5 years ago

Good find! Looks like it was escaped as a result of the escape_path function in pyadutils.py.

So, the solution might be a more intelligent path escaping function that can handle this case. The tricky part is that it should only escape if a space is in leading or trailing position in a component part of the path. The other thing would be to make sure that the escape backslash is not interpreted like a literal backslash.

Even though the convention seems to use a plain backslash, I wonder if replacing spaces with the ascii hex escape \20 would work... Would need make sure that it doesn't break dn's with enclosed (not leading/trailing) spaces.

glaterza6 commented 5 years ago

Ok maybe I solved. As a workaround, I simply modified the following line inside generate_ads_path function in pyadutils.py:

ads_path = ''.join((ads_path,escape_path(distinguished_name)))

in:

ads_path = ''.join((ads_path,distinguished_name))

Substantially, I didn't let "distinguished_name" pass inside escape_path function.

Everything work simply using this:

user2 = 'CN=user2\ ,OU=Standard Users,OU=New York,DC=test,DC=com'
pyad.from_dn( user2 )

At this point I think escape_path function is wrong; the article from Microsoft TechNet simply said that you have to escape with \ the following set of characters: ,\#+<>;"= and "Leading or trailing spaces" in every field of distinguishedname. But escape_path function doesn't do this.

Thank you very much for the help!

GabrielZabielski commented 4 years ago

It's crazy... Thank you guys for this workaround.

sed -i "s|ads_path,escape_path(distinguished_name)|ads_path,distinguished_name|" .venv\Lib\site-packages\pyad\pyadutils.py