- 论坛徽章:
- 0
|
分析nimh.org的qmail-smtpd.c。关于不auth能发信的问题。
就加了几句,每一句的后面有注释 // add by luojie.
搜索一下,很快就可以把他们找出来
**********************************************
//看了整个程序,如果客户端不发送auth 命令,程序好像就不执行 auth这一段。
//造成我机器上的现象就是如果客户端选择,不需要auth ,就可以发信
//如果选择了需要auth,而认证用户名,密码错误,不能发信,正确,可以发信
//by luojie ;flytod@21cn.com MSN:flyod@21cn.com
#include "sig.h"
#include <stdio.h>;
#include "readwrite.h"
#include "stralloc.h"
#include "substdio.h"
#include "alloc.h"
#include "auto_qmail.h"
#include "control.h"
#include "received.h"
#include "constmap.h"
#include "error.h"
#include "ipme.h"
#include "ip.h"
#include "qmail.h"
#include "str.h"
#include "fmt.h"
#include "scan.h"
#include "byte.h"
#include "case.h"
#include "wait.h"
#include "env.h"
#include "now.h"
#include "exit.h"
#include "rcpthosts.h"
#include "timeoutread.h"
#include "timeoutwrite.h"
#include "commands.h"
#define MAXHOPS 100
#define USE_SMTPAUTH
int smtpauth_ok = 0; //add by luojie,标记是否smtp验证成功
unsigned int databytes = 0;
int timeout = 1200;
int safewrite(fd,buf,len) int fd; char *buf; int len;
{
int r;
r = timeoutwrite(timeout,fd,buf,len);
if (r <= 0) _exit(1);
return r;
}
char ssoutbuf[512];
substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
void flush() { substdio_flush(&ssout); }
void out(s) char *s; { substdio_puts(&ssout,s); }
void die_read() { _exit(1); }
void die_alarm() { out("451 timeout (#4.4.2)\r\n" ; flush(); _exit(1); }
void die_nomem() { out("421 out of memory (#4.3.0)\r\n" ; flush(); _exit(1); }
void die_control() { out("421 unable to read controls (#4.3.0)\r\n" ; flush(); _exit(1); }
void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n" ; flush(); _exit(1); }
void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n" ; flush(); _exit(1); }
void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n" ; }
void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n" ; }
void err_smtpauth() { out("553 sorry, Authentication is required\r\n" ; } //add by luojie,输入需要验证的消息
void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n" ; }
void err_syntax() { out("555 syntax error (#5.5.4)\r\n" ; }
void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }
void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); }
void err_noop() { out("250 ok\r\n"); }
void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); }
void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); }
stralloc greeting = {0};
void smtp_greet(code) char *code;
{
substdio_puts(&ssout,code);
substdio_put(&ssout,greeting.s,greeting.len);
}
void smtp_help()
{
out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n");
}
void smtp_quit()
{
smtp_greet("221 "); out("\r\n"); flush(); _exit(0);
}
char *remoteip;
char *remotehost;
char *remoteinfo;
char *local;
char *relayclient;
stralloc helohost = {0};
char *fakehelo; /* pointer into helohost, or 0 */
void dohelo(arg) char *arg; {
if (!stralloc_copys(&helohost,arg)) die_nomem();
if (!stralloc_0(&helohost)) die_nomem();
fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0;
}
int liphostok = 0;
stralloc liphost = {0};
int bmfok = 0;
stralloc bmf = {0};
struct constmap mapbmf;
void setup()
{
char *x;
unsigned long u;
if (control_init() == -1) die_control();
if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1)
die_control();
liphostok = control_rldef(&liphost,"control/localiphost",1,(char *) 0);
if (liphostok == -1) die_control();
if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control();
if (timeout <= 0) timeout = 1;
if (rcpthosts_init() == -1) die_control();
bmfok = control_readfile(&bmf,"control/badmailfrom",0);
if (bmfok == -1) die_control();
if (bmfok)
if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem();
if (control_readint(&databytes,"control/databytes") == -1) die_control();
x = env_get("DATABYTES");
if (x) { scan_ulong(x,&u); databytes = u; }
if (!(databytes + 1)) --databytes;
remoteip = env_get("TCPREMOTEIP");
if (!remoteip) remoteip = "unknown";
local = env_get("TCPLOCALHOST");
if (!local) local = env_get("TCPLOCALIP");
if (!local) local = "unknown";
remotehost = env_get("TCPREMOTEHOST");
if (!remotehost) remotehost = "unknown";
remoteinfo = env_get("TCPREMOTEINFO");
relayclient = env_get("RELAYCLIENT");
dohelo(remotehost);
}
stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */
int addrparse(arg)
char *arg;
{
int i;
char ch;
char terminator;
struct ip_address ip;
int flagesc;
int flagquoted;
terminator = '>;';
i = str_chr(arg,'<');
if (arg)
arg += i + 1;
else { /* partner should go read rfc 821 */
terminator = ' ';
arg += str_chr(arg,':');
if (*arg == ':') ++arg;
while (*arg == ' ') ++arg;
}
/* strip source route */
if (*arg == '@') while (*arg) if (*arg++ == ':') break;
if (!stralloc_copys(&addr,"")) die_nomem();
flagesc = 0;
flagquoted = 0;
for (i = 0;ch = arg;++i) { /* copy arg to addr, stripping quotes */
if (flagesc) {
if (!stralloc_append(&addr,&ch)) die_nomem();
flagesc = 0;
}
else {
if (!flagquoted && (ch == terminator)) break;
switch(ch) {
case '\\': flagesc = 1; break;
case '"': flagquoted = !flagquoted; break;
default: if (!stralloc_append(&addr,&ch)) die_nomem();
}
}
}
/* could check for termination failure here, but why bother? */
if (!stralloc_append(&addr,"")) die_nomem();
if (liphostok) {
i = byte_rchr(addr.s,addr.len,'@');
if (i < addr.len) /* if not, partner should go read rfc 821 */
if (addr.s[i + 1] == '[')
if (!addr.s[i + 1 + ip_scanbracket(addr.s + i + 1,&ip)])
if (ipme_is(&ip)) {
addr.len = i + 1;
if (!stralloc_cat(&addr,&liphost)) die_nomem();
if (!stralloc_0(&addr)) die_nomem();
}
}
if (addr.len >; 900) return 0;
return 1;
}
int bmfcheck()
{
int j;
if (!bmfok) return 0;
if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1;
j = byte_rchr(addr.s,addr.len,'@');
if (j < addr.len)
if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1;
return 0;
}
int addrallowed()
{
int r;
r = rcpthosts(addr.s,str_len(addr.s));
if (r == -1) die_control();
return r;
}
int seenmail = 0;
int flagbarf; /* defined if seenmail */
stralloc mailfrom = {0};
stralloc rcptto = {0};
void smtp_helo(arg) char *arg;
{
smtp_greet("250 "); out("\r\n");
seenmail = 0; dohelo(arg);
}
void smtp_ehlo(arg) char *arg;
{
smtp_greet("250-");
#ifdef USE_SMTPAUTH
out("\r\n250-AUTH=LOGIN\r\n250-AUTH LOGIN");
#endif
out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
seenmail = 0; dohelo(arg);
}
void smtp_rset()
{
seenmail = 0;
out("250 flushed\r\n");
}
void smtp_mail(arg) char *arg;
{
if (!smtpauth_ok) { err_smtpauth(); smtp_quit(); } //add by luojie 接受mail 命令,如果没有smtp认证过,退出
if (!addrparse(arg)) { err_syntax(); return; }
flagbarf = bmfcheck();
seenmail = 1;
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");
}
void smtp_rcpt(arg) char *arg; {
if (!seenmail) { err_wantmail(); return; }
if (!addrparse(arg)) { err_syntax(); return; }
if (flagbarf) { err_bmf(); return; }
if (relayclient) {
--addr.len;
if (!stralloc_cats(&addr,relayclient)) die_nomem();
if (!stralloc_0(&addr)) die_nomem();
}
else
if (!addrallowed()) { err_nogateway(); return; }
if (!stralloc_cats(&rcptto,"T")) die_nomem();
if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
if (!stralloc_0(&rcptto)) die_nomem();
out("250 ok\r\n");
}
int saferead(fd,buf,len) int fd; char *buf; int len;
{
int r;
flush();
r = timeoutread(timeout,fd,buf,len);
if (r == -1) if (errno == error_timeout) die_alarm();
if (r <= 0) die_read();
return r;
}
char ssinbuf[1024];
substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
struct qmail qqt;
unsigned int bytestooverflow = 0;
void put(ch)
char *ch;
{
if (bytestooverflow)
if (!--bytestooverflow)
qmail_fail(&qqt);
qmail_put(&qqt,ch,1);
}
void blast(hops)
int *hops;
{
char ch;
int state;
int flaginheader;
int pos; /* number of bytes since most recent \n, if fih */
int flagmaybex; /* 1 if this line might match RECEIVED, if fih */
int flagmaybey; /* 1 if this line might match \r\n, if fih */
int flagmaybez; /* 1 if this line might match DELIVERED, if fih */
state = 1;
*hops = 0;
flaginheader = 1;
pos = 0; flagmaybex = flagmaybey = flagmaybez = 1;
for (;;) {
substdio_get(&ssin,&ch,1);
if (flaginheader) {
if (pos < 9) {
if (ch != "delivered"[pos]) if (ch != "DELIVERED"[pos]) flagmaybez = 0;
if (flagmaybez) if (pos == ++*hops;
if (pos < 
if (ch != "received"[pos]) if (ch != "RECEIVED"[pos]) flagmaybex = 0;
if (flagmaybex) if (pos == 7) ++*hops;
if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0;
if (flagmaybey) if (pos == 1) flaginheader = 0;
}
++pos;
if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; }
}
switch(state) {
case 0:
if (ch == '\n') straynewline();
if (ch == '\r') { state = 4; continue; }
break;
case 1: /* \r\n */
if (ch == '\n') straynewline();
if (ch == '.') { state = 2; continue; }
if (ch == '\r') { state = 4; continue; }
state = 0;
break;
case 2: /* \r\n + . */
if (ch == '\n') straynewline();
if (ch == '\r') { state = 3; continue; }
state = 0;
break;
case 3: /* \r\n + .\r */
if (ch == '\n') return;
put(".");
put("\r");
if (ch == '\r') { state = 4; continue; }
state = 0;
break;
case 4: /* + \r */
if (ch == '\n') { state = 1; break; }
if (ch != '\r') { put("\r"); state = 0; }
}
put(&ch);
}
}
char accept_buf[FMT_ULONG];
void acceptmessage(qp) unsigned long qp;
{
datetime_sec when;
when = now();
out("250 ok ");
accept_buf[fmt_ulong(accept_buf,(unsigned long) when)] = 0;
out(accept_buf);
out(" qp ");
accept_buf[fmt_ulong(accept_buf,qp)] = 0;
out(accept_buf);
out("\r\n");
}
void smtp_data() {
int hops;
unsigned long qp;
char *qqx;
if (!seenmail) { err_wantmail(); return; }
if (!rcptto.len) { err_wantrcpt(); return; }
seenmail = 0;
if (databytes) bytestooverflow = databytes + 1;
if (qmail_open(&qqt) == -1) { err_qqt(); return; }
qp = qmail_qp(&qqt);
out("354 go ahead\r\n");
received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo);
blast(&hops);
hops = (hops >;= MAXHOPS);
if (hops) qmail_fail(&qqt);
qmail_from(&qqt,mailfrom.s);
qmail_put(&qqt,rcptto.s,rcptto.len);
qqx = qmail_close(&qqt);
if (!*qqx) { acceptmessage(qp); return; }
if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; }
if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; }
if (*qqx == 'D') out("554 "); else out("451 ");
out(qqx + 1);
out("\r\n");
}
#ifdef USE_SMTPAUTH
static unsigned char *base64_alphabet =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
static int unbase64(ch) int ch; {
int i;
if (ch == '=') return 0;
for (i = 0; i < 64; i++)
if (ch == base64_alphabet)
return i;
return 0;
}
static int base64_dec_buffer(str,dst,len) const char *str;void *dst;int len;
{
int i, j, l;
unsigned char input[4], output[3], *result = (char *)dst;
if (str == 0)
return 0;
l = str_len(str);
if (dst == 0 || l >; len)
return (l / 4) * 3;
memset(dst,0,len);
for (i=j=0; i<l; i +=4) {
input[0] = unbase64(str);
input[1] = unbase64(str[i+1]);
input[2] = unbase64(str[i+2]);
input[3] = unbase64(str[i+3]);
output[0] = (input[0] << 2) | (input[1] >;>; 4);
output[1] = (input[1] << 4) | (input[2] >;>; 2);
output[2] = (input[2] << 6) | (input[3]);
result[j] = output[0];
if (str[i+1] == '=') return j+1;
result[j+1]=output[1];
if (str[i+2] == '=') return j+2;
result[j+2]=output[2];
j += 3;
}
return j;
}
static stralloc smtpauth = {0};
static char smtpauthlogin[65];
static char smtpauthpass[65];
static int smtpauth_getl(void) {
int i;
if (!stralloc_copys(&smtpauth, "")) return -1;
for (;;) {
if (!stralloc_readyplus(&smtpauth,1)) return -1;
i = substdio_get(&ssin, smtpauth.s + smtpauth.len, 1);
if (i != 1) return i;
if (smtpauth.s[smtpauth.len] == '\n') break;
++smtpauth.len;
}
if (smtpauth.len >; 0) if (smtpauth.s[smtpauth.len-1] == '\r') --smtpauth.len;
smtpauth.s[smtpauth.len] = 0;
return smtpauth.len;
}
static char **smtpauth_argv;
void smtp_auth(arg) char *arg; {
int st, pid, fds[2];
/* netscape 4.5 sends AUTH LOGIN <base64encodedusername>;
microsoft outlook express sends AUTH LOGIN
idea is simple
use an external program to test authority
if success, set 'RELAYCLIENT'
otherwise, let them know nicely (hangup)
note, i really don't like djb's coding style even though i'm using it here.
i think using spaces for tabs is bad.
-mrs.brisby@nimh.org
*/
while (arg && *arg && *arg != ' ') arg++;
/* pass over the space */
while (arg && *arg && *arg == ' ') arg++;
if (arg && *arg) {
/* here's the base64 encoded login */
base64_dec_buffer(arg, smtpauthlogin, sizeof(smtpauthlogin));
} else {
out("334 VXNlcm5hbWU6\r\n"); /* b64 <- 'Username:' */
flush();
if (smtpauth_getl() >; 0)
base64_dec_buffer(smtpauth.s, smtpauthlogin, sizeof(smtpauthlogin));
else
die_read();
}
out("334 UGFzc3dvcmQ6\r\n"); /* b64 <- 'Password:' */
flush();
if (smtpauth_getl() >; 0)
base64_dec_buffer(smtpauth.s, smtpauthpass, sizeof(smtpauthpass));
else
die_read();
if (pipe(fds)) {
out("535 pipe failure\r\n");
flush();
_exit(0);
}
/* spawn external program
external program should return '0' if it was successful,
submit: /bin/checkpassword /bin/true
*/
switch ((pid=fork())) {
case -1: die_nomem();
case 0: close(fds[1]);
fd_copy(3,fds[0]);
execvp(smtpauth_argv[1], smtpauth_argv+1);
die_nomem();
};
close(fds[0]);
write(fds[1], smtpauthlogin, str_len(smtpauthlogin)+1);
write(fds[1], smtpauthpass, str_len(smtpauthpass)+1);
close(fds[1]);
wait_pid(&st, pid);
if (wait_exitcode(st) == 0) {
smtpauth_ok = 1; //add by luojie ,标记验证通过
out("235 go ahead\r\n");
flush();
relayclient="";
return;
}
sleep(2);
out("535 auth failure\r\n"); flush(); _exit(0);
/* done */
}
#endif
struct commands smtpcommands[] = {
{ "rcpt", smtp_rcpt, 0 }
, { "mail", smtp_mail, 0 }
, { "data", smtp_data, flush }
#ifdef USE_SMTPAUTH
, { "auth", smtp_auth, flush }
#endif
, { "quit", smtp_quit, flush }
, { "helo", smtp_helo, flush }
, { "ehlo", smtp_ehlo, flush }
, { "rset", smtp_rset, 0 }
, { "help", smtp_help, flush }
, { "noop", err_noop, flush }
, { "vrfy", err_vrfy, flush }
, { 0, err_unimpl, flush }
} ;
void main(argc,argv) int argc; char **argv;
{
#ifdef USE_SMTPAUTH
char *u;
smtpauth_argv = argv;
#endif
sig_pipeignore();
if (chdir(auto_qmail) == -1) die_control();
setup();
if (ipme_init() != 1) die_ipme();
smtp_greet("220 ");
out(" ESMTP\r\n");
if (commands(&ssin,&smtpcommands) == 0) die_read();
die_nomem();
} |
|