*** qmail-smtpd-orig.c Sun Jan 30 17:45:57 2000 --- qmail-smtpd.c Tue Oct 17 11:28:40 2000 *************** *** 1,4 **** --- 1,7 ---- #include "sig.h" + #include + #include + #include #include "readwrite.h" #include "stralloc.h" #include "substdio.h" *************** *** 16,21 **** --- 19,25 ---- #include "scan.h" #include "byte.h" #include "case.h" + #include "wait.h" #include "env.h" #include "now.h" #include "exit.h" *************** *** 25,30 **** --- 29,38 ---- #include "commands.h" #define MAXHOPS 100 + #define USE_SMTPAUTH + #define USE_OLD_GREETING + #define USE_NEW_GREETING + unsigned int databytes = 0; int timeout = 1200; *************** *** 45,50 **** --- 53,60 ---- 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_crash() { out("421 child crashed (#4.3.0)\r\n"); flush(); _exit(1); } + void die_fork() { out("421 unable to start checkpassword.\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); } *************** *** 86,93 **** 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; } --- 96,103 ---- 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; } *************** *** 101,107 **** { char *x; unsigned long u; ! if (control_init() == -1) die_control(); if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1) die_control(); --- 111,117 ---- { char *x; unsigned long u; ! if (control_init() == -1) die_control(); if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1) die_control(); *************** *** 116,127 **** 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"); --- 126,137 ---- 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"); *************** *** 146,152 **** struct ip_address ip; int flagesc; int flagquoted; ! terminator = '>'; i = str_chr(arg,'<'); if (arg[i]) --- 156,162 ---- struct ip_address ip; int flagesc; int flagquoted; ! terminator = '>'; i = str_chr(arg,'<'); if (arg[i]) *************** *** 229,235 **** } void smtp_ehlo(arg) char *arg; { ! smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); seenmail = 0; dohelo(arg); } void smtp_rset() --- 239,255 ---- } void smtp_ehlo(arg) char *arg; { ! smtp_greet("250-"); ! #ifdef USE_SMTPAUTH ! #ifdef USE_OLD_GREETING ! out("\r\n250-AUTH=LOGIN PLAIN"); ! #endif ! #ifdef USE_NEW_GREETING ! out("\r\n250-AUTH LOGIN PLAIN"); ! #endif ! #endif ! ! out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); seenmail = 0; dohelo(arg); } void smtp_rset() *************** *** 300,306 **** 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; --- 320,326 ---- 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; *************** *** 369,375 **** int hops; unsigned long qp; char *qqx; ! if (!seenmail) { err_wantmail(); return; } if (!rcptto.len) { err_wantrcpt(); return; } seenmail = 0; --- 389,395 ---- int hops; unsigned long qp; char *qqx; ! if (!seenmail) { err_wantmail(); return; } if (!rcptto.len) { err_wantrcpt(); return; } seenmail = 0; *************** *** 377,390 **** 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; } --- 397,410 ---- 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; } *************** *** 394,403 **** --- 414,779 ---- out("\r\n"); } + #ifdef USE_SMTPAUTH + static unsigned char authenticated=0; + 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[i]) + 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> 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 char **smtpauth_argv; + static char *auth_argv[4]; + static stralloc smtpauth = {0}; + static char smtpauthlogin[65]; + static char smtpauthpass[65]; + static char smtpauthtimestamp[65]; + + char unique[FMT_ULONG + FMT_ULONG + 3]; + + void base64encode(stralloc *input, stralloc *output) + { + int a=0,b=0,c=0; + int i, j; + int d, e, f, g; + + if (input->len == 0) return; + + for (j=i=0; i< input->len ; i += 3) + { + a=input->s[i]; + b= i+1 < input->len ? input->s[i+1]:0; + c= i+2 < input->len ? input->s[i+2]:0; + + d=base64_alphabet[ a >> 2 ]; + e=base64_alphabet[ ((a & 3 ) << 4) | (b >> 4)]; + f=base64_alphabet[ ((b & 15) << 2) | (c >> 6)]; + g=base64_alphabet[ c & 63 ]; + if (i + 1 >= input->len) f='='; + if (i + 2 >= input->len) g='='; + stralloc_append(output,&d); + stralloc_append(output,&e); + stralloc_append(output,&f); + stralloc_append(output,&g); + } + } + + 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 void smtpauth_authenticate(void) + { + int st, pid, fds[2]; + + 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_fork(); + case 0: close(fds[1]); + fd_copy(3,fds[0]); + flush(); + execvp(auth_argv[1], auth_argv+1); + die_nomem(); + }; + close(fds[0]); + write(fds[1], smtpauthlogin, str_len(smtpauthlogin)+1); + write(fds[1], smtpauthpass, str_len(smtpauthpass)+1); + if (str_len(smtpauthtimestamp)) + { + write(fds[1], smtpauthtimestamp, str_len(smtpauthtimestamp)+1); + } + close(fds[1]); + wait_pid(&st, pid); + + if (wait_crashed(st)) + die_crash(); + if (wait_exitcode(st) == 0) { + out("235 go ahead\r\n"); + flush(); + relayclient=""; + authenticated=1; + return; + } + sleep(2); + out("535 auth failure\r\n"); + flush(); + return; + } + + void smtp_auth(arg) char *arg; { + int ret,i,toolong,start; + char *helper; + /* netscape 4.5 sends AUTH LOGIN + 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 + */ + /* Here i've added support for other auth types. + + -brush@elysium.pl */ + if (!authenticated) + { + if ((ret=strncasecmp(arg,"login",5))==0) + { + 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(); + smtpauthtimestamp[0]=0; + auth_argv[1]=smtpauth_argv[1]; /* change checkpass prg */ + auth_argv[2]=smtpauth_argv[2]; /* change checkpass prg */ + auth_argv[3]=NULL; /* change checkpass prg */ + smtpauth_authenticate(); + return; + } + else if ((ret=strncasecmp(arg,"plain",5))==0) + { + static char smtpauthloginpass[200]; + + while (arg && *arg && *arg != ' ') arg++; + + /* pass over the space */ + while (arg && *arg && *arg == ' ') arg++; + + if (arg && *arg) + { + if(strlen(arg)*3/4 >= sizeof(smtpauthloginpass)) + { + out("535 input too long\r\n"); + flush(); + return; + } + /* here's the base64 encoded login/password */ + base64_dec_buffer(arg, smtpauthloginpass, sizeof(smtpauthloginpass)-1); + } else { + out("334 ok. go on.\r\n"); + flush(); + i=smtpauth_getl(); + if(i <= 0) + die_read(); + else if(i*3/4 >= sizeof(smtpauthloginpass)) + { + out("535 input too long\r\n"); + flush(); + return; + } else base64_dec_buffer(smtpauth.s, smtpauthloginpass, sizeof(smtpauthloginpass)-1); + } + smtpauthloginpass[sizeof(smtpauthloginpass)-1]=0; + start=strlen(smtpauthloginpass)+1; + if((start >= sizeof(smtpauthloginpass)) || (strlen(smtpauthloginpass+start) >= 65)) + { + out("535 malformed input\r\n"); + flush(); + return; + } + strcpy(smtpauthlogin,smtpauthloginpass+start); + + start+=strlen(smtpauthlogin)+1; + if((start >= sizeof(smtpauthloginpass)) || (strlen(smtpauthloginpass+start) >= 65)) + { + out("535 malformed input\r\n"); + flush(); + return; + } + strcpy(smtpauthpass,smtpauthloginpass+start); + + smtpauthtimestamp[0]=0; + auth_argv[1]=smtpauth_argv[1]; /* change checkpass prg */ + auth_argv[2]=smtpauth_argv[2]; /* change checkpass prg */ + auth_argv[3]=NULL; /* change checkpass prg */ + smtpauth_authenticate(); + return; + } + else if ((ret=strncasecmp(arg,"cram-md5",8))==0) + { + int r; + static stralloc me = {0}; + static stralloc greet = {0}; + static stralloc greetenc = {0}; + char *s; + r = control_readline(&me,"control/me"); + if (r != 1) + { + out("535 internal server error\r\n"); flush(); _exit(0); + } + for (r=0;r <= me.len;r++) + { + if (me.s[r]=='\n') + { + me.s[r]=0; + break; + } + } + + s = unique; + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + if (greet.len) + { + for (i=0;i"); + greet.s[greet.len]=0; // obscure fix but it works + stralloc_readyplus(&greet,3); + if (greetenc.len) + { + for (i=0;i 0) + { + s=calloc((size_t) strlen(smtpauth.s),(size_t)1); + base64_dec_buffer(smtpauth.s, s, strlen(smtpauth.s)); + } + helper=strtok(s," "); + if(helper!=NULL) + { + strncpy (smtpauthlogin,helper,64); + } + else + { + out("535 malformed input\r\n"); + return; + } + helper=strtok(NULL," "); + if(helper!=NULL) + { + strncpy (smtpauthtimestamp,helper,64); + } + else + { + out("535 malformed input\r\n"); + return; + } + strncpy (smtpauthpass,greet.s,64); + auth_argv[1]=smtpauth_argv[3]; /* change checkpass prg */ + auth_argv[2]=smtpauth_argv[4]; /* change checkpass prg */ + auth_argv[3]=NULL; /* change checkpass prg */ + smtpauth_authenticate(); + return; + } + else + { + out("504 auth type not supported\r\n"); + flush(); + return; + } + } + else + { + out("503 you are already authenticated\r\n"); + flush(); + return; + } + } + #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 } *************** *** 408,415 **** , { 0, err_unimpl, flush } } ; ! void main() { sig_pipeignore(); if (chdir(auto_qmail) == -1) die_control(); setup(); --- 784,794 ---- , { 0, err_unimpl, flush } } ; ! void main(argc,argv) int argc; char **argv; { + #ifdef USE_SMTPAUTH + smtpauth_argv = argv; + #endif sig_pipeignore(); if (chdir(auto_qmail) == -1) die_control(); setup();