11 #include "../smtp/rfc822.tab.h"
22 int filterstate = ACCEPT;
47 String* startcmd(void);
48 int rejectcheck(void);
49 String* mailerpath(char*);
52 catchalarm(void *a, char *msg)
58 /* log alarms but continue */
59 if(strstr(msg, "alarm")){
60 if(senders.first && rcvers.first)
61 syslog(0, "smtpd", "note: %s->%s: %s", s_to_c(senders.first->p),
62 s_to_c(rcvers.first->p), msg);
64 syslog(0, "smtpd", "note: %s", msg);
68 /* kill the children if there are any */
75 /* override string error functions to do something reasonable */
77 s_error(char *f, char *status)
82 rerrstr(errbuf, sizeof(errbuf));
84 reply("452 out of memory %s: %s\r\n", f, errbuf);
86 reply("452 out of memory %s\r\n", errbuf);
87 syslog(0, "smtpd", "++Malloc failure %s [%s]", him, nci->rsys);
88 threadexitsall(status);
92 threadmain(int argc, char **argv)
106 case 'n': /* log peer ip address */
109 case 'f': /* disallow relaying */
115 case 'h': /* default domain name */
118 case 'k': /* prohibited ip address */
123 case 'm': /* set mail command */
126 mailer = mailerpath(p);
129 rflag = 1; /* verify sender's domain */
131 case 's': /* save blocked messages */
141 fprint(2, "tls is not available\n");
142 threadexitsall("no tls");
146 fprint(2, "%s: the -t option is no longer supported, see -c\n", argv0);
147 tlscert = "/sys/lib/ssl/smtpd-cert.pem";
150 fprint(2, "usage: smtpd [-dfhrs] [-n net] [-c cert]\n");
151 threadexitsall("usage");
154 nci = getnetconninfo(netdir, 0);
156 sysfatal("can't get remote system's address");
159 mailer = mailerpath("send");
163 snprint(buf, sizeof(buf), "%s/smtpd.db", UPASLOG);
164 if (open(buf, OWRITE) >= 0) {
166 fprint(2, "%d smtpd %s\n", getpid(), thedate());
171 Binit(&bin, 0, OREAD);
175 if(dom == 0 || dom[0] == 0)
176 dom = domainname_read();
177 if(dom == 0 || dom[0] == 0)
181 /* allow 45 minutes to parse the header */
182 atnotify(catchalarm, 1);
194 for(lp = l->first; lp; lp = next){
199 l->first = l->last = 0;
203 listadd(List *l, String *path)
207 lp = (Link *)malloc(sizeof(Link));
220 reply(char *fmt, ...)
222 char buf[SIZE], *out;
227 out = vseprint(buf, buf+SIZE, fmt, arg);
245 if(filterstate != DIALUP){
247 filterstate = ACCEPT;
255 reply("220 %s SMTP\r\n", dom);
259 hello(String *himp, int extended)
264 syslog(0, "smtpd", "%s from %s as %s", extended ? "ehlo" : "helo", nci->rsys, him);
268 if(strchr(him, '.') && nci && !trusted && fflag && strcmp(nci->rsys, nci->lsys) != 0){
270 * We don't care if he lies about who he is, but it is
271 * not okay to pretend to be us. Many viruses do this,
272 * just parroting back what we say in the greeting.
274 if(strcmp(him, dom) == 0)
276 for(mynames=sysnames_read(); mynames && *mynames; mynames++){
277 if(cistrcmp(*mynames, him) == 0){
279 syslog(0, "smtpd", "Hung up on %s; claimed to be %s",
281 reply("554 Liar!\r\n");
282 threadexitsall("client pretended to be us");
288 * it is never acceptable to claim to be "localhost",
289 * "localhost.localdomain" or "localhost.example.com"; only spammers
290 * do this. it should be unacceptable to claim any string that doesn't
291 * look like a domain name (e.g., has at least one dot in it), but
292 * Microsoft mail software gets this wrong.
294 if (strcmp(him, "localhost") == 0 ||
295 strcmp(him, "localhost.localdomain") == 0 ||
296 strcmp(him, "localhost.example.com") == 0)
298 if(strchr(him, '.') == 0 && nci != nil && strchr(nci->rsys, '.') != nil)
303 reply("250%c%s you are %s\r\n", extended ? '-' : ' ', dom, him);
306 reply("250-STARTTLS\r\n");
308 reply("250 AUTH CRAM-MD5 PLAIN LOGIN\r\n");
310 reply("250 AUTH CRAM-MD5\r\n");
318 static char *lastsender;
322 if (authenticate && !authenticated) {
324 reply("530 Authentication required\r\n");
327 if(him == 0 || *him == 0){
329 reply("503 Start by saying HELO, please.\r\n", s_to_c(path));
333 /* don't add the domain onto black holes or we will loop */
334 if(strchr(s_to_c(path), '!') == 0 && strcmp(s_to_c(path), "/dev/null") != 0){
338 s_append(s, s_to_c(path));
343 if(shellchars(s_to_c(path))){
345 reply("503 Bad character in sender address %s.\r\n", s_to_c(path));
350 * if the last sender address resulted in a rejection because the sending
351 * domain didn't exist and this sender has the same domain, reject immediately.
354 if (strncmp(lastsender, s_to_c(path), strlen(lastsender)) == 0){
355 filterstate = REFUSED;
357 reply("554 Sender domain must exist: %s\r\n", s_to_c(path));
360 free(lastsender); /* different sender domain */
365 * see if this ip address, domain name, user name or account is blocked
367 filterstate = blocked(path);
370 listadd(&senders, path);
371 reply("250 sender is %s\r\n", s_to_c(path));
374 enum { Rcpt, Domain, Ntoks };
376 typedef struct Sender Sender;
382 static Sender *sendlist, *sendlast;
383 static uchar rsysip[IPaddrlen];
388 int lnlen, nf, ok = 1;
389 char *line, *senderfile;
393 static int beenhere = 0;
399 fmtinstall('I', eipfmt);
400 parseip(rsysip, nci->rsys);
403 * we're sticking with a system-wide sender list because
404 * per-user lists would require fully resolving recipient
405 * addresses to determine which users they correspond to
406 * (barring syntactic conventions).
408 senderfile = smprint("%s/senders", UPASLIB);
409 sf = Bopen(senderfile, OREAD);
413 while ((line = Brdline(sf, '\n')) != nil) {
414 if (line[0] == '#' || line[0] == '\n')
416 lnlen = Blinelen(sf);
417 line[lnlen-1] = '\0'; /* clobber newline */
418 nf = tokenize(line, toks, nelem(toks));
419 if (nf != nelem(toks))
420 continue; /* malformed line */
422 snd = malloc(sizeof *snd);
424 sysfatal("out of memory: %r");
425 memset(snd, 0, sizeof *snd);
431 sendlast->next = snd;
433 snd->rcpt = strdup(toks[Rcpt]);
434 snd->domain = strdup(toks[Domain]);
441 * read (recipient, sender's DNS) pairs from /mail/lib/senders.
442 * Only allow mail to recipient from any of sender's IPs.
443 * A recipient not mentioned in the file is always permitted.
448 int mentioned = 0, matched = 0;
449 uchar dnsip[IPaddrlen];
451 Ndbtuple *nt, *next, *first;
454 for (snd = sendlist; snd != nil; snd = snd->next) {
455 if (strcmp(rcpt, snd->rcpt) != 0)
458 * see if this domain's ips match nci->rsys.
459 * if not, perhaps a later entry's domain will.
462 if (parseip(dnsip, snd->domain) != -1 &&
463 memcmp(rsysip, dnsip, IPaddrlen) == 0)
466 * NB: nt->line links form a circular list(!).
467 * we need to make one complete pass over it to free it all.
469 first = nt = dnsquery(nci->root, snd->domain, "ip");
473 if (strcmp(nt->attr, "ip") == 0 &&
474 parseip(dnsip, nt->val) != -1 &&
475 memcmp(rsysip, dnsip, IPaddrlen) == 0)
480 } while (nt != first);
489 receiver(String *path)
495 if(him == 0 || *him == 0){
497 reply("503 Start by saying HELO, please\r\n");
501 sender = s_to_c(senders.last->p);
503 sender = "<unknown>";
505 if(!recipok(s_to_c(path))){
507 syslog(0, "smtpd", "Disallowed %s (%s/%s) to blocked name %s",
508 sender, him, nci->rsys, s_to_c(path));
509 reply("550 %s ... user unknown\r\n", s_to_c(path));
513 if (!senderok(rcpt)) {
515 syslog(0, "smtpd", "Disallowed sending IP of %s (%s/%s) to %s",
516 sender, him, nci->rsys, rcpt);
517 reply("550 %s ... sending system not allowed\r\n", rcpt);
522 /* forwarding() can modify 'path' on loopback request */
523 if(filterstate == ACCEPT && (fflag && !authenticated) && forwarding(path)) {
524 syslog(0, "smtpd", "Bad Forward %s (%s/%s) (%s)",
525 s_to_c(senders.last->p), him, nci->rsys, s_to_c(path));
527 reply("550 we don't relay. send to your-path@[] for loopback.\r\n");
530 listadd(&rcvers, path);
531 reply("250 receiver is %s\r\n", s_to_c(path));
537 reply("221 Successful termination\r\n");
547 reply("502 TURN unimplemented\r\n");
555 reply("250 Stop wasting my time!\r\n");
565 reply("250 Read rfc821 and stop wasting my time\r\n");
576 if(shellchars(s_to_c(path))){
577 reply("503 Bad character in address %s.\r\n", s_to_c(path));
580 av[0] = s_to_c(mailer);
582 av[2] = s_to_c(path);
585 pp = noshell_proc_start(av, (stream *)0, outstream(), (stream *)0, 1, 0);
587 reply("450 We're busy right now, try later\r\n");
591 p = Brdline(pp->std[1]->fp, '\n');
593 reply("550 String does not match anything.\r\n");
595 p[Blinelen(pp->std[1]->fp)-1] = 0;
597 reply("550 String does not match anything.\r\n");
602 reply("250 %s <%s@%s>\r\n", s_to_c(path), p, dom);
611 * get a line that ends in crnl or cr, turn terminating crnl into a nl
616 getcrnl(String *s, Biobuf *fp)
663 for(l = senders.first; l; l = l->next){
664 if(l != senders.first)
665 s_append(from, ", ");
666 s_append(from, s_to_c(l->p));
668 for(l = rcvers.first; l; l = l->next){
669 if(l != rcvers.first)
671 s_append(to, s_to_c(l->p));
673 syslog(0, "smtpd", "[%s/%s] %s sent %d bytes to %s", him, nci->rsys,
674 s_to_c(from), nbytes, s_to_c(to));
688 for(l = rcvers.first; l; l = l->next)
689 syslog(0, "smtpd", "%s %s (%s/%s) (%s)", action,
690 s_to_c(senders.last->p), him, nci->rsys, s_to_c(l->p));
694 optoutall(int filterstate)
704 for(l = rcvers.first; l; l = l->next)
705 if(!optoutofspamfilter(s_to_c(l->p)))
721 * ignore the filterstate if the all the receivers prefer it.
723 filterstate = optoutall(filterstate);
725 switch (filterstate){
730 filename = dumpfile(s_to_c(senders.last->p));
732 s_append(cmd, "cat > ");
733 s_append(cmd, filename);
734 pp = proc_start(s_to_c(cmd), instream(), 0, outstream(), 0, 0);
739 reply("554 We don't accept mail from dial-up ports.\r\n");
741 * we could exit here, because we're never going to accept mail from this
742 * ip address, but it's unclear that RFC821 allows that. Instead we set
743 * the hardreject flag and go stupid.
750 reply("554-We don't accept mail from %s.\r\n", s_to_c(senders.last->p));
751 reply("554 Contact postmaster@%s for more information.\r\n", dom);
756 reply("554 Sender domain must exist: %s\r\n", s_to_c(senders.last->p));
762 reply("554-We have had an internal mailer error classifying your message.\r\n");
763 reply("554-Filterstate is %d\r\n", filterstate);
764 reply("554 Contact postmaster@%s for more information.\r\n", dom);
769 * now that all other filters have been passed,
770 * do grey-list processing.
776 * set up mail command
778 cmd = s_clone(mailer);
780 for(l = rcvers.first; l; l = l->next)
782 av = malloc(n*sizeof(char*));
784 reply("450 We're busy right now, try later\n");
790 av[n++] = s_to_c(cmd);
792 for(l = rcvers.first; l; l = l->next)
793 av[n++] = s_to_c(l->p);
798 pp = noshell_proc_start(av, instream(), outstream(), outstream(), 0, 0);
803 reply("450 We're busy right now, try later\n");
811 * print out a header line, expanding any domainless addresses into
815 bprintnode(Biobuf *b, Node *p)
818 if(p->addr && strchr(s_to_c(p->s), '@') == nil){
819 if(Bprint(b, "%s@%s", s_to_c(p->s), him) < 0)
822 if(Bwrite(b, s_to_c(p->s), s_len(p->s)) < 0)
826 if(Bputc(b, p->c) < 0)
830 if(Bwrite(b, s_to_c(p->white), s_len(p->white)) < 0)
838 for(; p; p = p->next)
845 * add waring headers of the form
846 * X-warning: <reason>
847 * for any headers that looked like they might be forged.
849 * return byte count of new headers
852 forgedheaderwarnings(void)
859 /* warn about envelope sender */
860 if(strcmp(s_to_c(senders.last->p), "/dev/null") != 0 && masquerade(senders.last->p, nil))
861 nbytes += Bprint(pp->std[0]->fp, "X-warning: suspect envelope domain\n");
864 * check Sender: field. If it's OK, ignore the others because this is an
865 * exploded mailing list.
867 for(f = firstfield; f; f = f->next){
868 if(f->node->c == SENDER){
869 if(masquerade(getaddr(f->node), him))
870 nbytes += Bprint(pp->std[0]->fp, "X-warning: suspect Sender: domain\n");
877 for(f = firstfield; f; f = f->next){
878 if(f->node->c == FROM && masquerade(getaddr(f->node), him))
879 nbytes += Bprint(pp->std[0]->fp, "X-warning: suspect From: domain\n");
885 * pipe message to mailer with the following transformations:
886 * - change \r\n into \n.
887 * - add sender's domain to any addrs with no domain
888 * - add a From: if none of From:, Sender:, or Replyto: exists
889 * - add a Received: line
892 pipemsg(int *byteswritten)
904 pipesig(&status); /* set status to 1 on write to closed pipe */
909 * add a 'From ' line as envelope
912 nbytes += Bprint(pp->std[0]->fp, "From %s %s remote from \n",
913 s_to_c(senders.first->p), thedate());
916 * add our own Received: stamp
918 nbytes += Bprint(pp->std[0]->fp, "Received: from %s ", him);
920 nbytes += Bprint(pp->std[0]->fp, "([%s]) ", nci->rsys);
921 nbytes += Bprint(pp->std[0]->fp, "by %s; %s\n", me, thedate());
924 * read first 16k obeying '.' escape. we're assuming
925 * the header will all be there.
929 while(sawdot == 0 && s_len(hdr) < 16*1024){
930 n = getcrnl(s_reset(line), &bin);
932 /* eof or error ends the message */
936 /* a line with only a '.' ends the message */
938 if(n == 2 && *cp == '.' && *(cp+1) == '\n'){
943 s_append(hdr, *cp == '.' ? cp+1 : cp);
949 yyinit(s_to_c(hdr), s_len(hdr));
953 * Look for masquerades. Let Sender: trump From: to allow mailing list
954 * forwarded messages.
957 nbytes += forgedheaderwarnings();
960 * add an orginator and/or destination if either is missing
963 if(senders.last == nil)
964 Bprint(pp->std[0]->fp, "From: /dev/null@%s\n", him);
966 Bprint(pp->std[0]->fp, "From: %s\n", s_to_c(senders.last->p));
968 if(destination == 0){
969 Bprint(pp->std[0]->fp, "To: ");
970 for(l = rcvers.first; l; l = l->next){
971 if(l != rcvers.first)
972 Bprint(pp->std[0]->fp, ", ");
973 Bprint(pp->std[0]->fp, "%s", s_to_c(l->p));
975 Bprint(pp->std[0]->fp, "\n");
979 * add sender's domain to any domainless addresses
980 * (to avoid forging local addresses)
983 for(f = firstfield; cp != nil && f; f = f->next){
984 for(p = f->node; cp != 0 && p; p = p->next)
985 cp = bprintnode(pp->std[0]->fp, p);
986 if(status == 0 && Bprint(pp->std[0]->fp, "\n") < 0){
987 piperror = "write error";
992 piperror = "sender domain";
996 /* write anything we read following the header */
997 if(status == 0 && Bwrite(pp->std[0]->fp, cp, s_to_c(hdr) + s_len(hdr) - cp) < 0){
998 piperror = "write error 2";
1004 * pass rest of message to mailer. take care of '.'
1008 n = getcrnl(s_reset(line), &bin);
1010 /* eof or error ends the message */
1014 /* a line with only a '.' ends the message */
1016 if(n == 2 && *cp == '.' && *(cp+1) == '\n'){
1021 if(status == 0 && Bwrite(pp->std[0]->fp, *cp == '.' ? cp+1 : cp, n) < 0){
1022 piperror = "write error 3";
1028 /* message did not terminate normally */
1029 snprint(pipbuf, sizeof pipbuf, "network eof: %r");
1035 if(status == 0 && Bflush(pp->std[0]->fp) < 0){
1036 piperror = "write error 4";
1039 stream_free(pp->std[0]);
1041 *byteswritten = nbytes;
1043 if(status && !piperror)
1044 piperror = "write on closed pipe";
1051 static char buf[128];
1054 strncpy(buf, x, sizeof(buf));
1055 buf[sizeof(buf)-1] = 0;
1056 p = strchr(buf, '\n');
1065 char *cp, *senddom, *user;
1069 static char *validate;
1071 who = s_to_c(senders.first->p);
1072 if(strcmp(who, "/dev/null") == 0){
1073 /* /dev/null can only send to one rcpt at a time */
1074 if(rcvers.first != rcvers.last){
1075 werrstr("rejected: /dev/null sending to multiple recipients");
1082 validate = unsharp("#9/mail/lib/validatesender");
1083 if(access(validate, AEXEC) < 0)
1086 senddom = strdup(who);
1087 if((cp = strchr(senddom, '!')) == nil){
1088 werrstr("rejected: domainless sender %s", who);
1095 switch(pid = fork()){
1097 werrstr("deferred: fork: %r");
1101 * Could add an option with the remote IP address
1102 * to allow validatesender to implement SPF eventually.
1104 execl(validate, "validatesender",
1105 "-n", nci->root, senddom, user, nil);
1106 threadexitsall("exec validatesender: %r");
1114 werrstr("deferred: wait failed: %r");
1118 werrstr("deferred: wait returned wrong pid %d != %d", w->pid, pid);
1127 * skip over validatesender 143123132: prefix from rc.
1129 cp = strchr(w->msg, ':');
1130 if(cp && *(cp+1) == ' ')
1131 werrstr("%s", cp+2);
1133 werrstr("%s", w->msg);
1150 if(senders.last == 0){
1151 reply("503 Data without MAIL FROM:\r\n");
1155 if(rcvers.last == 0){
1156 reply("503 Data without RCPT TO:\r\n");
1160 if(sendermxcheck()){
1161 rerrstr(errx, sizeof errx);
1162 if(strncmp(errx, "rejected:", 9) == 0)
1163 reply("554 %s\r\n", errx);
1165 reply("450 %s\r\n", errx);
1166 for(l=rcvers.first; l; l=l->next)
1167 syslog(0, "smtpd", "[%s/%s] %s -> %s sendercheck: %s",
1168 him, nci->rsys, s_to_c(senders.first->p),
1169 s_to_c(l->p), errx);
1178 reply("354 Input message; end with <CRLF>.<CRLF>\r\n");
1181 * allow 145 more minutes to move the data
1185 status = pipemsg(&nbytes);
1188 * read any error messages
1191 while(s_read_line(pp->std[2]->fp, err))
1195 atnotify(catchalarm, 0);
1197 status |= proc_wait(pp);
1200 fprint(2, "%d status %ux\n", getpid(), status);
1202 fprint(2, "%d error %s\n", getpid(), s_to_c(err));
1206 * if process terminated abnormally, send back error message
1211 if(strstr(s_to_c(err), "mail refused")){
1212 syslog(0, "smtpd", "++[%s/%s] %s %s refused: %s", him, nci->rsys,
1213 s_to_c(senders.first->p), s_to_c(cmd), firstline(s_to_c(err)));
1216 syslog(0, "smtpd", "++[%s/%s] %s %s %s%s%sreturned %#q %s", him, nci->rsys,
1217 s_to_c(senders.first->p), s_to_c(cmd),
1218 piperror ? "error during pipemsg: " : "",
1219 piperror ? piperror : "",
1220 piperror ? "; " : "",
1221 pp->waitmsg->msg, firstline(s_to_c(err)));
1224 for(cp = s_to_c(err); ep = strchr(cp, '\n'); cp = ep){
1226 reply("%d-%s\r\n", code, cp);
1228 reply("%d mail process terminated abnormally\r\n", code);
1231 * if a message appeared on stderr, despite good status,
1232 * log it. this can happen if rewrite.in contains a bad
1233 * r.e., for example.
1237 "%s returned good status, but said: %s",
1238 s_to_c(mailer), s_to_c(err));
1240 if(filterstate == BLOCKED)
1241 reply("554 we believe this is spam. we don't accept it.\r\n");
1243 if(filterstate == DELAY)
1244 reply("554 There will be a delay in delivery of this message.\r\n");
1246 reply("250 sent\r\n");
1260 * when we have blocked a transaction based on IP address, there is nothing
1261 * that the sender can do to convince us to take the message. after the
1262 * first rejection, some spammers continually RSET and give a new MAIL FROM:
1263 * filling our logs with rejections. rejectcheck() limits the retries and
1264 * swiftly rejects all further commands after the first 500-series message
1271 if(rejectcount > MAXREJECTS){
1272 syslog(0, "smtpd", "Rejected (%s/%s)", him, nci->rsys);
1273 reply("554 too many errors. transaction failed.\r\n");
1274 threadexitsall("errcount");
1278 reply("554 We don't accept mail from dial-up ports.\r\n");
1284 * create abs path of the mailer
1296 s_append(s, UPASBIN);
1303 s_dec64(String *sin)
1310 * if the string is coming from smtpd.y, it will have no nl.
1311 * if it is coming from getcrnl below, it will have an nl.
1313 if (*(s_to_c(sin)+lin-1) == '\n')
1315 sout = s_newalloc(lin+1);
1316 lout = dec64((uchar *)s_to_c(sout), lin, s_to_c(sin), lin);
1321 sout->ptr = sout->base + lout;
1333 conn = mallocz(sizeof *conn, 1);
1334 cert = readcert(tlscert, &certlen);
1335 if (conn == nil || cert == nil) {
1338 reply("454 TLS not available\r\n");
1341 reply("220 Go ahead make my day\r\n");
1343 conn->certlen = certlen;
1344 fd = tlsServer(Bfildes(&bin), conn);
1348 syslog(0, "smtpd", "TLS start-up failed with %s", him);
1350 /* force the client to hang up */
1351 close(Bfildes(&bin)); /* probably fd 0 */
1353 threadexitsall("tls failed");
1356 Binit(&bin, fd, OREAD);
1358 fprint(2, "dup of %d failed: %r\n", fd);
1359 passwordinclear = 1;
1360 syslog(0, "smtpd", "started TLS with %s", him);
1364 auth(String *mech, String *resp)
1366 Chalstate *chs = nil;
1368 String *s_resp1_64 = nil;
1369 String *s_resp2_64 = nil;
1370 String *s_resp1 = nil;
1371 String *s_resp2 = nil;
1372 char *scratch = nil;
1378 syslog(0, "smtpd", "auth(%s, %s) from %s", s_to_c(mech),
1379 "(protected)", him);
1381 if (authenticated) {
1384 reply("503 Bad sequence of commands\r\n");
1387 if (cistrcmp(s_to_c(mech), "plain") == 0) {
1389 if (!passwordinclear) {
1391 reply("538 Encryption required for requested authentication mechanism\r\n");
1395 if (s_resp1_64 == nil) {
1397 s_resp1_64 = s_new();
1398 if (getcrnl(s_resp1_64, &bin) <= 0) {
1402 s_resp1 = s_dec64(s_resp1_64);
1403 if (s_resp1 == nil) {
1405 reply("501 Cannot decode base64\r\n");
1408 memset(s_to_c(s_resp1_64), 'X', s_len(s_resp1_64));
1409 user = (s_to_c(s_resp1) + strlen(s_to_c(s_resp1)) + 1);
1410 pass = user + (strlen(user) + 1);
1411 ai = auth_userpasswd(user, pass);
1412 authenticated = ai != nil;
1413 memset(pass, 'X', strlen(pass));
1416 else if (cistrcmp(s_to_c(mech), "login") == 0) {
1418 if (!passwordinclear) {
1420 reply("538 Encryption required for requested authentication mechanism\r\n");
1424 reply("334 VXNlcm5hbWU6\r\n");
1425 s_resp1_64 = s_new();
1426 if (getcrnl(s_resp1_64, &bin) <= 0)
1429 reply("334 UGFzc3dvcmQ6\r\n");
1430 s_resp2_64 = s_new();
1431 if (getcrnl(s_resp2_64, &bin) <= 0)
1433 s_resp1 = s_dec64(s_resp1_64);
1434 s_resp2 = s_dec64(s_resp2_64);
1435 memset(s_to_c(s_resp2_64), 'X', s_len(s_resp2_64));
1436 if (s_resp1 == nil || s_resp2 == nil) {
1438 reply("501 Cannot decode base64\r\n");
1441 ai = auth_userpasswd(s_to_c(s_resp1), s_to_c(s_resp2));
1442 authenticated = ai != nil;
1443 memset(s_to_c(s_resp2), 'X', s_len(s_resp2));
1446 reply("235 Authentication successful\r\n");
1449 reply("535 Authentication failed\r\n");
1453 else if (cistrcmp(s_to_c(mech), "cram-md5") == 0) {
1458 chs = auth_challenge("proto=cram role=server");
1461 reply("501 Couldn't get CRAM-MD5 challenge\r\n");
1464 scratch = malloc(chs->nchal * 2 + 1);
1465 chal64n = enc64(scratch, chs->nchal * 2, (uchar *)chs->chal, chs->nchal);
1466 scratch[chal64n] = 0;
1467 reply("334 %s\r\n", scratch);
1468 s_resp1_64 = s_new();
1469 if (getcrnl(s_resp1_64, &bin) <= 0)
1471 s_resp1 = s_dec64(s_resp1_64);
1472 if (s_resp1 == nil) {
1474 reply("501 Cannot decode base64\r\n");
1477 /* should be of form <user><space><response> */
1478 resp = s_to_c(s_resp1);
1479 t = strchr(resp, ' ');
1482 reply("501 Poorly formed CRAM-MD5 response\r\n");
1488 chs->nresp = strlen(t);
1489 ai = auth_response(chs);
1490 authenticated = ai != nil;
1494 reply("501 Unrecognised authentication type %s\r\n", s_to_c(mech));