Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

System.Runtime.InteropServices.COMException (0x80040E37): Unknown

36 views
Skip to first unread message

Tim Reynolds

unread,
Oct 11, 2004, 2:39:03 PM10/11/04
to
I am trying to access an LDAP server and am receiving the above error.

Background:
This is my first time trying to acces an LDAP server via C# VS.NET code.
The LDAP server is actually an LDAP service running on a IBM Z/OS Mainframe.
We have verified it using bach OMVS commands. However, when trying to code
to access this, the above exception is encountered. The code is simple:

DirectoryEntry de = new DirectoryEntry("ldap://serverDNS:9503", "racfid=" +
"V121119", "sept04", AuthenticationTypes.Encryption);
Object o = de.NativeObject; //this will force the bind

Ofcourse, serverDNS is substituted for the real DNS value.

The exception is generated on this last statement.

The LDAP was set up on port 9503 and that is why I am using :9503. I am
using 'ldap' in lower case b/c when I try upper case, I receive an error that
the
server is not operational. Since this LDAP is actually being used to
validate racf id's & pwd's on the mainframe, I am using racfid=....

This piece of code running on the MF server itself was used to validate the
same ID and it worked fine:
OSHELL ldapsearch -h serverDNS -p 9503 +
-D racfid=v121119,profiletype=user,sysplex=badqplex +
-w sept04 +
-b "racfid=v121119,profiletype=user,sysplex=badqplex" +
"objectclass=*" ;

Ofcourse serverDNS is again substituted from the real DNS.

Can some one help me figure out the correct values to put in the
DirectoryEntry constructor?

Thanks,

Tim


Joe Kaplan (MVP - ADSI)

unread,
Oct 11, 2004, 5:22:51 PM10/11/04
to
AuthenticationTypes.Encrypt only works with Windows authentication. I'd
start by trying AuthenticationTypes.None and see if you can get that to
work. If you can and still need some transport level security, then SSL is
the only option for non-AD directories. Your server would need an SSL
certificate to support this though.

Also, make sure that your user id syntax is correct for the directory you
are targeting.

Joe K.

"Tim Reynolds" <TimRe...@discussions.microsoft.com> wrote in message
news:1D835652-D7C6-493F...@microsoft.com...

Jason Tyler

unread,
Oct 11, 2004, 5:41:01 PM10/11/04
to
You may want to try AuthenticationTypes.FastBind since it is a third-party
server.

Thanks.

Jason Tyler
Microsoft AD/ADSI Developer Support

Tim Reynolds

unread,
Oct 12, 2004, 8:53:02 AM10/12/04
to
Thanks for the info. I did try setting the AuthenticationTypes.None and
received same result (also tried fastbind per other post and it did not work
either).
There are value in the LDAP config file on the mainframe that I probably am
not using correctly in my path, etc... If there is anyway I can send you the
config file for you to help me determine what to use in my constructor, it
would be much appreciated.

Thanks again,
Tim Reynolds

Joe Kaplan (MVP - ADSI)

unread,
Oct 12, 2004, 10:48:41 AM10/12/04
to
Please don't send me the config file as I don't think I'd know what to do
with it.

The first thing I'd try to do is to use the ldp.exe tool that comes with the
Windows Support Tools or ADAM installation to try to connect. This allows
you to try different binding options and is a more pure LDAP API client.
Once you get that working, then you can generally work backwards to figure
out what to use in code to bind.

One thing right off the top is that you need upper case LDAP in your binding
string. Lower case doesn't work for some reason. Sorry I didn't notice
that before.

Joe K.

"Tim Reynolds" <TimRe...@discussions.microsoft.com> wrote in message

news:5F302EAE-2120-4792...@microsoft.com...

Tim Reynolds

unread,
Oct 12, 2004, 4:19:11 PM10/12/04
to
Joe,
This was good info. I have made it a bit further now. This piece of code
does not blow up anylonger:

DirectoryEntry objDirEnt = new
DirectoryEntry("LDAP://BADEVL.verizon.com:9503/profiletype=user,CN=ldapadmin,O=vzds", "racfid=V121119", "sept04", AuthenticationTypes.Anonymous);
Object o = objDirEnt.NativeObject; //this will force the bind

However, I want to simply use LDAP to verify if a given userid/pw is valid.

Where do I go from here? Is this correct code - how do I use it.


Newbie,
Tim Reynolds

Thanks....

Joe Kaplan (MVP - ADSI)

unread,
Oct 12, 2004, 5:40:50 PM10/12/04
to
Sounds like you are almost there.

Generally with S.DS, you want to bind to an object that the user will be
allowed to view if the supplied credentials are valid. There may be one
object globally you can use for this (like RootDSE in Active Directory or
other LDAP V3 databases) or the domain root. You may also need to bind to
the user's own object in the directory. This all depends on authorization
policy.

In any event, that will affect the DN part of the binding string (after the
9503/ in your sample).

You would definitely want to use AuthenticationTypes.None as
AuthenticationTypes.Anonymous won't do any authentication. If you need to
protect the credentials on the wire, then you must use SecureSocketsLayer,
but that requires that the LDAP server be configured for SSL/LDAP and have a
valid certificate.

Normally, to see if the bind works, you put the Object o =
objDirEnt.NativeObject line in a try/catch block and catch the COMException
that will be thrown if the bind fails. If you want to be really picky, you
can look for the specific HRESULT (in the ErrorCode property) that indicates
bad credentials (0x8007052eL).

That should give you enough for a boolean test that you can use to do
authentication against the other directory.

HTH,

Joe K.

"Tim Reynolds" <TimRe...@discussions.microsoft.com> wrote in message

news:85D19FC7-4157-4C41...@microsoft.com...

Tim Reynolds

unread,
Oct 13, 2004, 9:51:04 AM10/13/04
to
Thanks for the info.

I have tried using AuthenticationTypes.None, but I receive
System.Runtime.InteropServices.COMException (0x8007052E): Logon failure:
unknown
user name or bad password
each time...

So I am guessing that my DN is still bad.

If I just use the constructor that only takes a path - I can bind with no
problem, but I this obviously isn't proving much:
DirectoryEntry objDirEnt = new
DirectoryEntry("LDAP://BADEVL.verizon.com:9503/profiletype=user,CN=ldapadmin,O=vzds");

It does confirm that the DN is there, but as for sending a valid user & pw
and having it work, I haven't been able to complete that yet. In fact, all
the AuthenticationTypes except Anonomous fail for one reason of the other.

As for the ldp.exe tool, I have been able to establish the connection, but I
have not been able to get a successful bind. I am not sure how to pass the
userid/pw I want in the bind (I'm guessing it is using my windows id&pw).
Here is what I get from that tool:

res = ldap_bind_s(ld, NULL, &NtAuthIdentity, 1158); // v.3
{NtAuthIdentity: User='v121119'; Pwd= <unavailable>; domain =
'cn=ldapadmin,o=vzds'.}
Error <86>: ldap_bind_s() failed: Auth Unknown.
res = ldap_bind_s(ld, NULL, &NtAuthIdentity, 1158); // v.3
{NtAuthIdentity: User='v121119'; Pwd= <unavailable>; domain =
'profiletype=user,cn=ldapadmin,o=vzds'.}
Error <86>: ldap_bind_s() failed: Auth Unknown.
0x0 = ldap_unbind(ld);


I'm on the trail - so if you have any other advice, I'll gladly take.

Thanks,

Joe Kaplan (MVP - ADSI)

unread,
Oct 13, 2004, 10:34:16 AM10/13/04
to
A couple of points that might help:

AuthenticationTypes.Secure, Sealing, Signing and Delegation are Windows
authentication features and probably should not be used with any directory
that isn't AD. I'd be very surprised to find a mainframe LDAP directory
that supports Windows negotiate authentication using GSSAPI.

AuthenticationTypes.None basically means a simple bind in LDAP terms. All
LDAP servers should support this. You can combine this flag with
SecureSocketsLayer if you want to use SSL/TLS channel encryption and your
server is configured with a proper certificate. Otherwise, credentials are
passed in plain text on the network.

AuthenticationTypes.Anonymous (as far as I know) doesn't perform an LDAP
bind, but searches anonymously. This is not what you want here as the bind
is what is doing the authentication.

ServerBind is just a performance optimization that you can use if you supply
an explicit server. The client stack will skip a DNS lookup for you if you
specify this flag. You could probably use that here since you are
specifying a specific host.

My guess for you is that either your username syntax is wrong (you might
need the full distinguished name for example) or the object you are binding
to doesn't allow you to view it. I'd suspect the former first though.

Regarding ldp.exe, make sure you uncheck the "domain" check box so that
Windows authentication isn't used. You want to use simple bind if possible.

I hope that sheds some more light.

Joe K.

"Tim Reynolds" <TimRe...@discussions.microsoft.com> wrote in message

news:A5C0D7FA-D238-4EC2...@microsoft.com...

Tim Reynolds

unread,
Oct 21, 2004, 3:35:02 PM10/21/04
to
Thanks Joe for your help on this.

We finally have some connectivity with our LDAP on the Main Frame.

However, when the id or pw is wrong or when pw has expired, i get the same
generic exception:

Runtime.InteropServices.ComException with message 'unknown user name or bad
password'

I should be able to get the real return code out of LDAP.

I can see in the LDAP trace on the mainframe the real reason - like (R000100
The pass word has expired.)

However, I am wondering how to get that back in my C# module.

My C# code is simply:

DirectoryEntry objDirEnt = new
DirectoryEntry("LDAP://BADEVL.verizon.com:9503/profiletype=user,cn=ldapadmin,o=vzds",
"racfid=V230093,profiletype=user,cn=ldapadmin,o=vzds", "pwgoeshere",
AuthenticationTypes.Delegation);

Object o = objDirEnt.NativeObject; //this will force the bind

This code is in a try / catch block. If no exception - it is valid id/pw -
this works - finally. However, for any other reason - bad id/pw/or expired
pw - it throws a generic exception noted above.

Any clues on how to get the real LDAP return code?

Thanks,
Tim

Lee Flight

unread,
Oct 21, 2004, 3:42:18 PM10/21/04
to
Unfortunately you cannot get back the specific failure
message on binding, this is a limitation of the underlying
MS LDAP client libraries used by ADSI and so S.DS.

Lee Flight

"Tim Reynolds" <TimRe...@discussions.microsoft.com> wrote in message

news:7298AE05-95F4-4943...@microsoft.com...

Joe Kaplan (MVP - ADSI)

unread,
Oct 21, 2004, 3:54:11 PM10/21/04
to
There might be a way if you p/invoke to ADsGetLastError, but I'm not sure if
that will really work in this case or not. I wrote this declare a while ago
and it seems to work ok:

Public Shared Function GetErrorMessageForLastAdsiError(ByVal hresult
As Integer) As String

Dim win32Facility As Integer = &H80070000
Dim bufferSize As Int32 = 256
Dim hr As Int32
Dim functionError As Int32

Dim errorDescription As StringBuilder
Dim nameDescription As StringBuilder
Dim errorCode As Int32

If (hresult And win32Facility) = win32Facility Then
errorDescription = New StringBuilder(bufferSize)
nameDescription = New StringBuilder(bufferSize)

hr = ADsGetLastError(errorCode, errorDescription,
bufferSize, nameDescription, bufferSize)

If hr = 0 Then
Return String.Format("{0}: {1} ({2})", errorCode,
errorDescription, nameDescription)
Else
functionError = Marshal.GetLastWin32Error()
Throw New
System.ComponentModel.Win32Exception(functionError)
End If

Else
Return String.Empty
End If


End Function

<DllImport("activeds.dll", SetLastError:=True,
CharSet:=CharSet.Unicode)> _
Private Shared Function ADsGetLastError(ByRef lpError As Int32,
ByVal lpErrorBuf As StringBuilder, ByVal dwErrorBufLen As Int32, ByVal
lpNameBuf As StringBuilder, ByVal dwNameBufLen As Int32) As Int32
End Function

If that works, please let us know as lots of people would be interested.

However, in the interests of security, you may not want to reveal why an
authentication attempt failed as that might be giving more ammo to an
attacker. Just something to think about. That's why the actual error is so
obscure in the first place.

Joe K.


"Lee Flight" <l...@le.ac.uk-nospam> wrote in message
news:u2fiYY6t...@TK2MSFTNGP12.phx.gbl...

Tim Reynolds

unread,
Oct 21, 2004, 3:59:05 PM10/21/04
to
Lee,
Someone else sent me some Java code - They are using this code and able to
extract the LDAP return code:


try {
ctx = new InitialContext(env);
status = VALID; // the bind worked - user is authenticated
retval = true;
}
catch (NamingException e){
analyzeLdapException(e);
}


They actually have code to look for LDAP: error code 49 - R and then get the
error number.

So I assume, if I want this I can't used managed code - I have to use
something like Java?

With just a generic interface that can't tell me when a password is expired,
there obviously would be no way to tell the user they need to put in a new
password... How would that work? Seems like a big limitation of using this
product.

Thanks,
Tim

Lee Flight

unread,
Oct 21, 2004, 4:45:37 PM10/21/04
to
Hi Joe

FWIW I tried a P/Invoke of ADsGetLastError some time back and got
nothing out of it :( I was prepared to believe that this was poor coding
on my part as it was at the limit of my skill set, however subsequent
investigation convinced me that the LDAP library was blocking this
information. I raised this informally with Microsoft and apparently
this is by design and may now be under review.

My argument is that there is no security in this either inherent or by
obscurity as other (non-MS) windows LDAP libraries and LDAP
libraries running on other OS will give the information as will a
network sniff.

Lee Flight

"Joe Kaplan (MVP - ADSI)" <joseph....@removethis.accenture.com> wrote
in message news:%23BMV5e6...@TK2MSFTNGP10.phx.gbl...

Tim Reynolds

unread,
Oct 21, 2004, 4:49:03 PM10/21/04
to
Joe,
Couple questions on the code:
Is the input parameter (hresult) the hresult of the exception that you pass
when catching an exception?
I am trying to access this field but it is not exposed in intellisense. I
catch exception e and HResult is not visisble. It must be private (I am
converting this code to C#) ...

Lee Flight

unread,
Oct 21, 2004, 4:53:17 PM10/21/04
to
I think if the fine-grained information is critical to you then
you will need to use a different LDAP library on the windows
platform, you might want to try Joe Kaplan's code for
ADsGetLastError to prove the point.

You could try opening an incident with PSS to get the definitive
answer and hope to get a Design Change Request through if the
problem is confirmed.

Lee Flight

"Tim Reynolds" <TimRe...@discussions.microsoft.com> wrote in message

news:4D4601E5-4B6B-48AA...@microsoft.com...

Tim Reynolds

unread,
Oct 21, 2004, 5:13:03 PM10/21/04
to
Ok -
I catch the ComException and pass the errorCode (HResult value) to this
method and am returned: "0: (LDAP Provider)" So I get the nameDescription
but no errorDescription.

The code I am using is a slight modification of yours and is pasted below to
see if you see any errors with it. In any case, it is not pulling up a more
clear description.

As for your comment about security hiding some info - I agree overall - but
in the case of a need to change pw, the user needs to know this. As for a
wrong id or pw - it should be generic - agreed.

Thanks,Tim

catch(COMException e)
{
string exceptionError = GetErrorMessageForLastAdsiError(e.ErrorCode);
Console.WriteLine(exceptionError);
Console.WriteLine(e.ToString());
Console.ReadLine();
}

public static string GetErrorMessageForLastAdsiError(int hresult)
{
int bufferSize = 256;
int functionError;
StringBuilder errorDescription;
StringBuilder nameDescription;
uint errorCode;
errorDescription = new StringBuilder(bufferSize);
nameDescription = new StringBuilder(bufferSize);
ADsGetLastError(out errorCode, errorDescription, bufferSize,
nameDescription, bufferSize);
if (errorCode == 0)
{
return String.Format("{0}: {1} ({2})", errorCode,errorDescription,
nameDescription);
}
else
{
functionError = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(functionError);
}
}

[DllImport("activeds.dll",CharSet=CharSet.Unicode,PreserveSig=false)]
static extern void ADsGetLastError(out uint lpError, StringBuilder
lpErrorBuf, int dwErrorBufLen, StringBuilder lpNameBuf, int
dwNameBufLen);


.......

Joe Kaplan (MVP - ADSI)

unread,
Oct 21, 2004, 5:27:48 PM10/21/04
to
Well, it doesn't sound like this is going to work. At least not with S.DS.
It would probably work with a different LDAP library, as you can get the
LDAP error code that way.

I think you were calling the function correctly, passing the ErrorCode
property of the COMException in. It does work for other stuff.

I agree that having the failure reason, especially if the directory provides
it, can be very helpful. People complain about this all the time with LDAP
authentication and AD.

If you want a .NET solution for a straight LDAP API wrapper, I can think of
three other possibilities for you:
- System.DirectoryServices.Protocols in .NET 2.0 does this (but you have to
use the beta framework to get this now...)
- The Novell C# LDAP library would probably do this fine
- I have a custom p/invoke library created by an MS guy that I use for some
stuff that would probably work for this. I can send it to you offline if
you want it.

Cheers,

Joe K.

"Tim Reynolds" <TimRe...@discussions.microsoft.com> wrote in message

news:2CD71266-D37B-46A4...@microsoft.com...

Tim Reynolds

unread,
Oct 21, 2004, 5:43:02 PM10/21/04
to
Joe,
Yes - thanks for the offer on suggestion #3 - I have a custom p/invoke
library created by an MS guy that I use for some
> stuff that would probably work for this. I can send it to you offline if
> you want it.

Please send....

Lee Flight

unread,
Oct 21, 2004, 5:44:52 PM10/21/04
to
That's an interesting one, I had assumed that it would still
be using the same underlying MS LDAP library as .NET1.* (?)

Lee Flight

"Joe Kaplan (MVP - ADSI)" <joseph....@removethis.accenture.com> wrote

in message news:OXJ3RT7t...@TK2MSFTNGP10.phx.gbl...

Joe Kaplan (MVP - ADSI)

unread,
Oct 21, 2004, 6:23:28 PM10/21/04
to
The new System.DirectoryServices.Protocols assembly uses wldap32 under the
hood, but it has no COM dependency on ADSI at all and requires no references
to S.DS either. It is pretty low level, but still an OO approach to LDAP.
It gives you the async programming model, access to controls and all that
other LDAP goodness. It is also very helpful for directories with impedance
mismatches with ADSI due to schema issues as it doesn't do the ADSI schema
mapping thing.

The other cool thing is that it can be used to get data via native LDAP
calls OR through DSML using, XML, SOAP and HTTP if you have a DSML server
configured. The API is the same, you just initialize it differently.

I have had very little exposure to it so far, but it looks like it will be
helpful to people who need it. I don't know enough to answer any questions
on it yet though.

Joe K.

"Lee Flight" <l...@le.ac.uk-nospam> wrote in message

news:epbh6c7t...@TK2MSFTNGP12.phx.gbl...

Joe Kaplan (MVP - ADSI)

unread,
Oct 21, 2004, 6:23:58 PM10/21/04
to
Send me an email (you can get my address from the headers) and I'll forward
you a copy.

Joe K.

"Tim Reynolds" <TimRe...@discussions.microsoft.com> wrote in message

news:6CC5E3FF-9B1B-4D11...@microsoft.com...

Tim Reynolds

unread,
Oct 22, 2004, 9:05:02 AM10/22/04
to
Joe - That's kinda funny. I thought you'd have access to my email based on
my ID. You mention I can get your address from the headers. I don't see
your address. I am looking in this post and only seeing "Joe Kaplan (MVP -
ADSI)" - I don't see an email address. Please advise.
Thank you,

Joe Kaplan (MVP - ADSI)

unread,
Oct 22, 2004, 10:23:31 AM10/22/04
to
Haha! Your's shows up as TimRe...@discussions.microsoft.com which I
figured was fake.

Send me a note here: joe @ joekaplan . net

(remove spaces obviously).

Cheers,

Joe K.

"Tim Reynolds" <TimRe...@discussions.microsoft.com> wrote in message

news:00B53DF8-1D4F-4C41...@microsoft.com...

Tim Reynolds

unread,
Oct 22, 2004, 11:09:11 AM10/22/04
to
sent....

Also wondering where I can get info about the Novell C# LDAP library....

Thanks,Tim

Lee Flight

unread,
Oct 22, 2004, 11:36:21 AM10/22/04
to
http://developer.novell.com/ndk/ldapcsharp.htm

You will need to register (free) to download.

Lee Flight

"Tim Reynolds" <TimRe...@discussions.microsoft.com> wrote in message

news:1DA9CCA1-BFCC-4329...@microsoft.com...

0 new messages