public void CRAM_MD5による認証_失敗() { //setUp const bool usePlain = false; const bool useLogin = false; const bool useCramMd5 = true; var sut = new SmtpAuth(_smtpAuthUserList, usePlain, useLogin, useCramMd5); var str = sut.Job("AUTH CRAM-MD5"); var hash = Md5.Hash("user2", Base64.Decode(str.Substring(4))); var expected = (String)null; //exercise var actual = sut.Job(Base64.Encode(string.Format("user1 {0}", hash))); //verify Assert.That(actual, Is.EqualTo(expected)); }
public void LOGIN_初期パラメータ付_による認証_成功() { //setUp const bool usePlain = false; const bool useLogin = true; const bool useCramMd5 = false; var sut = new SmtpAuth(_smtpAuthUserList, usePlain, useLogin, useCramMd5); var str = String.Format("AUTH login {0}", Base64.Encode("user1")); Assert.That(sut.Job(str), Is.EqualTo("334 UGFzc3dvcmQ6")); Assert.That(sut.Job(Base64.Encode("user1")), Is.EqualTo("235 Authentication successful.")); var expected = true; //exercise var actual = sut.IsFinish; //verify Assert.That(actual, Is.EqualTo(expected)); }
public void CRAM_MD5による認証_成功() { //setUp const bool usePlain = false; const bool useLogin = false; const bool useCramMd5 = true; var sut = new SmtpAuth(_smtpAuthUserList, usePlain, useLogin, useCramMd5); var str = sut.Job("AUTH CRAM-MD5"); var hash = Md5.Hash("user1", Base64.Decode(str.Substring(4))); Assert.That(sut.Job(Base64.Encode(string.Format("user1 {0}", hash))),Is.EqualTo("235 Authentication successful.")); var expected = true; //exercise var actual = sut.IsFinish; //verify Assert.That(actual, Is.EqualTo(expected)); }
//接続単位の処理 protected override void OnSubThread(SockObj sockObj) { var sockTcp = (SockTcp)sockObj; //WebApi関連 if (!Kernel.WebApi.ServiceSmtp) { if (sockTcp != null) sockTcp.Close(); return; } //グリーティングメッセージの表示 sockTcp.AsciiSend("220 " + Kernel.ChangeTag((string)Conf.Get("bannerMessage"))); var checkParam = new CheckParam((bool)Conf.Get("useNullFrom"), (bool)Conf.Get("useNullDomain")); var session = new Session(); SmtpAuth smtpAuth = null; var useEsmtp = (bool)Conf.Get("useEsmtp"); if (useEsmtp) { if (_smtpAuthRange.IsHit(sockTcp.RemoteIp)) { var usePlain = (bool)Conf.Get("useAuthPlain"); var useLogin = (bool)Conf.Get("useAuthLogin"); var useCramMd5 = (bool)Conf.Get("useAuthCramMD5"); smtpAuth = new SmtpAuth(_smtpAuthUserList, usePlain, useLogin, useCramMd5); } } //受信サイズ制限 var sizeLimit = (int)Conf.Get("sizeLimit"); //Ver5.0.0-b8 Frmo:偽造の拒否 var useCheckFrom = (bool)Conf.Get("useCheckFrom"); while (IsLife()) { Thread.Sleep(0); var cmd = recvCmd(sockTcp); if (cmd == null){ break;//切断された } if (cmd.Str == "") { Thread.Sleep(100);//受信待機中 continue; } var smtpCmd = new SmtpCmd(cmd); //WebApi関連 var responseSmtp = Kernel.WebApi.ResponseSmtp(cmd.CmdStr); if (responseSmtp != -1){ sockTcp.AsciiSend(string.Format("{0} WebAPI response", responseSmtp)); continue; } if (smtpCmd.Kind == SmtpCmdKind.Unknown) {//無効コマンド //SMTP認証 if (smtpAuth != null) { if (!smtpAuth.IsFinish) { var ret = smtpAuth.Job(smtpCmd.Str); if (ret != null) { sockTcp.AsciiSend(ret); continue; } } } sockTcp.AsciiSend(string.Format("500 command not understood: {0}", smtpCmd.Str)); //無効コマンドが10回続くと不正アクセスとして切断する session.UnknownCmdCounter++; if (session.UnknownCmdCounter > 10) { Logger.Set(LogKind.Secure, sockTcp, 54, string.Format("unknownCmdCount={0}", session.UnknownCmdCounter)); break; } continue; } session.UnknownCmdCounter = 0; //不正でない場合クリアする //QUIT・NOOP・RSETはいつでも受け付ける if (smtpCmd.Kind == SmtpCmdKind.Quit) { sockTcp.AsciiSend("221 closing connection"); break; } if (smtpCmd.Kind == SmtpCmdKind.Noop) { sockTcp.AsciiSend("250 OK"); continue; } if (smtpCmd.Kind == SmtpCmdKind.Rset) { session.Rest(); sockTcp.AsciiSend("250 Reset state"); continue; } //下記のコマンド以外は、SMTP認証の前には使用できない if (smtpCmd.Kind != SmtpCmdKind.Noop && smtpCmd.Kind != SmtpCmdKind.Helo && smtpCmd.Kind != SmtpCmdKind.Ehlo && smtpCmd.Kind != SmtpCmdKind.Rset) { if (smtpAuth != null) { if (!smtpAuth.IsFinish) { sockTcp.AsciiSend("530 Authentication required."); continue; } } } if (smtpCmd.Kind == SmtpCmdKind.Helo || smtpCmd.Kind == SmtpCmdKind.Ehlo) { if (session.Hello != null) {//HELO/EHLOは1回しか受け取らない sockTcp.AsciiSend(string.Format("503 {0} Duplicate HELO/EHLO", Kernel.ServerName)); continue; } if (smtpCmd.ParamList.Count < 1) { sockTcp.AsciiSend(string.Format("501 {0} requires domain address", smtpCmd.Kind.ToString().ToUpper())); continue; } session.Helo(smtpCmd.ParamList[0]); Logger.Set(LogKind.Normal, sockTcp, 1, string.Format("{0} {1} from {2}[{3}]", smtpCmd.Kind.ToString().ToUpper(), session.Hello, sockObj.RemoteHostname, sockTcp.RemoteAddress)); if (smtpCmd.Kind == SmtpCmdKind.Ehlo) { sockTcp.AsciiSend(string.Format("250-{0} Helo {1}[{2}], Pleased to meet you.", Kernel.ServerName, sockObj.RemoteHostname, sockObj.RemoteAddress)); sockTcp.AsciiSend("250-8BITMIME"); sockTcp.AsciiSend(string.Format("250-SIZE={0}", sizeLimit)); if (smtpAuth != null) { string ret = smtpAuth.EhloStr();//SMTP認証に関するhelp文字列の取得 if (ret != null) { sockTcp.AsciiSend(ret); } } sockTcp.AsciiSend("250 HELP"); } else { sockTcp.AsciiSend(string.Format("250 {0} Helo {1}[{2}], Pleased to meet you.", Kernel.ServerName, sockObj.RemoteHostname, sockObj.RemoteAddress)); } continue; } if (smtpCmd.Kind == SmtpCmdKind.Mail) { if (!checkParam.Mail(smtpCmd.ParamList)){ sockTcp.AsciiSend(checkParam.Message); continue; } session.Mail(new MailAddress(smtpCmd.ParamList[1]));//MAILコマンドを取得完了(""もあり得る) sockTcp.AsciiSend(string.Format("250 {0}... Sender ok", smtpCmd.ParamList[1])); continue; } if (smtpCmd.Kind == SmtpCmdKind.Rcpt) { if (session.From == null) {//RCPTの前にMAILコマンドが必要 sockTcp.AsciiSend("503 Need MAIL before RCPT"); continue; } if (!checkParam.Rcpt(smtpCmd.ParamList)) { sockTcp.AsciiSend(checkParam.Message); continue; } var mailAddress = new MailAddress(smtpCmd.ParamList[1]); if (mailAddress.Domain == "") {//ドメイン指定の無い場合は、自ドメイン宛と判断する mailAddress = new MailAddress(mailAddress.User, DomainList[0]); } //自ドメイン宛かどうかの確認 if (mailAddress.IsLocal(DomainList)) { //Ver5.0.0-b4 エリアスで指定したユーザ名の確認 if (!Alias.IsUser(mailAddress.User)) { //有効なユーザかどうかの確認 if (!Kernel.MailBox.IsUser(mailAddress.User)) { //Ver_Ml //有効なメーリングリスト名かどうかの確認 //********************************************************************** //Ver_Ml //********************************************************************** //#if ML_SERVER if(!_mlList.IsUser(mailAddress)){ this.Logger.Set(LogKind.Secure,sockTcp,6,mailAddress.User); sockTcp.AsciiSend(string.Format("550 {0}... User unknown",mailAddress.User)); continue; } //#else // Logger.Set(LogKind.Secure, sockTcp, 6, mailAddress.User); // sockTcp.AsciiSend(string.Format("550 {0}... User unknown", mailAddress.User)); // continue; //#endif //********************************************************************** } } } else {//中継(リレー)が許可されているかどうかのチェック if (!_popBeforeSmtp.Auth(sockObj.RemoteIp)) { //Allow及びDenyリストで中継(リレー)が許可されているかどうかのチェック if (!_relay.IsAllow(sockObj.RemoteIp)) { sockTcp.AsciiSend(string.Format("553 {0}... Relay operation rejected", mailAddress)); continue; } } } //メールアドレスをRCPTリストへ追加する session.Rcpt(mailAddress); sockTcp.AsciiSend(string.Format("250 {0}... Recipient ok", mailAddress)); continue; } if (smtpCmd.Kind == SmtpCmdKind.Data) { if (session.From == null) { sockTcp.AsciiSend("503 Need MAIL command"); continue; } if (session.To.Count == 0) { sockTcp.AsciiSend("503 Need RCPT (recipient)"); continue; } sockTcp.AsciiSend("354 Enter mail,end with \".\" on a line by ltself"); var data = new Data(sizeLimit); if(!data.Recv(sockTcp,20,Logger,this)){ Thread.Sleep(1000); break; } //以降は、メール受信完了の場合 if (useCheckFrom) {//Frmo:偽造の拒否 var mailAddress = new MailAddress(data.Mail.GetHeader("From")); if (mailAddress.User == "") { Logger.Set(LogKind.Secure, sockTcp, 52, string.Format("From:{0}", mailAddress)); sockTcp.AsciiSend("530 There is not an email address in a local user"); continue; } //ローカルドメインでない場合は拒否する if (!mailAddress.IsLocal(DomainList)) { Logger.Set(LogKind.Secure, sockTcp, 28, string.Format("From:{0}", mailAddress)); sockTcp.AsciiSend("530 There is not an email address in a local domain"); continue; } //有効なユーザでない場合拒否する if (!Kernel.MailBox.IsUser(mailAddress.User)) { Logger.Set(LogKind.Secure, sockTcp, 29, string.Format("From:{0}", mailAddress)); sockTcp.AsciiSend("530 There is not an email address in a local user"); continue; } } //ヘッダの変換及び追加 _changeHeader.Exec(data.Mail, Logger); //テンポラリバッファの内容でMailオブジェクトを生成する var error = false; foreach (var to in Alias.Reflection(session.To, Logger)) { if (!MailSave2(session.From, to, data.Mail, sockTcp.RemoteHostname, sockTcp.RemoteIp)) {//MLとそれ以外を振り分けて保存する error = true; break; } } sockTcp.AsciiSend(error ? "554 MailBox Error" : "250 OK"); session.To.Clear(); } } if (sockTcp != null) sockTcp.Close(); }
public void LOGINによる認証_失敗() { //setUp const bool usePlain = false; const bool useLogin = true; const bool useCramMd5 = false; var sut = new SmtpAuth(_smtpAuthUserList, usePlain, useLogin, useCramMd5); Assert.That(sut.Job("AUTH LOGIN"), Is.EqualTo("334 VXNlcm5hbWU6")); Assert.That(sut.Job(Base64.Encode("user1")), Is.EqualTo("334 UGFzc3dvcmQ6")); String expected = null; //exercise var actual = sut.Job(Base64.Encode("xxx")); //verify Assert.That(actual, Is.EqualTo(expected)); }
public void 認証前にIsFinishはfalseがセットされる() { //setUp const bool usePlain = true; const bool useLogin = true; const bool useCramMd5 = true; var sut = new SmtpAuth(_smtpAuthUserList, usePlain, useLogin, useCramMd5); var expected = false; //exercise var actual = sut.IsFinish; //verify Assert.That(actual, Is.EqualTo(expected)); }
public void 無効なAUTHコマンド2() { //setUp const bool usePlain = false; //無効になっている const bool useLogin = true; const bool useCramMd5 = true; var sut = new SmtpAuth(_smtpAuthUserList, usePlain, useLogin, useCramMd5); var expected = "500 command not understood: AUTH PLAIN\r\n"; //exercise var actual = sut.Job("AUTH PLAIN");//本来は、正しいコマンドだが //verify Assert.That(actual, Is.EqualTo(expected)); }
public void 無効なAUTHコマンド() { //setUp const bool usePlain = true; const bool useLogin = true; const bool useCramMd5 = true; var sut = new SmtpAuth(_smtpAuthUserList, usePlain, useLogin, useCramMd5); var expected = "504 Unrecognized authentication type."; //exercise var actual = sut.Job("AUTH XXXX"); //verify Assert.That(actual, Is.EqualTo(expected)); }
public void PLAINによる認証_成功() { //setUp const bool usePlain = true; const bool useLogin = false; const bool useCramMd5 = false; var sut = new SmtpAuth(_smtpAuthUserList, usePlain, useLogin, useCramMd5); Assert.That(sut.Job("AUTH PLAIN"), Is.EqualTo("334 ")); Assert.That(sut.Job(Base64.Encode("user1\0user1\0user1")), Is.EqualTo("235 Authentication successful.")); var expected = true; //exercise var actual = sut.IsFinish; //verify Assert.That(actual, Is.EqualTo(expected)); }
public void PLAINによる認証_失敗() { //setUp const bool usePlain = true; const bool useLogin = false; const bool useCramMd5 = false; var sut = new SmtpAuth(_smtpAuthUserList, usePlain, useLogin, useCramMd5); Assert.That(sut.Job("AUTH PLAIN"), Is.EqualTo("334 ")); String expected = null; //exercise var actual = sut.Job(Base64.Encode("user1\0user1\0user2")); //verify Assert.That(actual, Is.EqualTo(expected)); }
//接続単位の処理 override protected void OnSubThread(SockObj sockObj) { var sockTcp = (SockTcp)sockObj; //WebApi関連 if (!Kernel.WebApi.ServiceSmtp) { if (sockTcp != null) { sockTcp.Close(); } return; } //グリーティングメッセージの表示 sockTcp.AsciiSend("220 " + Kernel.ChangeTag((string)Conf.Get("bannerMessage"))); var checkParam = new CheckParam((bool)Conf.Get("useNullFrom"), (bool)Conf.Get("useNullDomain")); var session = new Session(); SmtpAuth smtpAuth = null; var useEsmtp = (bool)Conf.Get("useEsmtp"); if (useEsmtp) { if (_smtpAuthRange.IsHit(sockTcp.RemoteIp)) { var usePlain = (bool)Conf.Get("useAuthPlain"); var useLogin = (bool)Conf.Get("useAuthLogin"); var useCramMd5 = (bool)Conf.Get("useAuthCramMD5"); smtpAuth = new SmtpAuth(_smtpAuthUserList, usePlain, useLogin, useCramMd5); } } //受信サイズ制限 var sizeLimit = (int)Conf.Get("sizeLimit"); //Ver5.0.0-b8 Frmo:偽造の拒否 var useCheckFrom = (bool)Conf.Get("useCheckFrom"); while (IsLife()) { Thread.Sleep(0); var cmd = recvCmd(sockTcp); if (cmd == null) { break;//切断された } if (cmd.Str == "") { Thread.Sleep(100);//受信待機中 continue; } var smtpCmd = new SmtpCmd(cmd); //WebApi関連 var responseSmtp = Kernel.WebApi.ResponseSmtp(cmd.CmdStr); if (responseSmtp != -1) { sockTcp.AsciiSend(string.Format("{0} WebAPI response", responseSmtp)); continue; } if (smtpCmd.Kind == SmtpCmdKind.Unknown) //無効コマンド //SMTP認証 { if (smtpAuth != null) { if (!smtpAuth.IsFinish) { var ret = smtpAuth.Job(smtpCmd.Str); if (ret != null) { sockTcp.AsciiSend(ret); continue; } } } sockTcp.AsciiSend(string.Format("500 command not understood: {0}", smtpCmd.Str)); //無効コマンドが10回続くと不正アクセスとして切断する session.UnknownCmdCounter++; if (session.UnknownCmdCounter > 10) { Logger.Set(LogKind.Secure, sockTcp, 54, string.Format("unknownCmdCount={0}", session.UnknownCmdCounter)); break; } continue; } session.UnknownCmdCounter = 0; //不正でない場合クリアする //QUIT・NOOP・RSETはいつでも受け付ける if (smtpCmd.Kind == SmtpCmdKind.Quit) { sockTcp.AsciiSend("221 closing connection"); break; } if (smtpCmd.Kind == SmtpCmdKind.Noop) { sockTcp.AsciiSend("250 OK"); continue; } if (smtpCmd.Kind == SmtpCmdKind.Rset) { session.Rest(); sockTcp.AsciiSend("250 Reset state"); continue; } //下記のコマンド以外は、SMTP認証の前には使用できない if (smtpCmd.Kind != SmtpCmdKind.Noop && smtpCmd.Kind != SmtpCmdKind.Helo && smtpCmd.Kind != SmtpCmdKind.Ehlo && smtpCmd.Kind != SmtpCmdKind.Rset) { if (smtpAuth != null) { if (!smtpAuth.IsFinish) { sockTcp.AsciiSend("530 Authentication required."); continue; } } } if (smtpCmd.Kind == SmtpCmdKind.Helo || smtpCmd.Kind == SmtpCmdKind.Ehlo) { if (session.Hello != null) //HELO/EHLOは1回しか受け取らない { sockTcp.AsciiSend(string.Format("503 {0} Duplicate HELO/EHLO", Kernel.ServerName)); continue; } if (smtpCmd.ParamList.Count < 1) { sockTcp.AsciiSend(string.Format("501 {0} requires domain address", smtpCmd.Kind.ToString().ToUpper())); continue; } session.Helo(smtpCmd.ParamList[0]); Logger.Set(LogKind.Normal, sockTcp, 1, string.Format("{0} {1} from {2}[{3}]", smtpCmd.Kind.ToString().ToUpper(), session.Hello, sockObj.RemoteHostname, sockTcp.RemoteAddress)); if (smtpCmd.Kind == SmtpCmdKind.Ehlo) { sockTcp.AsciiSend(string.Format("250-{0} Helo {1}[{2}], Pleased to meet you.", Kernel.ServerName, sockObj.RemoteHostname, sockObj.RemoteAddress)); sockTcp.AsciiSend("250-8BITMIME"); sockTcp.AsciiSend(string.Format("250-SIZE={0}", sizeLimit)); if (smtpAuth != null) { string ret = smtpAuth.EhloStr();//SMTP認証に関するhelp文字列の取得 if (ret != null) { sockTcp.AsciiSend(ret); } } sockTcp.AsciiSend("250 HELP"); } else { sockTcp.AsciiSend(string.Format("250 {0} Helo {1}[{2}], Pleased to meet you.", Kernel.ServerName, sockObj.RemoteHostname, sockObj.RemoteAddress)); } continue; } if (smtpCmd.Kind == SmtpCmdKind.Mail) { if (!checkParam.Mail(smtpCmd.ParamList)) { sockTcp.AsciiSend(checkParam.Message); continue; } session.Mail(new MailAddress(smtpCmd.ParamList[1]));//MAILコマンドを取得完了(""もあり得る) sockTcp.AsciiSend(string.Format("250 {0}... Sender ok", smtpCmd.ParamList[1])); continue; } if (smtpCmd.Kind == SmtpCmdKind.Rcpt) { if (session.From == null) //RCPTの前にMAILコマンドが必要 { sockTcp.AsciiSend("503 Need MAIL before RCPT"); continue; } if (!checkParam.Rcpt(smtpCmd.ParamList)) { sockTcp.AsciiSend(checkParam.Message); continue; } var mailAddress = new MailAddress(smtpCmd.ParamList[1]); if (mailAddress.Domain == "") //ドメイン指定の無い場合は、自ドメイン宛と判断する { mailAddress = new MailAddress(mailAddress.User, DomainList[0]); } //自ドメイン宛かどうかの確認 if (mailAddress.IsLocal(DomainList)) { //Ver5.0.0-b4 エリアスで指定したユーザ名の確認 if (!Alias.IsUser(mailAddress.User)) { //有効なユーザかどうかの確認 if (!Kernel.MailBox.IsUser(mailAddress.User)) { //Ver_Ml //有効なメーリングリスト名かどうかの確認 //********************************************************************** //Ver_Ml //********************************************************************** //#if ML_SERVER if (!_mlList.IsUser(mailAddress)) { this.Logger.Set(LogKind.Secure, sockTcp, 6, mailAddress.User); sockTcp.AsciiSend(string.Format("550 {0}... User unknown", mailAddress.User)); continue; } //#else // Logger.Set(LogKind.Secure, sockTcp, 6, mailAddress.User); // sockTcp.AsciiSend(string.Format("550 {0}... User unknown", mailAddress.User)); // continue; //#endif //********************************************************************** } } } else //中継(リレー)が許可されているかどうかのチェック { if (!_popBeforeSmtp.Auth(sockObj.RemoteIp)) { //Allow及びDenyリストで中継(リレー)が許可されているかどうかのチェック if (!_relay.IsAllow(sockObj.RemoteIp)) { sockTcp.AsciiSend(string.Format("553 {0}... Relay operation rejected", mailAddress)); continue; } } } //メールアドレスをRCPTリストへ追加する session.Rcpt(mailAddress); sockTcp.AsciiSend(string.Format("250 {0}... Recipient ok", mailAddress)); continue; } if (smtpCmd.Kind == SmtpCmdKind.Data) { if (session.From == null) { sockTcp.AsciiSend("503 Need MAIL command"); continue; } if (session.To.Count == 0) { sockTcp.AsciiSend("503 Need RCPT (recipient)"); continue; } sockTcp.AsciiSend("354 Enter mail,end with \".\" on a line by ltself"); var data = new Data(sizeLimit); if (!data.Recv(sockTcp, 20, Logger, this)) { Thread.Sleep(1000); break; } //以降は、メール受信完了の場合 if (useCheckFrom) //Frmo:偽造の拒否 { var mailAddress = new MailAddress(data.Mail.GetHeader("From")); if (mailAddress.User == "") { Logger.Set(LogKind.Secure, sockTcp, 52, string.Format("From:{0}", mailAddress)); sockTcp.AsciiSend("530 There is not an email address in a local user"); continue; } //ローカルドメインでない場合は拒否する if (!mailAddress.IsLocal(DomainList)) { Logger.Set(LogKind.Secure, sockTcp, 28, string.Format("From:{0}", mailAddress)); sockTcp.AsciiSend("530 There is not an email address in a local domain"); continue; } //有効なユーザでない場合拒否する if (!Kernel.MailBox.IsUser(mailAddress.User)) { Logger.Set(LogKind.Secure, sockTcp, 29, string.Format("From:{0}", mailAddress)); sockTcp.AsciiSend("530 There is not an email address in a local user"); continue; } } //ヘッダの変換及び追加 _changeHeader.Exec(data.Mail, Logger); //テンポラリバッファの内容でMailオブジェクトを生成する var error = false; foreach (var to in Alias.Reflection(session.To, Logger)) { if (!MailSave2(session.From, to, data.Mail, sockTcp.RemoteHostname, sockTcp.RemoteIp)) //MLとそれ以外を振り分けて保存する { error = true; break; } } sockTcp.AsciiSend(error ? "554 MailBox Error" : "250 OK"); session.To.Clear(); } } if (sockTcp != null) { sockTcp.Close(); } }