歡迎您光臨本站 註冊首頁

修正Qmail auth smtp中電子郵件地址任意的patch

←手機掃碼閱讀     火星人 @ 2014-03-04 , reply:0

修正Qmail auth smtp中電子郵件地址任意的patch

qmail 的auth smtp有兩個patch,
分別在:http://members.elysium.pl/brush/qmail-smtpd-auth/和www.nimh.org/hacks/qmail-smtpd.c。

不過都有個問題,就是auth 的用戶可以和mail from的用戶不一樣,這樣就
給了垃圾郵件發送者可乘之機,只要知道了某個用戶的地址和口令,就可以
以自己的名義發信。同時認證用戶的過程並不記錄在郵件日誌中,這給了系統管理員造成了很大不便,難以知道是哪個用戶被黑掉了。

下面這個patch就是針對這種情況寫的。

作用有兩個:
1. 通過環境變數來控制是否記錄認證過程。
2. 如果mail from和auth user name不一致,拒絕發送信件。


這個patch是基於www.nimh.org/hacks/qmail-smtpd.c,iceblood版本中實際上也是的這個。因此,用iceblood版本的可以直接使用。如果是用
http://members.elysium.pl/brush/qmail-smtpd-auth/版本的就需要自己
相應的修改了。

安裝方法,如下,將下面這些內容存到qmail-smtpd.patch。放到qmail的源碼目錄。然後
1. patch < qmail-smtpd.patch
2. make qmail-smtpd
3. 替換qmail-smtpd
   或者複製到新的執行文件例如qmail-smtpd.auth,這樣你的舊版本就能保留
  了,只需要在smtpd的啟動腳本中,修改為對應的執行文件就可以。

如果你需要記錄smtp認證的信息,在smtpd的啟動腳本中定義環境變數,如下:
LOG_AUTH=1
export LOG_AUTH

預設情況下,認證信息不記入log


--- qmail-smtpd.c       2003-04-05 17:55:20.000000000 -0500
+++ qmail-smtpd.c.new   2003-04-05 17:50:58.000000000 -0500
@@ -304,7 +304,11 @@
   if (!stralloc_copys(&rcptto,"")) die_nomem();
   if (!stralloc_copys(&mailfrom,addr.s)) die_nomem();
   if (!stralloc_0(&mailfrom)) die_nomem();
+  if (smtp_auth_validfrom(mailfrom.s)) {
   out("250 ok\r\n");
+  } else {
+    out("must use username as From authenticated! (#5.7.1)\r\n");
+  }
}
void smtp_rcpt(arg) char *arg; {
   if (!seenmail) { err_wantmail(); return; }
@@ -527,6 +531,82 @@
static stralloc smtpauth = {0};
static char smtpauthlogin;
static char smtpauthpass;
+static int authd = 0;
+
+/* Author: gadfly@163.com.
+ * 1. Check consistent between auth user and 'From' user.
+ * 2. Read the LOG_AUTH enviroment variable to determine whether logging the auth info.
+ * LOG_AUTH=1, write the auth smtp info into syslog.
+ */
+#include <syslog.h>;
+
+#define SMTP_AUTH_SUCCESS     0
+#define SMTP_AUTH_FAILED      1
+
+int smtp_auth_validfrom(from) char * from;
+{
+  stralloc tmplogin={0},mydefaultdomain={0};
+  char authusername;
+  int k, userlen;
+
+  if (!authd) return 1;
+
+  authusername = '\0';
+  userlen = k = str_len(smtpauthlogin);
+  k = byte_rchr(smtpauthlogin, k, '@');
+
+  if (k == userlen)
+  {
+    k = byte_rchr(smtpauthlogin, userlen, '%');
+
+    if (k == userlen)
+    {
+      if (control_readfile(&mydefaultdomain,"/var/qmail/control/me",1) != 1)
+      {
+        die_nomem();
+      }
+      if (!stralloc_copys(&tmplogin, smtpauthlogin) )
+      {
+        die_nomem();
+      }
+      if (!stralloc_cats(&tmplogin, "@") )
+      {
+        die_nomem();
+      }
+      if (!stralloc_cat(&tmplogin, &mydefaultdomain))
+      {
+        die_nomem();
+      }
+      strncpy(authusername, tmplogin.s, sizeof(authusername));
+      }
+      else
+      {
+        strcpy(authusername, smtpauthlogin);
+      authusername='@';
+      }
+  }
+  else {
+    strcpy(authusername, smtpauthlogin);
+  }
+  return !strcasecmp(from, authusername);
+}
+
+
+void auth_smtplog(authlogin, authresult)
+char * authlogin;
+int authresult;
+{
+  char * x = env_get("LOG_AUTH");
+
+  if (!x || (*x != '1')) return;
+
+  if (authresult == SMTP_AUTH_SUCCESS) {
+    syslog(LOG_MAIL | LOG_INFO, "smtp auth: user name is %s, success!", authlogin);
+  } else {
+    syslog(LOG_MAIL | LOG_INFO, "smtp auth: user name is %s, failed!", authlogin);
+  }
+}
+
static int smtpauth_getl(void) {
   int i;
   if (!stralloc_copys(&smtpauth, "")) return -1;
@@ -611,11 +691,14 @@
   wait_pid(&st, pid);
   if (wait_exitcode(st) == 0) {
     out("235 go ahead\r\n");
+    authd = 1;
+    auth_smtplog(smtpauthlogin, SMTP_AUTH_SUCCESS);
     flush();
     relayclient="";
     return;
   }
   sleep(2);
+  auth_smtplog(smtpauthlogin, SMTP_AUTH_FAILED);
   out("535 auth failure\r\n"); flush(); _exit(0);
   /* done */
}


呵呵,正式用之前,各位多測試測試。

幾種情況需要測測看:
1. 其它伺服器發到本域,smtp過程是否正常,本域能否收到
2. from和auth user 不一樣會出現什麼情況, 一樣的話,能否發信。
3. LOG_AUTH是否起作用。

呵呵,qmail的對齊用空格,實在不習慣,沒辦法,為保持一致,我也用了。     

更新版本
應一些朋友的要求,加了本地發信人發給本地,也需要auth的功能。

另外而且還加了一些註釋,版本號。方法和前面一樣。

代碼如下:

--- qmail-smtpd.c        2003-06-02 18:34:51.000000000 -0400
+++ qmail-smtpd.c.new        2003-06-02 18:42:31.000000000 -0400
@@ -268,6 +268,7 @@
   int r;
   r = rcpthosts(addr.s,str_len(addr.s));
   if (r == -1) die_control();
+  if (!localauthd()) return 0;
   return r;
}

@@ -304,7 +305,11 @@
   if (!stralloc_copys(&rcptto,"")) die_nomem();
   if (!stralloc_copys(&mailfrom,addr.s)) die_nomem();
   if (!stralloc_0(&mailfrom)) die_nomem();
-  out("250 ok\r\n");
+  if (smtp_auth_validfrom(mailfrom.s)) {
+    out("250 ok\r\n");
+  } else {
+    out("must use username as From authenticated! (#5.7.1)\r\n");
+  }
}
void smtp_rcpt(arg) char *arg; {
   if (!seenmail) { err_wantmail(); return; }
@@ -527,6 +532,89 @@
static stralloc smtpauth = {0};
static char smtpauthlogin;
static char smtpauthpass;
+static int authd = 0;
+
+/* Author: gadfly@163.com. Version: 1.2.
+ * 1. Check consistent between auth user and 'From' user.
+ * 2. Read the LOG_AUTH enviroment variable to determine whether logging the auth info.
+ * LOG_AUTH=1, write the auth smtp info into syslog.
+ * 3. If mail from local, rcpt to local domain, user must auth before send data.
+ */
+#include <syslog.h>;
+
+#define SMTP_AUTH_SUCCESS     0
+#define SMTP_AUTH_FAILED      1
+
+int localauthd() {
+
+  if (rcpthosts(mailfrom.s, strlen(mailfrom.s)) && !authd) return 0;
+  return 1;
+}
+
+int smtp_auth_validfrom(from) char * from;
+{
+  stralloc tmplogin={0},mydefaultdomain={0};
+  char authusername;
+  int k, userlen;
+
+  if (!authd) return 1;
+
+  authusername = '\0';
+  userlen = k = str_len(smtpauthlogin);
+  k = byte_rchr(smtpauthlogin, k, '@');
+
+  if (k == userlen)
+  {
+    k = byte_rchr(smtpauthlogin, userlen, '%');
+
+    if (k == userlen)
+    {
+      if (control_readfile(&mydefaultdomain,"/var/qmail/control/me",1) != 1)
+      {
+        die_nomem();
+      }
+      if (!stralloc_copys(&tmplogin, smtpauthlogin) )
+      {
+        die_nomem();
+      }
+      if (!stralloc_cats(&tmplogin, "@") )
+      {
+        die_nomem();
+      }
+      if (!stralloc_cat(&tmplogin, &mydefaultdomain))
+      {
+        die_nomem();
+      }
+      strncpy(authusername, tmplogin.s, sizeof(authusername));
+      }
+      else
+      {
+        strcpy(authusername, smtpauthlogin);
+      authusername='@';
+      }
+  }
+  else {
+    strcpy(authusername, smtpauthlogin);
+  }
+  return !strcasecmp(from, authusername);
+}
+
+
+void auth_smtplog(authlogin, authresult)
+char * authlogin;
+int authresult;
+{
+  char * x = env_get("LOG_AUTH");
+
+  if (!x || (*x != '1')) return;
+
+  if (authresult == SMTP_AUTH_SUCCESS) {
+    syslog(LOG_MAIL | LOG_INFO, "smtp auth: user name is %s, success!", authlogin);
+  } else {
+    syslog(LOG_MAIL | LOG_INFO, "smtp auth: user name is %s, failed!", authlogin);
+  }
+}
+
static int smtpauth_getl(void) {
   int i;
   if (!stralloc_copys(&smtpauth, "")) return -1;
@@ -611,11 +699,14 @@
   wait_pid(&st, pid);
   if (wait_exitcode(st) == 0) {
     out("235 go ahead\r\n");
+    authd = 1;
+    auth_smtplog(smtpauthlogin, SMTP_AUTH_SUCCESS);
     flush();
     relayclient="";
     return;
   }
   sleep(2);
+  auth_smtplog(smtpauthlogin, SMTP_AUTH_FAILED);
   out("535 auth failure\r\n"); flush(); _exit(0);
   /* done */
}


另外,感謝erehw提供的空間可以下載這個patch。
http://gadfly.shanji.com/qmail-smtpd/


由於網頁上的代碼貼上去,格式有問題。
我就不再更新了。
請各位到以上的url下載。
或給我發信索要
《解決方案》

修正Qmail auth smtp中電子郵件地址任意的patch

好啊,可惜我沒在公司,周一到公司試一下~
《解決方案》

修正Qmail auth smtp中電子郵件地址任意的patch

gadfly真強啊!
看來做研發的就是厲害啊。
好文章。
《解決方案》

修正Qmail auth smtp中電子郵件地址任意的patch

暈,這上面的patch中,每行前面的空格都不顯示出來,複製下來肯定沒法用.

本站的ftp服務也關了.

如果手工複製下來,需要在每行沒有@或+開頭的代碼行前面加個空格,
就可以使用了.

如果覺得麻煩,可以給我發信,我把patch發給你們. gadfly@163.com

最好能有個上載的地方 :-(
《解決方案》

修正Qmail auth smtp中電子郵件地址任意的patch

peng老大過獎了,都在技術的苦海中苦苦掙扎. 兄弟們得攜手共進才是.

說不定還有不少問題呢
《解決方案》

修正Qmail auth smtp中電子郵件地址任意的patch

由於,有網友給我發信提了一些問題,我想還是到這裡說比較方便。

其實下面的問題,在我上面都說明了。沒辦法,我還是重點說一下

FAQ:

1. patch是基於哪一個auth的?
auth smtp有兩個patch,
分別在:http://members.elysium.pl/brush/qmail-smtpd-auth/
和www.nimh.org/hacks/qmail-smtpd.c。
基於的是後者。
如果你們auth smtp用的是前者,我提供的patch就不能直接使用。
等我有時間,在寫一個基於前者的patch吧。

2. patch執行后的結果是什麼?
暈,剛發現,www.nimh.org/hacks/qmail-smtpd.c又更新了,做的糟糕,沒有版本信息,我還不知道我針對的哪一個版本。下次我也要加上版本信息了。
現在只好說: iceblood版本: 正常執行成功的話,沒有任何輸出。
          最新版本(nimh.org主頁上的):輸出入下:

Hunk #1 succeeded at 252 (offset -52 lines).
Hunk #2 succeeded at 498 (offset -33 lines).
Hunk #3 succeeded at 633 (offset -58 lines).

但是還好,編譯出來的可以正常使用。

3.編譯出來的大小沒有區別?
這個和系統環境相關,不是絕對的。只要前兩個沒問題,你就看測試結果吧。

4.網頁上的patch不好用?
是的,因為格式的原因,手工複製下來,需要在每行沒有@或+開頭的代碼行前面加個空格, 就可以使用了.

如果覺得麻煩,可以給我發信,我把patch發給你們. gadfly@163.com

最好能有個上載的地方
《解決方案》

修正Qmail auth smtp中電子郵件地址任意的patch

gadfly真是強呀~~
有關資料可以上傳到linux版的FTP伺服器上~~~~
《解決方案》

修正Qmail auth smtp中電子郵件地址任意的patch

原帖由 "netloafer" 發表:


gadfly真是強呀~~
有關資料可以上傳到linux版的FTP伺服器上~~~~




呵呵,過獎了。

ftpserver我昨天試過了,還是沒有開。

聽說因為受攻擊,還影響了電信的其他業務,現在已經關閉了。只有另尋他途。
《解決方案》

修正Qmail auth smtp中電子郵件地址任意的patch

針對http://members.elysium.pl/brush/qmail-smtpd-auth/認證補丁再加上樓主所說的功能.

[ 非patch ]

首先你要打補丁,然後照下面的方法修改qmail-smtpd.c源代碼.

1.增加全局變數:
        stralloc authusername={0};   //全局變數用於記錄認證的email地址

2.在認證成功后將mailfrom存入上面的authusername中,也就是在函數
int authenticate(void)最後return 0;語句前插入下面的語句
         /*將用戶名存入全局變數 authusername*/
         if(!stralloc_copy(&authusername,&user)) return 1;

3.在void smtp_rcpt(arg)函數的if (relayclient) {語句后插入下面的語句.
             if(case_diffs(authusername.s,mailfrom.s)) {
                    err_noauth(); //認證用戶名與mailfrom中的地址不符
                    /*在這兒加入log代碼*/
                    return; //出錯返回
             }

===========================================================
一個問題:也不知道我想的對不對,在此提出與各位討論.

    但是我覺得你的patch和我上面的方法好象都有一個問題.就是如果遠程連接到伺服器25號埠,不通過認證。一樣可以向control/rcpthosts中列出的地址發郵件(就象是別的MTA與你對話一樣,別人的MTA不可能知道你的用戶名和密碼)因為不通過認證那麼relayclient將會是null,那麼就會進行rcpthosts的比較,如果rcpt 指定的email地址在rcpthosts中列出,qmail會接收並轉發該郵件,如果一個快速請求對一個慢速伺服器,那是什麼情況?
     有人想把用戶名與mailfrom地址比較的操作放在if(relayclient)這前,前面我說了,別人的MTA不會知道你的用戶名及密碼。如果你這樣作了,你將收不到郵件。
    我想就一台伺服器再打補丁也是白打。可以用兩台伺服器比較安全,分別打不同的補丁<也就是在前面提到的if(relayclient)處>;,一台專收,一台專發。兩台之間進行通信。這樣可能才能比較安全.

    不知道各位大蝦有什麼看法。不仿在此討論討論..
《解決方案》

修正Qmail auth smtp中電子郵件地址任意的patch

原帖由 "夜未眠"]  發表:


首先你要打補丁,然後照下面的方法修改qmail-smtpd.c源代碼.

1.增加全局變數:
        stralloc authusername={0};   //全局變數用於記錄認證的email地址

2.在認證成功后將mailfrom存入上面的authusern..........

就是如果遠程連接到伺服器25號埠,不通過認證。一樣可以向control/rcpthosts中列出的地址發郵件(就象是別的MTA與你對話一樣,別人的MTA不可能知道你的用戶名和密碼)因為不通過認證那麼relayclient將會是null,那麼就會進行rcpthosts的比較,如果rcpt 指定的email地址在rcpthosts中列出,qmail會接收並轉發該郵件,如果一個快速請求對一個慢速伺服器,那是什麼情況?


這個情況,是暫時沒辦法的啦。

[火星人 ] 修正Qmail auth smtp中電子郵件地址任意的patch已經有481次圍觀

http://coctec.com/docs/service/show-post-29928.html