qmail-smtpd.c continued:
#ifdef TLS
static int verify_cb(int ok, X509_STORE_CTX * ctx)
{
return(1);
}
#endif
void smtp_rcpt(arg) char *arg; {
rcptcounter++;
if (!seenmail) { err_wantmail(); return; }
if (checkrcptcount() == 1) { err_syntax(); return; }
if (!addrparse(arg)) { err_syntax(); return; }
if (addrrelay()) { err_relay(); return; }
if (flagbarf) { err_bmf(); return; }
if (flagbarfspf) { err_spf(); return; }
if (relayclient) {
--addr.len;
if (!stralloc_cats(&addr,relayclient)) die_nomem();
if (!stralloc_0(&addr)) die_nomem();
}
else
#ifndef TLS
if (!addrallowed()) { err_nogateway(); return; }
#else
if (!addrallowed())
{
if (ssl)
{ STACK_OF(X509_NAME) *sk;
X509 *peercert;
stralloc tlsclients = {0};
struct constmap maptlsclients;
int r;
SSL_set_verify(ssl,
SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
verify_cb);
if ((sk = SSL_load_client_CA_file("control/clientca.pem")) == NULL)
{ err_nogateway(); return; }
SSL_set_client_CA_list(ssl, sk);
if((control_readfile(&tlsclients,"control/tlsclients",0) != 1) ||
!constmap_init(&maptlsclients,tlsclients.s,tlsclients.len,0))
{ err_nogateway(); return; }
SSL_renegotiate(ssl);
SSL_do_handshake(ssl);
ssl->state = SSL_ST_ACCEPT;
SSL_do_handshake(ssl);
if ((r = SSL_get_verify_result(ssl)) != X509_V_OK)
{out("553 no valid cert for gatewaying: ");
out(X509_verify_cert_error_string(r));
out(" (#5.7.1)\r\n");
return;
}
if (peercert = SSL_get_peer_certificate(ssl))
{char emailAddress[256];
X509_NAME_get_text_by_NID(X509_get_subject_name(
SSL_get_peer_certificate(ssl)),
NID_pkcs9_emailAddress, emailAddress, 256); if (!stralloc_copys(&clientcert, emailAddress)) die_nomem();
if (!constmap(&maptlsclients,clientcert.s,clientcert.len))
{ err_nogwcert(); return; }
relayclient = "";
}
else { err_nogwcert(); return; }
}
else { err_nogateway(); return; }
}
#endif
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();
#ifdef TLS
r = ssl_timeoutread(timeout,fd,buf,len);
#else
r = timeoutread(timeout,fd,buf,len);
#endif
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 == 8) ++*hops;
if (pos < 8)
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);
}
}
void spfreceived()
{
stralloc sa = {0};
stralloc rcvd_spf = {0};
if (!spfbehavior || relayclient) return;
if (!stralloc_copys(&rcvd_spf, "Received-SPF: ")) die_nomem();
if (!spfinfo(&sa)) die_nomem();
if (!stralloc_cat(&rcvd_spf, &sa)) die_nomem();
if (!stralloc_append(&rcvd_spf, "\n")) die_nomem();
if (bytestooverflow) {
bytestooverflow -= rcvd_spf.len;
if (bytestooverflow <= 0) qmail_fail(&qqt);
}
qmail_put(&qqt,rcvd_spf.s,rcvd_spf.len);
}
//void spfreceived()
//{
// stralloc sa = {0};
// stralloc rcvd_spf = {0};
//
// if (!spfbehavior || relayclient) return;
//
// if (!stralloc_copys(&rcvd_spf, "Received-SPF: ")) die_nomem();
// if (!spfinfo(&sa)) die_nomem();
// if (!stralloc_cat(&rcvd_spf, &sa)) die_nomem();
// if (!stralloc_append(&rcvd_spf, "\n")) die_nomem();
// if (bytestooverflow) {
// bytestooverflow -= rcvd_spf.len;
// if (bytestooverflow <= 0) qmail_fail(&qqt);
// }
// qmail_put(&qqt,rcvd_spf.s,rcvd_spf.len);
//}
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;
#ifdef TLS
stralloc protocolinfo = {0};
#endif
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");
#ifdef TLS
if(ssl){
if (!stralloc_copys(&protocolinfo, SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)))) die_nomem();
if (!stralloc_catb(&protocolinfo, " encrypted SMTP", 15)) die_nomem();
if (clientcert.len){
if (!stralloc_catb(&protocolinfo," cert ", 6)) die_nomem();
if (!stralloc_catb(&protocolinfo,clientcert.s, clientcert.len)) die_nomem();
}
if (!stralloc_0(&protocolinfo)) die_nomem();
} else if (!stralloc_copyb(&protocolinfo,"SMTP",5)) die_nomem();
received(&qqt,protocolinfo.s,local,remoteip,remotehost,remoteinfo,case_diffs(remotehost,helohost.s) ? helohost.s : 0);
#else
received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo);
#endif
spfreceived();
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 == 'I') out("250 ok "); else if (*qqx == 'D') out("554 "); else out("451 ");
out(qqx + 1);
out("\r\n");
}
#ifdef TLS
static RSA *tmp_rsa_cb(ssl,export,keylength) SSL *ssl; int export; int keylength;
{
RSA* rsa;
BIO* in;
if (!export || keylength == 512)
if (in=BIO_new(BIO_s_file_internal()))
if (BIO_read_filename(in,"control/rsa512.pem") > 0)
if (rsa=PEM_read_bio_RSAPrivateKey(in,NULL,NULL,NULL))
return rsa;
return (RSA_generate_key(export?keylength:512,RSA_F4,NULL,NULL));
}
void smtp_tls(arg) char *arg;
{
SSL_CTX *ctx;
if (*arg)
{out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n");
return;}
SSL_library_init();
if(!(ctx=SSL_CTX_new(SSLv23_server_method())))
{out("454 TLS not available: unable to initialize ctx (#4.3.0)\r\n");
return;}
if(!SSL_CTX_use_RSAPrivateKey_file(ctx, "control/servercert.pem", SSL_FILETYPE_PEM))
{out("454 TLS not available: missing RSA private key (#4.3.0)\r\n");
return;}
if(!SSL_CTX_use_certificate_chain_file(ctx, "control/servercert.pem"))
{out("454 TLS not available: missing certificate (#4.3.0)\r\n");
return;}
SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_cb);
SSL_CTX_set_cipher_list(ctx,tlsserverciphers.s);
SSL_CTX_load_verify_locations(ctx, "control/clientca.pem",NULL);
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_cb);
out("220 ready for tls\r\n"); flush();
if(!(ssl=SSL_new(ctx))) die_read();
SSL_set_fd(ssl,0);
if(SSL_accept(ssl)<=0) die_read();
substdio_fdbuf(&ssout,SSL_write,ssl,ssoutbuf,sizeof(ssoutbuf));
remotehost = env_get("TCPREMOTEHOST");
if (!remotehost) remotehost = "unknown";
dohelo(remotehost);
}
#endif
char unique[FMT_ULONG + FMT_ULONG + 3];
static stralloc authin = {0};
static stralloc user = {0};
static stralloc pass = {0};
static stralloc resp = {0};
static stralloc slop = {0};
char *hostname;
char **childargs;
substdio ssup;
char upbuf[128];
int authd = 0;
int authgetl(void) {
int i;
if (!stralloc_copys(&authin, "")) die_nomem();
for (;

{
if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */
i = substdio_get(&ssin,authin.s + authin.len,1);
if (i != 1) die_read();
if (authin.s[authin.len] == '\n') break;
++authin.len;
}
if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len;
authin.s[authin.len] = 0;
if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); }
if (authin.len == 0) { return err_input(); }
return authin.len;
}
int authenticate(void)
{
int child;
int wstat;
int pi[2];
if (!stralloc_0(&user)) die_nomem();
if (!stralloc_0(&pass)) die_nomem();
if (!stralloc_0(&resp)) die_nomem();
if (fd_copy(2,1) == -1) return err_pipe();
close(3);
if (pipe(pi) == -1) return err_pipe();
if (pi[0] != 3) return err_pipe();
switch(child = fork()) {
case -1:
return err_fork();
case 0:
close(pi[1]);
sig_pipedefault();
execvp(*childargs, childargs);
_exit(1);
}
close(pi[0]);
substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof upbuf);
if (substdio_put(&ssup,user.s,user.len) == -1) return err_write();
if (substdio_put(&ssup,pass.s,pass.len) == -1) return err_write();
if (substdio_put(&ssup,resp.s,resp.len) == -1) return err_write();
if (substdio_flush(&ssup) == -1) return err_write();
close(pi[1]);
byte_zero(pass.s,pass.len);
byte_zero(upbuf,sizeof upbuf);
if (wait_pid(&wstat,child) == -1) return err_child();
if (wait_crashed(wstat)) return err_child();
if (wait_exitcode(wstat)) { sleep(5); return 1; } /* no */
return 0; /* yes */
}
int auth_login(arg) char *arg;
{
int r;
if (*arg) {
if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input();
}
else {
out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */
if (authgetl() < 0) return -1;
if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input();
}
if (r == -1) die_nomem();
out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */
if (authgetl() < 0) return -1;
if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input();
if (r == -1) die_nomem();
if (!user.len || !pass.len) return err_input();
return authenticate();
}
int auth_plain(arg) char *arg;
{
int r, id = 0;
if (*arg) {
if (r = b64decode(arg,str_len(arg),&slop) == 1) return err_input();
}
else {
out("334 \r\n"); flush();
if (authgetl() < 0) return -1;
if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input();
}
if (r == -1 || !stralloc_0(&slop)) die_nomem();
while (slop.s[id]) id++; /* ignore authorize-id */
if (slop.len > id + 1)
if (!stralloc_copys(&user,slop.s + id + 1)) die_nomem();
if (slop.len > id + user.len + 2)
if (!stralloc_copys(&pass,slop.s + id + user.len + 2)) die_nomem();
if (!user.len || !pass.len) return err_input();
return authenticate();
}
#ifdef AUTHCRAM
int auth_cram()
{
int i, r;
char *s;
s = unique;
s += fmt_uint(s,getpid());
*s++ = '.';
s += fmt_ulong(s,(unsigned long) now());
*s++ = '@';
*s++ = 0;
if (!stralloc_copys(&pass,"<")) die_nomem();
if (!stralloc_cats(&pass,unique)) die_nomem();
if (!stralloc_cats(&pass,hostname)) die_nomem();
if (!stralloc_cats(&pass,">")) die_nomem();
if (b64encode(&pass,&slop) < 0) die_nomem();
if (!stralloc_0(&slop)) die_nomem();
out("334 ");
out(slop.s);
out("\r\n");
flush();
if (authgetl() < 0) return -1;
if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input();
if (r == -1 || !stralloc_0(&slop)) die_nomem();
i = str_chr(slop.s,' ');
s = slop.s + i;
while (*s == ' ') ++s;
slop.s[i] = 0;
if (!stralloc_copys(&user,slop.s)) die_nomem();
if (!stralloc_copys(&resp,s)) die_nomem();
if (!user.len || !resp.len) return err_input();
return authenticate();
}
#endif
struct authcmd {
char *text;
int (*fun)();
} authcmds[] = {
{ "login", auth_login }
, { "plain", auth_plain }
#ifdef AUTHCRAM
, { "cram-md5", auth_cram }
#endif
, { 0, err_noauth }
};
void smtp_auth(arg)
char *arg;
{
int i;
char *cmd = arg;
if (!( useauth && (ssl||essl) ))
//if (!( useauth && ( ssl || essl || !forcetls ) ))
{
out("503 auth not available (#5.3.3)\r\n");
return;
}
if (authd) { err_authd(); return; }
if (seenmail) { err_authmail(); return; }
if (!stralloc_copys(&user,"")) die_nomem();
if (!stralloc_copys(&pass,"")) die_nomem();
if (!stralloc_copys(&resp,"")) die_nomem();
i = str_chr(cmd,' ');
arg = cmd + i;
while (*arg == ' ') ++arg;
cmd[i] = 0;
for (i = 0;authcmds[i].text;++i)
if (case_equals(authcmds[i].text,cmd)) break;
switch (authcmds[i].fun(arg)) {
case 0:
authd = 1;
relayclient = "";
remoteinfo = user.s;
if (!env_unset("TCPREMOTEINFO")) die_read();
if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem();
if (!env_unset("SMTP_AUTH_USER")) die_read();
if (!env_put2("SMTP_AUTH_USER",remoteinfo)) die_nomem();
out("235 ok, go ahead (#2.0.0)\r\n");
break;
case 1:
out("535 authorization failed (#5.7.0)\r\n");
}
}
struct commands smtpcommands[] = {
{ "rcpt", smtp_rcpt, 0 }
, { "mail", smtp_mail, 0 }
, { "data", smtp_data, flush }
, { "auth", smtp_auth, flush }
, { "quit", smtp_quit, flush }
, { "helo", smtp_helo, flush }
, { "ehlo", smtp_ehlo, flush }
, { "rset", smtp_rset, 0 }
, { "help", smtp_help, flush }
#ifdef TLS
, { "starttls", smtp_tls, flush }
#endif
, { "noop", err_noop, flush }
, { "vrfy", err_vrfy, flush }
, { 0, err_unimpl, flush }
} ;
void main(argc,argv)
int argc;
char **argv;
{
char *x ;
unsigned long u ;
if (argc>3)
{
hostname = argv[1];
childargs = argv + 2;
useauth = 1;
}
x = env_get("SSL");
if(x) { scan_ulong(x,&u); essl = u; }
#ifdef TLS
sig_alarmcatch(sigalrm);
#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();
}
int checkrcptcount() {
if (maxrcpt == -1) { return 0;}
else if (rcptcounter > maxrcpt ) { return 1;}
return 0;
}
//int checkrcptcount() {
// if (maxrcpt == -1) { return 0;}
// else if (rcptcounter > maxrcpt ) { return 1;}
// return 0;
//}
any ideas?
thanks