10 #include "../smtp/y.tab.h"
21 int filterstate = ACCEPT;
46 String* startcmd(void);
47 int rejectcheck(void);
48 String* mailerpath(char*);
51 catchalarm(void *a, char *msg)
57 /* log alarms but continue */
58 if(strstr(msg, "alarm")){
59 if(senders.first && rcvers.first)
60 syslog(0, "smtpd", "note: %s->%s: %s", s_to_c(senders.first->p),
61 s_to_c(rcvers.first->p), msg);
63 syslog(0, "smtpd", "note: %s", msg);
67 /* kill the children if there are any */
74 /* override string error functions to do something reasonable */
76 s_error(char *f, char *status)
81 rerrstr(errbuf, sizeof(errbuf));
83 reply("452 out of memory %s: %s\r\n", f, errbuf);
85 reply("452 out of memory %s\r\n", errbuf);
86 syslog(0, "smtpd", "++Malloc failure %s [%s]", him, nci->rsys);
91 main(int argc, char **argv)
105 case 'n': /* log peer ip address */
108 case 'f': /* disallow relaying */
114 case 'h': /* default domain name */
117 case 'k': /* prohibited ip address */
122 case 'm': /* set mail command */
125 mailer = mailerpath(p);
128 rflag = 1; /* verify sender's domain */
130 case 's': /* save blocked messages */
143 fprint(2, "%s: the -t option is no longer supported, see -c\n", argv0);
144 tlscert = "/sys/lib/ssl/smtpd-cert.pem";
147 fprint(2, "usage: smtpd [-dfhrs] [-n net] [-c cert]\n");
151 nci = getnetconninfo(netdir, 0);
153 sysfatal("can't get remote system's address");
156 mailer = mailerpath("send");
160 snprint(buf, sizeof(buf), "%s/smtpd", UPASLOG);
161 if (open(buf, OWRITE) >= 0) {
163 fprint(2, "%d smtpd %s\n", getpid(), thedate());
168 Binit(&bin, 0, OREAD);
172 if(dom == 0 || dom[0] == 0)
173 dom = domainname_read();
174 if(dom == 0 || dom[0] == 0)
178 /* allow 45 minutes to parse the header */
179 atnotify(catchalarm, 1);
191 for(lp = l->first; lp; lp = next){
196 l->first = l->last = 0;
200 listadd(List *l, String *path)
204 lp = (Link *)malloc(sizeof(Link));
217 reply(char *fmt, ...)
219 char buf[SIZE], *out;
224 out = vseprint(buf, buf+SIZE, fmt, arg);
242 if(filterstate != DIALUP){
244 filterstate = ACCEPT;
252 reply("220 %s SMTP\r\n", dom);
256 hello(String *himp, int extended)
261 syslog(0, "smtpd", "%s from %s as %s", extended ? "ehlo" : "helo", nci->rsys, him);
265 if(strchr(him, '.') && nci && !trusted && fflag && strcmp(nci->rsys, nci->lsys) != 0){
267 * We don't care if he lies about who he is, but it is
268 * not okay to pretend to be us. Many viruses do this,
269 * just parroting back what we say in the greeting.
271 if(strcmp(him, dom) == 0)
273 for(mynames=sysnames_read(); mynames && *mynames; mynames++){
274 if(cistrcmp(*mynames, him) == 0){
276 syslog(0, "smtpd", "Hung up on %s; claimed to be %s",
278 reply("554 Liar!\r\n");
279 exits("client pretended to be us");
285 * it is never acceptable to claim to be "localhost",
286 * "localhost.localdomain" or "localhost.example.com"; only spammers
287 * do this. it should be unacceptable to claim any string that doesn't
288 * look like a domain name (e.g., has at least one dot in it), but
289 * Microsoft mail software gets this wrong.
291 if (strcmp(him, "localhost") == 0 ||
292 strcmp(him, "localhost.localdomain") == 0 ||
293 strcmp(him, "localhost.example.com") == 0)
295 if(strchr(him, '.') == 0 && nci != nil && strchr(nci->rsys, '.') != nil)
300 reply("250%c%s you are %s\r\n", extended ? '-' : ' ', dom, him);
303 reply("250-STARTTLS\r\n");
305 reply("250 AUTH CRAM-MD5 PLAIN LOGIN\r\n");
307 reply("250 AUTH CRAM-MD5\r\n");
315 static char *lastsender;
319 if (authenticate && !authenticated) {
321 reply("530 Authentication required\r\n");
324 if(him == 0 || *him == 0){
326 reply("503 Start by saying HELO, please.\r\n", s_to_c(path));
330 /* don't add the domain onto black holes or we will loop */
331 if(strchr(s_to_c(path), '!') == 0 && strcmp(s_to_c(path), "/dev/null") != 0){
335 s_append(s, s_to_c(path));
340 if(shellchars(s_to_c(path))){
342 reply("503 Bad character in sender address %s.\r\n", s_to_c(path));
347 * if the last sender address resulted in a rejection because the sending
348 * domain didn't exist and this sender has the same domain, reject immediately.
351 if (strncmp(lastsender, s_to_c(path), strlen(lastsender)) == 0){
352 filterstate = REFUSED;
354 reply("554 Sender domain must exist: %s\r\n", s_to_c(path));
357 free(lastsender); /* different sender domain */
362 * see if this ip address, domain name, user name or account is blocked
364 filterstate = blocked(path);
367 listadd(&senders, path);
368 reply("250 sender is %s\r\n", s_to_c(path));
371 enum { Rcpt, Domain, Ntoks };
373 typedef struct Sender Sender;
379 static Sender *sendlist, *sendlast;
380 static uchar rsysip[IPaddrlen];
385 int lnlen, nf, ok = 1;
386 char *line, *senderfile;
390 static int beenhere = 0;
396 fmtinstall('I', eipfmt);
397 parseip(rsysip, nci->rsys);
400 * we're sticking with a system-wide sender list because
401 * per-user lists would require fully resolving recipient
402 * addresses to determine which users they correspond to
403 * (barring syntactic conventions).
405 senderfile = smprint("%s/senders", UPASLIB);
406 sf = Bopen(senderfile, OREAD);
410 while ((line = Brdline(sf, '\n')) != nil) {
411 if (line[0] == '#' || line[0] == '\n')
413 lnlen = Blinelen(sf);
414 line[lnlen-1] = '\0'; /* clobber newline */
415 nf = tokenize(line, toks, nelem(toks));
416 if (nf != nelem(toks))
417 continue; /* malformed line */
419 snd = malloc(sizeof *snd);
421 sysfatal("out of memory: %r");
422 memset(snd, 0, sizeof *snd);
428 sendlast->next = snd;
430 snd->rcpt = strdup(toks[Rcpt]);
431 snd->domain = strdup(toks[Domain]);
438 * read (recipient, sender's DNS) pairs from /mail/lib/senders.
439 * Only allow mail to recipient from any of sender's IPs.
440 * A recipient not mentioned in the file is always permitted.
445 int mentioned = 0, matched = 0;
446 uchar dnsip[IPaddrlen];
448 Ndbtuple *nt, *next, *first;
451 for (snd = sendlist; snd != nil; snd = snd->next) {
452 if (strcmp(rcpt, snd->rcpt) != 0)
455 * see if this domain's ips match nci->rsys.
456 * if not, perhaps a later entry's domain will.
459 if (parseip(dnsip, snd->domain) != -1 &&
460 memcmp(rsysip, dnsip, IPaddrlen) == 0)
463 * NB: nt->line links form a circular list(!).
464 * we need to make one complete pass over it to free it all.
466 first = nt = dnsquery(nci->root, snd->domain, "ip");
470 if (strcmp(nt->attr, "ip") == 0 &&
471 parseip(dnsip, nt->val) != -1 &&
472 memcmp(rsysip, dnsip, IPaddrlen) == 0)
477 } while (nt != first);
486 receiver(String *path)
492 if(him == 0 || *him == 0){
494 reply("503 Start by saying HELO, please\r\n");
498 sender = s_to_c(senders.last->p);
500 sender = "<unknown>";
502 if(!recipok(s_to_c(path))){
504 syslog(0, "smtpd", "Disallowed %s (%s/%s) to blocked name %s",
505 sender, him, nci->rsys, s_to_c(path));
506 reply("550 %s ... user unknown\r\n", s_to_c(path));
510 if (!senderok(rcpt)) {
512 syslog(0, "smtpd", "Disallowed sending IP of %s (%s/%s) to %s",
513 sender, him, nci->rsys, rcpt);
514 reply("550 %s ... sending system not allowed\r\n", rcpt);
519 /* forwarding() can modify 'path' on loopback request */
520 if(filterstate == ACCEPT && (fflag && !authenticated) && forwarding(path)) {
521 syslog(0, "smtpd", "Bad Forward %s (%s/%s) (%s)",
522 s_to_c(senders.last->p), him, nci->rsys, s_to_c(path));
524 reply("550 we don't relay. send to your-path@[] for loopback.\r\n");
527 listadd(&rcvers, path);
528 reply("250 receiver is %s\r\n", s_to_c(path));
534 reply("221 Successful termination\r\n");
544 reply("502 TURN unimplemented\r\n");
552 reply("250 Stop wasting my time!\r\n");
562 reply("250 Read rfc821 and stop wasting my time\r\n");
573 if(shellchars(s_to_c(path))){
574 reply("503 Bad character in address %s.\r\n", s_to_c(path));
577 av[0] = s_to_c(mailer);
579 av[2] = s_to_c(path);
582 pp = noshell_proc_start(av, (stream *)0, outstream(), (stream *)0, 1, 0);
584 reply("450 We're busy right now, try later\r\n");
588 p = Brdline(pp->std[1]->fp, '\n');
590 reply("550 String does not match anything.\r\n");
592 p[Blinelen(pp->std[1]->fp)-1] = 0;
594 reply("550 String does not match anything.\r\n");
599 reply("250 %s <%s@%s>\r\n", s_to_c(path), p, dom);
608 * get a line that ends in crnl or cr, turn terminating crnl into a nl
613 getcrnl(String *s, Biobuf *fp)
660 for(l = senders.first; l; l = l->next){
661 if(l != senders.first)
662 s_append(from, ", ");
663 s_append(from, s_to_c(l->p));
665 for(l = rcvers.first; l; l = l->next){
666 if(l != rcvers.first)
668 s_append(to, s_to_c(l->p));
670 syslog(0, "smtpd", "[%s/%s] %s sent %d bytes to %s", him, nci->rsys,
671 s_to_c(from), nbytes, s_to_c(to));
685 for(l = rcvers.first; l; l = l->next)
686 syslog(0, "smtpd", "%s %s (%s/%s) (%s)", action,
687 s_to_c(senders.last->p), him, nci->rsys, s_to_c(l->p));
691 optoutall(int filterstate)
701 for(l = rcvers.first; l; l = l->next)
702 if(!optoutofspamfilter(s_to_c(l->p)))
718 * ignore the filterstate if the all the receivers prefer it.
720 filterstate = optoutall(filterstate);
722 switch (filterstate){
727 filename = dumpfile(s_to_c(senders.last->p));
729 s_append(cmd, "cat > ");
730 s_append(cmd, filename);
731 pp = proc_start(s_to_c(cmd), instream(), 0, outstream(), 0, 0);
736 reply("554 We don't accept mail from dial-up ports.\r\n");
738 * we could exit here, because we're never going to accept mail from this
739 * ip address, but it's unclear that RFC821 allows that. Instead we set
740 * the hardreject flag and go stupid.
747 reply("554-We don't accept mail from %s.\r\n", s_to_c(senders.last->p));
748 reply("554 Contact postmaster@%s for more information.\r\n", dom);
753 reply("554 Sender domain must exist: %s\r\n", s_to_c(senders.last->p));
759 reply("554-We have had an internal mailer error classifying your message.\r\n");
760 reply("554-Filterstate is %d\r\n", filterstate);
761 reply("554 Contact postmaster@%s for more information.\r\n", dom);
766 * now that all other filters have been passed,
767 * do grey-list processing.
773 * set up mail command
775 cmd = s_clone(mailer);
777 for(l = rcvers.first; l; l = l->next)
779 av = malloc(n*sizeof(char*));
781 reply("450 We're busy right now, try later\n");
787 av[n++] = s_to_c(cmd);
789 for(l = rcvers.first; l; l = l->next)
790 av[n++] = s_to_c(l->p);
795 pp = noshell_proc_start(av, instream(), outstream(), outstream(), 0, 0);
800 reply("450 We're busy right now, try later\n");
808 * print out a header line, expanding any domainless addresses into
812 bprintnode(Biobuf *b, Node *p)
815 if(p->addr && strchr(s_to_c(p->s), '@') == nil){
816 if(Bprint(b, "%s@%s", s_to_c(p->s), him) < 0)
819 if(Bwrite(b, s_to_c(p->s), s_len(p->s)) < 0)
823 if(Bputc(b, p->c) < 0)
827 if(Bwrite(b, s_to_c(p->white), s_len(p->white)) < 0)
835 for(; p; p = p->next)
842 * add waring headers of the form
843 * X-warning: <reason>
844 * for any headers that looked like they might be forged.
846 * return byte count of new headers
849 forgedheaderwarnings(void)
856 /* warn about envelope sender */
857 if(strcmp(s_to_c(senders.last->p), "/dev/null") != 0 && masquerade(senders.last->p, nil))
858 nbytes += Bprint(pp->std[0]->fp, "X-warning: suspect envelope domain\n");
861 * check Sender: field. If it's OK, ignore the others because this is an
862 * exploded mailing list.
864 for(f = firstfield; f; f = f->next){
865 if(f->node->c == SENDER){
866 if(masquerade(getaddr(f->node), him))
867 nbytes += Bprint(pp->std[0]->fp, "X-warning: suspect Sender: domain\n");
874 for(f = firstfield; f; f = f->next){
875 if(f->node->c == FROM && masquerade(getaddr(f->node), him))
876 nbytes += Bprint(pp->std[0]->fp, "X-warning: suspect From: domain\n");
882 * pipe message to mailer with the following transformations:
883 * - change \r\n into \n.
884 * - add sender's domain to any addrs with no domain
885 * - add a From: if none of From:, Sender:, or Replyto: exists
886 * - add a Received: line
889 pipemsg(int *byteswritten)
901 pipesig(&status); /* set status to 1 on write to closed pipe */
906 * add a 'From ' line as envelope
909 nbytes += Bprint(pp->std[0]->fp, "From %s %s remote from \n",
910 s_to_c(senders.first->p), thedate());
913 * add our own Received: stamp
915 nbytes += Bprint(pp->std[0]->fp, "Received: from %s ", him);
917 nbytes += Bprint(pp->std[0]->fp, "([%s]) ", nci->rsys);
918 nbytes += Bprint(pp->std[0]->fp, "by %s; %s\n", me, thedate());
921 * read first 16k obeying '.' escape. we're assuming
922 * the header will all be there.
926 while(sawdot == 0 && s_len(hdr) < 16*1024){
927 n = getcrnl(s_reset(line), &bin);
929 /* eof or error ends the message */
933 /* a line with only a '.' ends the message */
935 if(n == 2 && *cp == '.' && *(cp+1) == '\n'){
940 s_append(hdr, *cp == '.' ? cp+1 : cp);
946 yyinit(s_to_c(hdr), s_len(hdr));
950 * Look for masquerades. Let Sender: trump From: to allow mailing list
951 * forwarded messages.
954 nbytes += forgedheaderwarnings();
957 * add an orginator and/or destination if either is missing
960 if(senders.last == nil)
961 Bprint(pp->std[0]->fp, "From: /dev/null@%s\n", him);
963 Bprint(pp->std[0]->fp, "From: %s\n", s_to_c(senders.last->p));
965 if(destination == 0){
966 Bprint(pp->std[0]->fp, "To: ");
967 for(l = rcvers.first; l; l = l->next){
968 if(l != rcvers.first)
969 Bprint(pp->std[0]->fp, ", ");
970 Bprint(pp->std[0]->fp, "%s", s_to_c(l->p));
972 Bprint(pp->std[0]->fp, "\n");
976 * add sender's domain to any domainless addresses
977 * (to avoid forging local addresses)
980 for(f = firstfield; cp != nil && f; f = f->next){
981 for(p = f->node; cp != 0 && p; p = p->next)
982 cp = bprintnode(pp->std[0]->fp, p);
983 if(status == 0 && Bprint(pp->std[0]->fp, "\n") < 0){
984 piperror = "write error";
989 piperror = "sender domain";
993 /* write anything we read following the header */
994 if(status == 0 && Bwrite(pp->std[0]->fp, cp, s_to_c(hdr) + s_len(hdr) - cp) < 0){
995 piperror = "write error 2";
1001 * pass rest of message to mailer. take care of '.'
1005 n = getcrnl(s_reset(line), &bin);
1007 /* eof or error ends the message */
1011 /* a line with only a '.' ends the message */
1013 if(n == 2 && *cp == '.' && *(cp+1) == '\n'){
1018 if(status == 0 && Bwrite(pp->std[0]->fp, *cp == '.' ? cp+1 : cp, n) < 0){
1019 piperror = "write error 3";
1025 /* message did not terminate normally */
1026 snprint(pipbuf, sizeof pipbuf, "network eof: %r");
1032 if(status == 0 && Bflush(pp->std[0]->fp) < 0){
1033 piperror = "write error 4";
1036 stream_free(pp->std[0]);
1038 *byteswritten = nbytes;
1040 if(status && !piperror)
1041 piperror = "write on closed pipe";
1048 static char buf[128];
1051 strncpy(buf, x, sizeof(buf));
1052 buf[sizeof(buf)-1] = 0;
1053 p = strchr(buf, '\n');
1062 char *cp, *senddom, *user;
1067 who = s_to_c(senders.first->p);
1068 if(strcmp(who, "/dev/null") == 0){
1069 /* /dev/null can only send to one rcpt at a time */
1070 if(rcvers.first != rcvers.last){
1071 werrstr("rejected: /dev/null sending to multiple recipients");
1077 if(access("/mail/lib/validatesender", AEXEC) < 0)
1080 senddom = strdup(who);
1081 if((cp = strchr(senddom, '!')) == nil){
1082 werrstr("rejected: domainless sender %s", who);
1089 switch(pid = fork()){
1091 werrstr("deferred: fork: %r");
1095 * Could add an option with the remote IP address
1096 * to allow validatesender to implement SPF eventually.
1098 execl("/mail/lib/validatesender", "validatesender",
1099 "-n", nci->root, senddom, user, nil);
1100 _exits("exec validatesender: %r");
1108 werrstr("deferred: wait failed: %r");
1112 werrstr("deferred: wait returned wrong pid %d != %d", w->pid, pid);
1121 * skip over validatesender 143123132: prefix from rc.
1123 cp = strchr(w->msg, ':');
1124 if(cp && *(cp+1) == ' ')
1125 werrstr("%s", cp+2);
1127 werrstr("%s", w->msg);
1144 if(senders.last == 0){
1145 reply("503 Data without MAIL FROM:\r\n");
1149 if(rcvers.last == 0){
1150 reply("503 Data without RCPT TO:\r\n");
1154 if(sendermxcheck()){
1155 rerrstr(errx, sizeof errx);
1156 if(strncmp(errx, "rejected:", 9) == 0)
1157 reply("554 %s\r\n", errx);
1159 reply("450 %s\r\n", errx);
1160 for(l=rcvers.first; l; l=l->next)
1161 syslog(0, "smtpd", "[%s/%s] %s -> %s sendercheck: %s",
1162 him, nci->rsys, s_to_c(senders.first->p),
1163 s_to_c(l->p), errx);
1172 reply("354 Input message; end with <CRLF>.<CRLF>\r\n");
1175 * allow 145 more minutes to move the data
1179 status = pipemsg(&nbytes);
1182 * read any error messages
1185 while(s_read_line(pp->std[2]->fp, err))
1189 atnotify(catchalarm, 0);
1191 status |= proc_wait(pp);
1194 fprint(2, "%d status %ux\n", getpid(), status);
1196 fprint(2, "%d error %s\n", getpid(), s_to_c(err));
1200 * if process terminated abnormally, send back error message
1205 if(strstr(s_to_c(err), "mail refused")){
1206 syslog(0, "smtpd", "++[%s/%s] %s %s refused: %s", him, nci->rsys,
1207 s_to_c(senders.first->p), s_to_c(cmd), firstline(s_to_c(err)));
1210 syslog(0, "smtpd", "++[%s/%s] %s %s %s%s%sreturned %#q %s", him, nci->rsys,
1211 s_to_c(senders.first->p), s_to_c(cmd),
1212 piperror ? "error during pipemsg: " : "",
1213 piperror ? piperror : "",
1214 piperror ? "; " : "",
1215 pp->waitmsg->msg, firstline(s_to_c(err)));
1218 for(cp = s_to_c(err); ep = strchr(cp, '\n'); cp = ep){
1220 reply("%d-%s\r\n", code, cp);
1222 reply("%d mail process terminated abnormally\r\n", code);
1224 if(filterstate == BLOCKED)
1225 reply("554 we believe this is spam. we don't accept it.\r\n");
1227 if(filterstate == DELAY)
1228 reply("554 There will be a delay in delivery of this message.\r\n");
1230 reply("250 sent\r\n");
1244 * when we have blocked a transaction based on IP address, there is nothing
1245 * that the sender can do to convince us to take the message. after the
1246 * first rejection, some spammers continually RSET and give a new MAIL FROM:
1247 * filling our logs with rejections. rejectcheck() limits the retries and
1248 * swiftly rejects all further commands after the first 500-series message
1255 if(rejectcount > MAXREJECTS){
1256 syslog(0, "smtpd", "Rejected (%s/%s)", him, nci->rsys);
1257 reply("554 too many errors. transaction failed.\r\n");
1262 reply("554 We don't accept mail from dial-up ports.\r\n");
1268 * create abs path of the mailer
1280 s_append(s, UPASBIN);
1287 s_dec64(String *sin)
1294 * if the string is coming from smtpd.y, it will have no nl.
1295 * if it is coming from getcrnl below, it will have an nl.
1297 if (*(s_to_c(sin)+lin-1) == '\n')
1299 sout = s_newalloc(lin+1);
1300 lout = dec64((uchar *)s_to_c(sout), lin, s_to_c(sin), lin);
1305 sout->ptr = sout->base + lout;
1317 conn = mallocz(sizeof *conn, 1);
1318 cert = readcert(tlscert, &certlen);
1319 if (conn == nil || cert == nil) {
1322 reply("454 TLS not available\r\n");
1325 reply("220 Go ahead make my day\r\n");
1327 conn->certlen = certlen;
1328 fd = tlsServer(Bfildes(&bin), conn);
1332 syslog(0, "smtpd", "TLS start-up failed with %s", him);
1334 /* force the client to hang up */
1335 close(Bfildes(&bin)); /* probably fd 0 */
1337 exits("tls failed");
1340 Binit(&bin, fd, OREAD);
1342 fprint(2, "dup of %d failed: %r\n", fd);
1343 passwordinclear = 1;
1344 syslog(0, "smtpd", "started TLS with %s", him);
1348 auth(String *mech, String *resp)
1350 Chalstate *chs = nil;
1352 String *s_resp1_64 = nil;
1353 String *s_resp2_64 = nil;
1354 String *s_resp1 = nil;
1355 String *s_resp2 = nil;
1356 char *scratch = nil;
1362 syslog(0, "smtpd", "auth(%s, %s) from %s", s_to_c(mech),
1363 "(protected)", him);
1365 if (authenticated) {
1368 reply("503 Bad sequence of commands\r\n");
1371 if (cistrcmp(s_to_c(mech), "plain") == 0) {
1373 if (!passwordinclear) {
1375 reply("538 Encryption required for requested authentication mechanism\r\n");
1379 if (s_resp1_64 == nil) {
1381 s_resp1_64 = s_new();
1382 if (getcrnl(s_resp1_64, &bin) <= 0) {
1386 s_resp1 = s_dec64(s_resp1_64);
1387 if (s_resp1 == nil) {
1389 reply("501 Cannot decode base64\r\n");
1392 memset(s_to_c(s_resp1_64), 'X', s_len(s_resp1_64));
1393 user = (s_to_c(s_resp1) + strlen(s_to_c(s_resp1)) + 1);
1394 pass = user + (strlen(user) + 1);
1395 ai = auth_userpasswd(user, pass);
1396 authenticated = ai != nil;
1397 memset(pass, 'X', strlen(pass));
1400 else if (cistrcmp(s_to_c(mech), "login") == 0) {
1402 if (!passwordinclear) {
1404 reply("538 Encryption required for requested authentication mechanism\r\n");
1408 reply("334 VXNlcm5hbWU6\r\n");
1409 s_resp1_64 = s_new();
1410 if (getcrnl(s_resp1_64, &bin) <= 0)
1413 reply("334 UGFzc3dvcmQ6\r\n");
1414 s_resp2_64 = s_new();
1415 if (getcrnl(s_resp2_64, &bin) <= 0)
1417 s_resp1 = s_dec64(s_resp1_64);
1418 s_resp2 = s_dec64(s_resp2_64);
1419 memset(s_to_c(s_resp2_64), 'X', s_len(s_resp2_64));
1420 if (s_resp1 == nil || s_resp2 == nil) {
1422 reply("501 Cannot decode base64\r\n");
1425 ai = auth_userpasswd(s_to_c(s_resp1), s_to_c(s_resp2));
1426 authenticated = ai != nil;
1427 memset(s_to_c(s_resp2), 'X', s_len(s_resp2));
1430 reply("235 Authentication successful\r\n");
1433 reply("535 Authentication failed\r\n");
1437 else if (cistrcmp(s_to_c(mech), "cram-md5") == 0) {
1442 chs = auth_challenge("proto=cram role=server");
1445 reply("501 Couldn't get CRAM-MD5 challenge\r\n");
1448 scratch = malloc(chs->nchal * 2 + 1);
1449 chal64n = enc64(scratch, chs->nchal * 2, (uchar *)chs->chal, chs->nchal);
1450 scratch[chal64n] = 0;
1451 reply("334 %s\r\n", scratch);
1452 s_resp1_64 = s_new();
1453 if (getcrnl(s_resp1_64, &bin) <= 0)
1455 s_resp1 = s_dec64(s_resp1_64);
1456 if (s_resp1 == nil) {
1458 reply("501 Cannot decode base64\r\n");
1461 /* should be of form <user><space><response> */
1462 resp = s_to_c(s_resp1);
1463 t = strchr(resp, ' ');
1466 reply("501 Poorly formed CRAM-MD5 response\r\n");
1472 chs->nresp = strlen(t);
1473 ai = auth_response(chs);
1474 authenticated = ai != nil;
1478 reply("501 Unrecognised authentication type %s\r\n", s_to_c(mech));