"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:
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.
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!
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);
Guess which smtp authentication module will be used now :-)