"Hacking" System.Net.Mail.SmtpClient

In .NET 2.0 there is a cool class called SmtpClient within System.Net.Mail.

With this class you can send mail (at least I thought).
The requirement was to send an email over a server of one of our agencies.
The mail server was secured by username/password authentication. That's why I used the following code:

 SmtpClient _client = new SmtpClient();
_client = new SmtpClient("smtp.myserver.at");

_client.UseDefaultCredentials = false;
_client.Credentials = new NetworkCredential("username", "password");

_client.Send("from@test.at", "to@test.at", "Hallo Welt", "Hallo max!!");

At execution of the last statement an exceptíon occurred:
image

 The inner exception said "Invalid lenght for a Base-64 char array."
 That didn't make any sense for me (nor helped me to fix the problem).

I had a look at the stack trace and found out, that the exception had occurred in a class called
SmtpNtlmAuthenticationModule.
image

So I used telnet to manually connect to the smtp server.

S: 220 smtp.myserver.at ESMTP
C: ehlo asd.com
S: 250-smtp.myserver.at Hello mypc.microsoft.com [xxx.xxx.xxx.xxx]
S: 250-SIZE 20971520
S: 250-PIPELINING
S: 250-AUTH PLAIN LOGIN CRAM-MD5 NTLM
C: AUTH NTLM <base64 encoded string>

S: 334 NTLM supported

Seemed like the server would support NTLM.. But.. when I looked up the NTLM-SMTP specification, I found out that the server should respond with

334 <NTLM supported as base64 encoded string>

So the problem obviously is, that "NTLM supported" was not a valid Base-64 encoded string (as the inner exception above also pointed out).

So how could this problem be solved...

I digged into the private members of the SmtpClient object and found a member called transport (of type SmtpTransport).

The SmtpTransport object had private members as well, and one of them was called authenticationModules - bingo!
image

This is an array of ISmtpAuthenticationModules like Negotiate, NTLM, Digest and Login.

Unfortunately SmtpClient always picks the most effective supported method (in this case NTLM). As NTLM was not working I needed a way to kick out NTLM of the list of supported auth methods.

So I used reflection to modify the array and "disable" (override) NTLM in the array. Here's what I did:

 FieldInfo transport = _client.GetType().GetField("transport",
    BindingFlags.NonPublic | BindingFlags.Instance);

FieldInfo authModules = transport.GetValue(_client).GetType()
    .GetField("authenticationModules",
        BindingFlags.NonPublic | BindingFlags.Instance);

Array modulesArray = authModules.GetValue(transport.GetValue(_client)) as Array;
modulesArray.SetValue(modulesArray.GetValue(2), 0);
modulesArray.SetValue(modulesArray.GetValue(2), 1);
modulesArray.SetValue(modulesArray.GetValue(2), 3);

Voila!
image

Guess which smtp authentication module will be used now :-)