📄 smtpd.c
字号:
* pass rest of message to mailer. take care of '.' * escapes. */ while(sawdot == 0){ n = getcrnl(s_reset(line), &bin); /* eof or error ends the message */ if(n <= 0) break; /* a line with only a '.' ends the message */ cp = s_to_c(line); if(n == 2 && *cp == '.' && *(cp+1) == '\n'){ sawdot = 1; break; } nbytes += n; if(status == 0 && Bwrite(pp->std[0]->fp, *cp == '.' ? cp+1 : cp, n) < 0){ piperror = "write error 3"; status = 1; } } s_free(line); if(sawdot == 0){ /* message did not terminate normally */ snprint(pipbuf, sizeof pipbuf, "network eof: %r"); piperror = pipbuf; syskillpg(pp->pid); status = 1; } if(status == 0 && Bflush(pp->std[0]->fp) < 0){ piperror = "write error 4"; status = 1; } stream_free(pp->std[0]); pp->std[0] = 0; *byteswritten = nbytes; pipesigoff(); if(status && !piperror) piperror = "write on closed pipe"; return status;}char*firstline(char *x){ static char buf[128]; char *p; strncpy(buf, x, sizeof(buf)); buf[sizeof(buf)-1] = 0; p = strchr(buf, '\n'); if(p) *p = 0; return buf;}intsendermxcheck(void){ char *cp, *senddom, *user; char *who; int pid; Waitmsg *w; who = s_to_c(senders.first->p); if(strcmp(who, "/dev/null") == 0){ /* /dev/null can only send to one rcpt at a time */ if(rcvers.first != rcvers.last){ werrstr("rejected: /dev/null sending to multiple recipients"); return -1; } return 0; } if(access("/mail/lib/validatesender", AEXEC) < 0) return 0; senddom = strdup(who); if((cp = strchr(senddom, '!')) == nil){ werrstr("rejected: domainless sender %s", who); free(senddom); return -1; } *cp++ = 0; user = cp; switch(pid = fork()){ case -1: werrstr("deferred: fork: %r"); return -1; case 0: /* * Could add an option with the remote IP address * to allow validatesender to implement SPF eventually. */ execl("/mail/lib/validatesender", "validatesender", "-n", nci->root, senddom, user, nil); _exits("exec validatesender: %r"); default: break; } free(senddom); w = wait(); if(w == nil){ werrstr("deferred: wait failed: %r"); return -1; } if(w->pid != pid){ werrstr("deferred: wait returned wrong pid %d != %d", w->pid, pid); free(w); return -1; } if(w->msg[0] == 0){ free(w); return 0; } /* * skip over validatesender 143123132: prefix from rc. */ cp = strchr(w->msg, ':'); if(cp && *(cp+1) == ' ') werrstr("%s", cp+2); else werrstr("%s", w->msg); free(w); return -1;}voiddata(void){ String *cmd; String *err; int status, nbytes; char *cp, *ep; char errx[ERRMAX]; Link *l; if(rejectcheck()) return; if(senders.last == 0){ reply("503 Data without MAIL FROM:\r\n"); rejectcount++; return; } if(rcvers.last == 0){ reply("503 Data without RCPT TO:\r\n"); rejectcount++; return; } if(!trusted && sendermxcheck()){ rerrstr(errx, sizeof errx); if(strncmp(errx, "rejected:", 9) == 0) reply("554 %s\r\n", errx); else reply("450 %s\r\n", errx); for(l=rcvers.first; l; l=l->next) syslog(0, "smtpd", "[%s/%s] %s -> %s sendercheck: %s", him, nci->rsys, s_to_c(senders.first->p), s_to_c(l->p), errx); rejectcount++; return; } cmd = startcmd(); if(cmd == 0) return; reply("354 Input message; end with <CRLF>.<CRLF>\r\n"); /* * allow 145 more minutes to move the data */ alarm(145*60*1000); status = pipemsg(&nbytes); /* * read any error messages */ err = s_new(); while(s_read_line(pp->std[2]->fp, err)) ; alarm(0); atnotify(catchalarm, 0); status |= proc_wait(pp); if(debug){ seek(2, 0, 2); fprint(2, "%d status %ux\n", getpid(), status); if(*s_to_c(err)) fprint(2, "%d error %s\n", getpid(), s_to_c(err)); } /* * if process terminated abnormally, send back error message */ if(status){ int code; if(strstr(s_to_c(err), "mail refused")){ syslog(0, "smtpd", "++[%s/%s] %s %s refused: %s", him, nci->rsys, s_to_c(senders.first->p), s_to_c(cmd), firstline(s_to_c(err))); code = 554; } else { syslog(0, "smtpd", "++[%s/%s] %s %s %s%s%sreturned %#q %s", him, nci->rsys, s_to_c(senders.first->p), s_to_c(cmd), piperror ? "error during pipemsg: " : "", piperror ? piperror : "", piperror ? "; " : "", pp->waitmsg->msg, firstline(s_to_c(err))); code = 450; } for(cp = s_to_c(err); ep = strchr(cp, '\n'); cp = ep){ *ep++ = 0; reply("%d-%s\r\n", code, cp); } reply("%d mail process terminated abnormally\r\n", code); } else { /* * if a message appeared on stderr, despite good status, * log it. this can happen if rewrite.in contains a bad * r.e., for example. */ if(*s_to_c(err)) syslog(0, "smtpd", "%s returned good status, but said: %s", s_to_c(mailer), s_to_c(err)); if(filterstate == BLOCKED) reply("554 we believe this is spam. we don't accept it.\r\n"); else if(filterstate == DELAY) reply("554 There will be a delay in delivery of this message.\r\n"); else { reply("250 sent\r\n"); logcall(nbytes); } } proc_free(pp); pp = 0; s_free(cmd); s_free(err); listfree(&senders); listfree(&rcvers);}/* * when we have blocked a transaction based on IP address, there is nothing * that the sender can do to convince us to take the message. after the * first rejection, some spammers continually RSET and give a new MAIL FROM: * filling our logs with rejections. rejectcheck() limits the retries and * swiftly rejects all further commands after the first 500-series message * is issued. */intrejectcheck(void){ if(rejectcount > MAXREJECTS){ syslog(0, "smtpd", "Rejected (%s/%s)", him, nci->rsys); reply("554 too many errors. transaction failed.\r\n"); exits("errcount"); } if(hardreject){ rejectcount++; reply("554 We don't accept mail from dial-up ports.\r\n"); } return hardreject;}/* * create abs path of the mailer */String*mailerpath(char *p){ String *s; if(p == nil) return nil; if(*p == '/') return s_copy(p); s = s_new(); s_append(s, UPASBIN); s_append(s, "/"); s_append(s, p); return s;}String *s_dec64(String *sin){ String *sout; int lin, lout; lin = s_len(sin); /* * if the string is coming from smtpd.y, it will have no nl. * if it is coming from getcrnl below, it will have an nl. */ if (*(s_to_c(sin)+lin-1) == '\n') lin--; sout = s_newalloc(lin+1); lout = dec64((uchar *)s_to_c(sout), lin, s_to_c(sin), lin); if (lout < 0) { s_free(sout); return nil; } sout->ptr = sout->base + lout; s_terminate(sout); return sout;}voidstarttls(void){ uchar *cert; int certlen, fd; TLSconn *conn; if (tlscert == nil) { reply("454 TLS not available\r\n"); return; } conn = mallocz(sizeof *conn, 1); cert = readcert(tlscert, &certlen); if (conn == nil || cert == nil) { if (conn != nil) free(conn); reply("454 TLS not available\r\n"); return; } reply("220 Go ahead make my day\r\n"); conn->cert = cert; conn->certlen = certlen; fd = tlsServer(Bfildes(&bin), conn); if (fd < 0) { free(cert); free(conn); syslog(0, "smtpd", "TLS start-up failed with %s", him); /* force the client to hang up */ close(Bfildes(&bin)); /* probably fd 0 */ close(1); exits("tls failed"); } Bterm(&bin); Binit(&bin, fd, OREAD); if (dup(fd, 1) < 0) fprint(2, "dup of %d failed: %r\n", fd); passwordinclear = 1; syslog(0, "smtpd", "started TLS with %s", him);}voidauth(String *mech, String *resp){ Chalstate *chs = nil; AuthInfo *ai = nil; String *s_resp1_64 = nil; String *s_resp2_64 = nil; String *s_resp1 = nil; String *s_resp2 = nil; char *scratch = nil; char *user, *pass; if (rejectcheck()) goto bomb_out; syslog(0, "smtpd", "auth(%s, %s) from %s", s_to_c(mech), "(protected)", him); if (authenticated) { bad_sequence: rejectcount++; reply("503 Bad sequence of commands\r\n"); goto bomb_out; } if (cistrcmp(s_to_c(mech), "plain") == 0) { if (!passwordinclear) { rejectcount++; reply("538 Encryption required for requested authentication mechanism\r\n"); goto bomb_out; } s_resp1_64 = resp; if (s_resp1_64 == nil) { reply("334 \r\n"); s_resp1_64 = s_new(); if (getcrnl(s_resp1_64, &bin) <= 0) { goto bad_sequence; } } s_resp1 = s_dec64(s_resp1_64); if (s_resp1 == nil) { rejectcount++; reply("501 Cannot decode base64\r\n"); goto bomb_out; } memset(s_to_c(s_resp1_64), 'X', s_len(s_resp1_64)); user = (s_to_c(s_resp1) + strlen(s_to_c(s_resp1)) + 1); pass = user + (strlen(user) + 1); ai = auth_userpasswd(user, pass); authenticated = ai != nil; memset(pass, 'X', strlen(pass)); goto windup; } else if (cistrcmp(s_to_c(mech), "login") == 0) { if (!passwordinclear) { rejectcount++; reply("538 Encryption required for requested authentication mechanism\r\n"); goto bomb_out; } if (resp == nil) { reply("334 VXNlcm5hbWU6\r\n"); s_resp1_64 = s_new(); if (getcrnl(s_resp1_64, &bin) <= 0) goto bad_sequence; } reply("334 UGFzc3dvcmQ6\r\n"); s_resp2_64 = s_new(); if (getcrnl(s_resp2_64, &bin) <= 0) goto bad_sequence; s_resp1 = s_dec64(s_resp1_64); s_resp2 = s_dec64(s_resp2_64); memset(s_to_c(s_resp2_64), 'X', s_len(s_resp2_64)); if (s_resp1 == nil || s_resp2 == nil) { rejectcount++; reply("501 Cannot decode base64\r\n"); goto bomb_out; } ai = auth_userpasswd(s_to_c(s_resp1), s_to_c(s_resp2)); authenticated = ai != nil; memset(s_to_c(s_resp2), 'X', s_len(s_resp2)); windup: if (authenticated) reply("235 Authentication successful\r\n"); else { rejectcount++; reply("535 Authentication failed\r\n"); } goto bomb_out; } else if (cistrcmp(s_to_c(mech), "cram-md5") == 0) { char *resp; int chal64n; char *t; chs = auth_challenge("proto=cram role=server"); if (chs == nil) { rejectcount++; reply("501 Couldn't get CRAM-MD5 challenge\r\n"); goto bomb_out; } scratch = malloc(chs->nchal * 2 + 1); chal64n = enc64(scratch, chs->nchal * 2, (uchar *)chs->chal, chs->nchal); scratch[chal64n] = 0; reply("334 %s\r\n", scratch); s_resp1_64 = s_new(); if (getcrnl(s_resp1_64, &bin) <= 0) goto bad_sequence; s_resp1 = s_dec64(s_resp1_64); if (s_resp1 == nil) { rejectcount++; reply("501 Cannot decode base64\r\n"); goto bomb_out; } /* should be of form <user><space><response> */ resp = s_to_c(s_resp1); t = strchr(resp, ' '); if (t == nil) { rejectcount++; reply("501 Poorly formed CRAM-MD5 response\r\n"); goto bomb_out; } *t++ = 0; chs->user = resp; chs->resp = t; chs->nresp = strlen(t); ai = auth_response(chs); authenticated = ai != nil; goto windup; } rejectcount++; reply("501 Unrecognised authentication type %s\r\n", s_to_c(mech));bomb_out: if (ai) auth_freeAI(ai); if (chs) auth_freechal(chs); if (scratch) free(scratch); if (s_resp1) s_free(s_resp1); if (s_resp2) s_free(s_resp2); if (s_resp1_64) s_free(s_resp1_64); if (s_resp2_64) s_free(s_resp2_64);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -