0
votes

From a previous question here I learned how to use INDY SMTP (TIdSMTP) to send mails with an Office 365 account. I also figured it out for many others and it works for nearly any common e-mail provider out there. But I can't figure out how to use it with my local Exchange Server. Some time ago there were SASL-NTLM components shipped with indy, but it seems they have been removed. I need NTLM to connect to a local Exchange Server. But I can't figure out how to do this without NTLM.

1
What specific problem are you having? If you don't explain what "that problem" is, it's pretty difficult for us to offer advice on how to fix it. Please edit your question and ask a more specific question, so we can try and help you solve it.Ken White
Suppprt for NTLM SASL was not removed. It was never finalized in the first place. The TIdSASLNTLM component still exists, in the IdSASL_NTLM.pas unit, it is simply not registered on the Component Palette by default, but you can instantiate it programmably in code.Remy Lebeau
@RemyLebeau: The "IdSASL_NTLM.pas" does not exist in my INDY Version (Indy 10 shipped with XE2). I just own "IdNTLM.pas". {at} KenWhite: I will add the errors I get when I use other SALS methodes (or simply no SASL) tomorrow, the machine with the exchange server is no at home.Timestop

1 Answers

1
votes

I have recently struggled with Indy and my Exchange server as well. The SaslNtlm component is not in the version of Indy10 shipped with Delphi XE5. It is not even in the source files of the Indy Protocols folder.

Fortunately a lot of the stuff required to do NTLM authentication with the SMTP client is available. There is a unit called IdAuthenticationSSPI which implements the entire NTLM exchange. All that was left to do was to implement a custom descendant from TIdSASL which interacts with the TIndySSPINTLMClient object.

  TSaslNtlm = class(TIdSASL)
  public
    constructor Create(AOwner: TComponent);
    destructor Destroy; override;

    function StartAuthenticate(const AChallenge, AHost, AProtocolName : string): string; override;
    function ContinueAuthenticate(const ALastResponse, AHost, AProtocolName : string): string; override;
    function IsReadyToStart: Boolean; override;

    class function ServiceName: TIdSASLServiceName; override;

  private
    FSSPIClient: TIndySSPINTLMClient;
  end;

The implementation of the class is as follows:

constructor TSaslNtlm.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FSSPIClient := TIndySSPINTLMClient.Create;
end;

destructor TSaslNtlm.Destroy;
begin
  FSSPIClient.Free;
  inherited;
end;

function TSaslNtlm.StartAuthenticate(const AChallenge, AHost,
                                     AProtocolName: string): string;
begin
  FSSPIClient.SetCredentials(AHost, '', '');
  Result := BytesToStringRaw(FSSPIClient.InitAndBuildType1Message);
end;

function TSaslNtlm.ContinueAuthenticate(const ALastResponse, AHost,
                                        AProtocolName: string): string;
var LastMsg: TIdBytes;
begin
  LastMsg := ToBytes(ALastResponse, Indy8BitEncoding
                     {$IFDEF STRING_IS_ANSI}, Indy8BitEncoding{$ENDIF});
  Result := BytesToStringRaw(FSSPIClient.UpdateAndBuildType3Message(LastMsg));
end;

function TSaslNtlm.IsReadyToStart: Boolean;
begin
  Result := True;
end;

class function TSaslNtlm.ServiceName: TIdSASLServiceName;
begin
  Result := 'NTLM';
end;

And then it is simply a matter of adding this SASL mechanism to the SMTP client:

smtp.AuthType := satSASL;
ntml := TSaslNtlm.Create(smtp);
with smtp.SASLMechanisms.Add do begin
  DisplayName := ntlm.ServiceName;
  SASL := ntlm;
end;