URI: 
       tsmtpd.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tsmtpd.c (31322B)
       ---
            1 #include "common.h"
            2 #include "smtpd.h"
            3 #include "smtp.h"
            4 #include <ctype.h>
            5 #include <ip.h>
            6 #include <ndb.h>
            7 #include <mp.h>
            8 #include <libsec.h>
            9 #include <auth.h>
           10 #include <thread.h>
           11 #include "../smtp/rfc822.tab.h"
           12 
           13 #define DBGMX 1
           14 
           15 char        *me;
           16 char        *him="";
           17 char        *dom;
           18 process        *pp;
           19 String        *mailer;
           20 NetConnInfo *nci;
           21 
           22 int        filterstate = ACCEPT;
           23 int        trusted;
           24 int        logged;
           25 int        rejectcount;
           26 int        hardreject;
           27 
           28 Biobuf        bin;
           29 
           30 int        debug;
           31 int        Dflag;
           32 int        fflag;
           33 int        gflag;
           34 int        rflag;
           35 int        sflag;
           36 int        authenticate;
           37 int        authenticated;
           38 int        passwordinclear;
           39 char        *tlscert;
           40 
           41 List        senders;
           42 List        rcvers;
           43 
           44 char pipbuf[ERRMAX];
           45 char        *piperror;
           46 int        pipemsg(int*);
           47 String*        startcmd(void);
           48 int        rejectcheck(void);
           49 String*        mailerpath(char*);
           50 
           51 static int
           52 catchalarm(void *a, char *msg)
           53 {
           54         int rv = 1;
           55 
           56         USED(a);
           57 
           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);
           63                 else
           64                         syslog(0, "smtpd", "note: %s", msg);
           65                 rv = 0;
           66         }
           67 
           68         /* kill the children if there are any */
           69         if(pp)
           70                 syskillpg(pp->pid);
           71 
           72         return rv;
           73 }
           74 
           75         /* override string error functions to do something reasonable */
           76 void
           77 s_error(char *f, char *status)
           78 {
           79         char errbuf[Errlen];
           80 
           81         errbuf[0] = 0;
           82         rerrstr(errbuf, sizeof(errbuf));
           83         if(f && *f)
           84                 reply("452 out of memory %s: %s\r\n", f, errbuf);
           85         else
           86                 reply("452 out of memory %s\r\n", errbuf);
           87         syslog(0, "smtpd", "++Malloc failure %s [%s]", him, nci->rsys);
           88         threadexitsall(status);
           89 }
           90 
           91 void
           92 threadmain(int argc, char **argv)
           93 {
           94         char *p, buf[1024];
           95         char *netdir;
           96 
           97         netdir = nil;
           98         quotefmtinstall();
           99         ARGBEGIN{
          100         case 'D':
          101                 Dflag++;
          102                 break;
          103         case 'd':
          104                 debug++;
          105                 break;
          106         case 'n':                                /* log peer ip address */
          107                 netdir = ARGF();
          108                 break;
          109         case 'f':                                /* disallow relaying */
          110                 fflag = 1;
          111                 break;
          112         case 'g':
          113                 gflag = 1;
          114                 break;
          115         case 'h':                                /* default domain name */
          116                 dom = ARGF();
          117                 break;
          118         case 'k':                                /* prohibited ip address */
          119                 p = ARGF();
          120                 if (p)
          121                         addbadguy(p);
          122                 break;
          123         case 'm':                                /* set mail command */
          124                 p = ARGF();
          125                 if(p)
          126                         mailer = mailerpath(p);
          127                 break;
          128         case 'r':
          129                 rflag = 1;                        /* verify sender's domain */
          130                 break;
          131         case 's':                                /* save blocked messages */
          132                 sflag = 1;
          133                 break;
          134         case 'a':
          135                 authenticate = 1;
          136                 break;
          137         case 'p':
          138                 passwordinclear = 1;
          139                 break;
          140         case 'c':
          141                 fprint(2, "tls is not available\n");
          142                 threadexitsall("no tls");
          143                 tlscert = ARGF();
          144                 break;
          145         case 't':
          146                 fprint(2, "%s: the -t option is no longer supported, see -c\n", argv0);
          147                 tlscert = "/sys/lib/ssl/smtpd-cert.pem";
          148                 break;
          149         default:
          150                 fprint(2, "usage: smtpd [-dfhrs] [-n net] [-c cert]\n");
          151                 threadexitsall("usage");
          152         }ARGEND;
          153 
          154         nci = getnetconninfo(netdir, 0);
          155         if(nci == nil)
          156                 sysfatal("can't get remote system's address");
          157 
          158         if(mailer == nil)
          159                 mailer = mailerpath("send");
          160 
          161         if(debug){
          162                 close(2);
          163                 snprint(buf, sizeof(buf), "%s/smtpd.db", UPASLOG);
          164                 if (open(buf, OWRITE) >= 0) {
          165                         seek(2, 0, 2);
          166                         fprint(2, "%d smtpd %s\n", getpid(), thedate());
          167                 } else
          168                         debug = 0;
          169         }
          170         getconf();
          171         Binit(&bin, 0, OREAD);
          172 
          173         chdir(UPASLOG);
          174         me = sysname_read();
          175         if(dom == 0 || dom[0] == 0)
          176                 dom = domainname_read();
          177         if(dom == 0 || dom[0] == 0)
          178                 dom = me;
          179         sayhi();
          180         parseinit();
          181                 /* allow 45 minutes to parse the header */
          182         atnotify(catchalarm, 1);
          183         alarm(45*60*1000);
          184         zzparse();
          185         threadexitsall(0);
          186 }
          187 
          188 void
          189 listfree(List *l)
          190 {
          191         Link *lp;
          192         Link *next;
          193 
          194         for(lp = l->first; lp; lp = next){
          195                 next = lp->next;
          196                 s_free(lp->p);
          197                 free(lp);
          198         }
          199         l->first = l->last = 0;
          200 }
          201 
          202 void
          203 listadd(List *l, String *path)
          204 {
          205         Link *lp;
          206 
          207         lp = (Link *)malloc(sizeof(Link));
          208         lp->p = path;
          209         lp->next = 0;
          210 
          211         if(l->last)
          212                 l->last->next = lp;
          213         else
          214                 l->first = lp;
          215         l->last = lp;
          216 }
          217 
          218 #define        SIZE        4096
          219 int
          220 reply(char *fmt, ...)
          221 {
          222         char buf[SIZE], *out;
          223         va_list arg;
          224         int n;
          225 
          226         va_start(arg, fmt);
          227         out = vseprint(buf, buf+SIZE, fmt, arg);
          228         va_end(arg);
          229         n = (long)(out-buf);
          230         if(debug) {
          231                 seek(2, 0, 2);
          232                 write(2, buf, n);
          233         }
          234         write(1, buf, n);
          235         return n;
          236 }
          237 
          238 void
          239 reset(void)
          240 {
          241         if(rejectcheck())
          242                 return;
          243         listfree(&rcvers);
          244         listfree(&senders);
          245         if(filterstate != DIALUP){
          246                 logged = 0;
          247                 filterstate = ACCEPT;
          248         }
          249         reply("250 ok\r\n");
          250 }
          251 
          252 void
          253 sayhi(void)
          254 {
          255         reply("220 %s SMTP\r\n", dom);
          256 }
          257 
          258 void
          259 hello(String *himp, int extended)
          260 {
          261         char **mynames;
          262 
          263         him = s_to_c(himp);
          264         syslog(0, "smtpd", "%s from %s as %s", extended ? "ehlo" : "helo", nci->rsys, him);
          265         if(rejectcheck())
          266                 return;
          267 
          268         if(strchr(him, '.') && nci && !trusted && fflag && strcmp(nci->rsys, nci->lsys) != 0){
          269                 /*
          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.
          273                  */
          274                 if(strcmp(him, dom) == 0)
          275                         goto Liarliar;
          276                 for(mynames=sysnames_read(); mynames && *mynames; mynames++){
          277                         if(cistrcmp(*mynames, him) == 0){
          278                         Liarliar:
          279                                 syslog(0, "smtpd", "Hung up on %s; claimed to be %s",
          280                                         nci->rsys, him);
          281                                 reply("554 Liar!\r\n");
          282                                 threadexitsall("client pretended to be us");
          283                                 return;
          284                         }
          285                 }
          286         }
          287         /*
          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.
          293          */
          294         if (strcmp(him, "localhost") == 0 ||
          295             strcmp(him, "localhost.localdomain") == 0 ||
          296             strcmp(him, "localhost.example.com") == 0)
          297                 goto Liarliar;
          298         if(strchr(him, '.') == 0 && nci != nil && strchr(nci->rsys, '.') != nil)
          299                 him = nci->rsys;
          300 
          301         if(Dflag)
          302                 sleep(15*1000);
          303         reply("250%c%s you are %s\r\n", extended ? '-' : ' ', dom, him);
          304         if (extended) {
          305                 if(tlscert != nil)
          306                         reply("250-STARTTLS\r\n");
          307                 if (passwordinclear)
          308                         reply("250 AUTH CRAM-MD5 PLAIN LOGIN\r\n");
          309                 else
          310                         reply("250 AUTH CRAM-MD5\r\n");
          311         }
          312 }
          313 
          314 void
          315 sender(String *path)
          316 {
          317         String *s;
          318         static char *lastsender;
          319 
          320         if(rejectcheck())
          321                 return;
          322         if (authenticate && !authenticated) {
          323                 rejectcount++;
          324                 reply("530 Authentication required\r\n");
          325                 return;
          326         }
          327         if(him == 0 || *him == 0){
          328                 rejectcount++;
          329                 reply("503 Start by saying HELO, please.\r\n", s_to_c(path));
          330                 return;
          331         }
          332 
          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){
          335                 s = s_new();
          336                 s_append(s, him);
          337                 s_append(s, "!");
          338                 s_append(s, s_to_c(path));
          339                 s_terminate(s);
          340                 s_free(path);
          341                 path = s;
          342         }
          343         if(shellchars(s_to_c(path))){
          344                 rejectcount++;
          345                 reply("503 Bad character in sender address %s.\r\n", s_to_c(path));
          346                 return;
          347         }
          348 
          349         /*
          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.
          352          */
          353         if(lastsender){
          354                 if (strncmp(lastsender, s_to_c(path), strlen(lastsender)) == 0){
          355                         filterstate = REFUSED;
          356                         rejectcount++;
          357                         reply("554 Sender domain must exist: %s\r\n", s_to_c(path));
          358                         return;
          359                 }
          360                 free(lastsender);        /* different sender domain */
          361                 lastsender = 0;
          362         }
          363 
          364         /*
          365          * see if this ip address, domain name, user name or account is blocked
          366          */
          367         filterstate = blocked(path);
          368 
          369         logged = 0;
          370         listadd(&senders, path);
          371         reply("250 sender is %s\r\n", s_to_c(path));
          372 }
          373 
          374 enum { Rcpt, Domain, Ntoks };
          375 
          376 typedef struct Sender Sender;
          377 struct Sender {
          378         Sender        *next;
          379         char        *rcpt;
          380         char        *domain;
          381 };
          382 static Sender *sendlist, *sendlast;
          383 static uchar rsysip[IPaddrlen];
          384 
          385 static int
          386 rdsenders(void)
          387 {
          388         int lnlen, nf, ok = 1;
          389         char *line, *senderfile;
          390         char *toks[Ntoks];
          391         Biobuf *sf;
          392         Sender *snd;
          393         static int beenhere = 0;
          394 
          395         if (beenhere)
          396                 return 1;
          397         beenhere = 1;
          398 
          399         fmtinstall('I', eipfmt);
          400         parseip(rsysip, nci->rsys);
          401 
          402         /*
          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).
          407          */
          408         senderfile = smprint("%s/senders", UPASLIB);
          409         sf = Bopen(senderfile, OREAD);
          410         free(senderfile);
          411         if (sf == nil)
          412                 return 1;
          413         while ((line = Brdline(sf, '\n')) != nil) {
          414                 if (line[0] == '#' || line[0] == '\n')
          415                         continue;
          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 */
          421 
          422                 snd = malloc(sizeof *snd);
          423                 if (snd == nil)
          424                         sysfatal("out of memory: %r");
          425                 memset(snd, 0, sizeof *snd);
          426                 snd->next = nil;
          427 
          428                 if (sendlast == nil)
          429                         sendlist = snd;
          430                 else
          431                         sendlast->next = snd;
          432                 sendlast = snd;
          433                 snd->rcpt = strdup(toks[Rcpt]);
          434                 snd->domain = strdup(toks[Domain]);
          435         }
          436         Bterm(sf);
          437         return ok;
          438 }
          439 
          440 /*
          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.
          444  */
          445 static int
          446 senderok(char *rcpt)
          447 {
          448         int mentioned = 0, matched = 0;
          449         uchar dnsip[IPaddrlen];
          450         Sender *snd;
          451         Ndbtuple *nt, *next, *first;
          452 
          453         rdsenders();
          454         for (snd = sendlist; snd != nil; snd = snd->next) {
          455                 if (strcmp(rcpt, snd->rcpt) != 0)
          456                         continue;
          457                 /*
          458                  * see if this domain's ips match nci->rsys.
          459                  * if not, perhaps a later entry's domain will.
          460                  */
          461                 mentioned = 1;
          462                 if (parseip(dnsip, snd->domain) != -1 &&
          463                     memcmp(rsysip, dnsip, IPaddrlen) == 0)
          464                         return 1;
          465                 /*
          466                  * NB: nt->line links form a circular list(!).
          467                  * we need to make one complete pass over it to free it all.
          468                  */
          469                 first = nt = dnsquery(nci->root, snd->domain, "ip");
          470                 if (first == nil)
          471                         continue;
          472                 do {
          473                         if (strcmp(nt->attr, "ip") == 0 &&
          474                             parseip(dnsip, nt->val) != -1 &&
          475                             memcmp(rsysip, dnsip, IPaddrlen) == 0)
          476                                 matched = 1;
          477                         next = nt->line;
          478                         free(nt);
          479                         nt = next;
          480                 } while (nt != first);
          481         }
          482         if (matched)
          483                 return 1;
          484         else
          485                 return !mentioned;
          486 }
          487 
          488 void
          489 receiver(String *path)
          490 {
          491         char *sender, *rcpt;
          492 
          493         if(rejectcheck())
          494                 return;
          495         if(him == 0 || *him == 0){
          496                 rejectcount++;
          497                 reply("503 Start by saying HELO, please\r\n");
          498                 return;
          499         }
          500         if(senders.last)
          501                 sender = s_to_c(senders.last->p);
          502         else
          503                 sender = "<unknown>";
          504 
          505         if(!recipok(s_to_c(path))){
          506                 rejectcount++;
          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));
          510                 return;
          511         }
          512         rcpt = s_to_c(path);
          513         if (!senderok(rcpt)) {
          514                 rejectcount++;
          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);
          518                 return;
          519         }
          520 
          521         logged = 0;
          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));
          526                 rejectcount++;
          527                 reply("550 we don't relay.  send to your-path@[] for loopback.\r\n");
          528                 return;
          529         }
          530         listadd(&rcvers, path);
          531         reply("250 receiver is %s\r\n", s_to_c(path));
          532 }
          533 
          534 void
          535 quit(void)
          536 {
          537         reply("221 Successful termination\r\n");
          538         close(0);
          539         threadexitsall(0);
          540 }
          541 
          542 void
          543 turn(void)
          544 {
          545         if(rejectcheck())
          546                 return;
          547         reply("502 TURN unimplemented\r\n");
          548 }
          549 
          550 void
          551 noop(void)
          552 {
          553         if(rejectcheck())
          554                 return;
          555         reply("250 Stop wasting my time!\r\n");
          556 }
          557 
          558 void
          559 help(String *cmd)
          560 {
          561         if(rejectcheck())
          562                 return;
          563         if(cmd)
          564                 s_free(cmd);
          565         reply("250 Read rfc821 and stop wasting my time\r\n");
          566 }
          567 
          568 void
          569 verify(String *path)
          570 {
          571         char *p, *q;
          572         char *av[4];
          573 
          574         if(rejectcheck())
          575                 return;
          576         if(shellchars(s_to_c(path))){
          577                 reply("503 Bad character in address %s.\r\n", s_to_c(path));
          578                 return;
          579         }
          580         av[0] = s_to_c(mailer);
          581         av[1] = "-x";
          582         av[2] = s_to_c(path);
          583         av[3] = 0;
          584 
          585         pp = noshell_proc_start(av, (stream *)0, outstream(),  (stream *)0, 1, 0);
          586         if (pp == 0) {
          587                 reply("450 We're busy right now, try later\r\n");
          588                 return;
          589         }
          590 
          591         p = Brdline(pp->std[1]->fp, '\n');
          592         if(p == 0){
          593                 reply("550 String does not match anything.\r\n");
          594         } else {
          595                 p[Blinelen(pp->std[1]->fp)-1] = 0;
          596                 if(strchr(p, ':'))
          597                         reply("550 String does not match anything.\r\n");
          598                 else{
          599                         q = strrchr(p, '!');
          600                         if(q)
          601                                 p = q+1;
          602                         reply("250 %s <%s@%s>\r\n", s_to_c(path), p, dom);
          603                 }
          604         }
          605         proc_wait(pp);
          606         proc_free(pp);
          607         pp = 0;
          608 }
          609 
          610 /*
          611  *  get a line that ends in crnl or cr, turn terminating crnl into a nl
          612  *
          613  *  return 0 on EOF
          614  */
          615 static int
          616 getcrnl(String *s, Biobuf *fp)
          617 {
          618         int c;
          619 
          620         for(;;){
          621                 c = Bgetc(fp);
          622                 if(debug) {
          623                         seek(2, 0, 2);
          624                         fprint(2, "%c", c);
          625                 }
          626                 switch(c){
          627                 case -1:
          628                         goto out;
          629                 case '\r':
          630                         c = Bgetc(fp);
          631                         if(c == '\n'){
          632                                 if(debug) {
          633                                         seek(2, 0, 2);
          634                                         fprint(2, "%c", c);
          635                                 }
          636                                 s_putc(s, '\n');
          637                                 goto out;
          638                         }
          639                         Bungetc(fp);
          640                         s_putc(s, '\r');
          641                         break;
          642                 case '\n':
          643                         s_putc(s, c);
          644                         goto out;
          645                 default:
          646                         s_putc(s, c);
          647                         break;
          648                 }
          649         }
          650 out:
          651         s_terminate(s);
          652         return s_len(s);
          653 }
          654 
          655 void
          656 logcall(int nbytes)
          657 {
          658         Link *l;
          659         String *to, *from;
          660 
          661         to = s_new();
          662         from = s_new();
          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));
          667         }
          668         for(l = rcvers.first; l; l = l->next){
          669                 if(l != rcvers.first)
          670                         s_append(to, ", ");
          671                 s_append(to, s_to_c(l->p));
          672         }
          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));
          675         s_free(to);
          676         s_free(from);
          677 }
          678 
          679 static void
          680 logmsg(char *action)
          681 {
          682         Link *l;
          683 
          684         if(logged)
          685                 return;
          686 
          687         logged = 1;
          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));
          691 }
          692 
          693 static int
          694 optoutall(int filterstate)
          695 {
          696         Link *l;
          697 
          698         switch(filterstate){
          699         case ACCEPT:
          700         case TRUSTED:
          701                 return filterstate;
          702         }
          703 
          704         for(l = rcvers.first; l; l = l->next)
          705                 if(!optoutofspamfilter(s_to_c(l->p)))
          706                         return filterstate;
          707 
          708         return ACCEPT;
          709 }
          710 
          711 String*
          712 startcmd(void)
          713 {
          714         int n;
          715         Link *l;
          716         char **av;
          717         String *cmd;
          718         char *filename;
          719 
          720         /*
          721          *  ignore the filterstate if the all the receivers prefer it.
          722          */
          723         filterstate = optoutall(filterstate);
          724 
          725         switch (filterstate){
          726         case BLOCKED:
          727         case DELAY:
          728                 rejectcount++;
          729                 logmsg("Blocked");
          730                 filename = dumpfile(s_to_c(senders.last->p));
          731                 cmd = s_new();
          732                 s_append(cmd, "cat > ");
          733                 s_append(cmd, filename);
          734                 pp = proc_start(s_to_c(cmd), instream(), 0, outstream(), 0, 0);
          735                 break;
          736         case DIALUP:
          737                 logmsg("Dialup");
          738                 rejectcount++;
          739                 reply("554 We don't accept mail from dial-up ports.\r\n");
          740                 /*
          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.
          744                  */
          745                 hardreject = 1;
          746                 return 0;
          747         case DENIED:
          748                 logmsg("Denied");
          749                 rejectcount++;
          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);
          752                 return 0;
          753         case REFUSED:
          754                 logmsg("Refused");
          755                 rejectcount++;
          756                 reply("554 Sender domain must exist: %s\r\n", s_to_c(senders.last->p));
          757                 return 0;
          758         default:
          759         case NONE:
          760                 logmsg("Confused");
          761                 rejectcount++;
          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);
          765                 return 0;
          766         case ACCEPT:
          767         case TRUSTED:
          768                 /*
          769                  * now that all other filters have been passed,
          770                  * do grey-list processing.
          771                  */
          772                 if(gflag)
          773                         vfysenderhostok();
          774 
          775                 /*
          776                  *  set up mail command
          777                  */
          778                 cmd = s_clone(mailer);
          779                 n = 3;
          780                 for(l = rcvers.first; l; l = l->next)
          781                         n++;
          782                 av = malloc(n*sizeof(char*));
          783                 if(av == nil){
          784                         reply("450 We're busy right now, try later\n");
          785                         s_free(cmd);
          786                         return 0;
          787                 }
          788 
          789                         n = 0;
          790                 av[n++] = s_to_c(cmd);
          791                 av[n++] = "-r";
          792                 for(l = rcvers.first; l; l = l->next)
          793                         av[n++] = s_to_c(l->p);
          794                 av[n] = 0;
          795                 /*
          796                  *  start mail process
          797                  */
          798                 pp = noshell_proc_start(av, instream(), outstream(), outstream(), 0, 0);
          799                 free(av);
          800                 break;
          801         }
          802         if(pp == 0) {
          803                 reply("450 We're busy right now, try later\n");
          804                 s_free(cmd);
          805                 return 0;
          806         }
          807         return cmd;
          808 }
          809 
          810 /*
          811  *  print out a header line, expanding any domainless addresses into
          812  *  address@him
          813  */
          814 char*
          815 bprintnode(Biobuf *b, Node *p)
          816 {
          817         if(p->s){
          818                 if(p->addr && strchr(s_to_c(p->s), '@') == nil){
          819                         if(Bprint(b, "%s@%s", s_to_c(p->s), him) < 0)
          820                                 return nil;
          821                 } else {
          822                         if(Bwrite(b, s_to_c(p->s), s_len(p->s)) < 0)
          823                                 return nil;
          824                 }
          825         }else{
          826                 if(Bputc(b, p->c) < 0)
          827                         return nil;
          828         }
          829         if(p->white)
          830                 if(Bwrite(b, s_to_c(p->white), s_len(p->white)) < 0)
          831                         return nil;
          832         return p->end+1;
          833 }
          834 
          835 static String*
          836 getaddr(Node *p)
          837 {
          838         for(; p; p = p->next)
          839                 if(p->s && p->addr)
          840                         return p->s;
          841         return nil;
          842 }
          843 
          844 /*
          845  *  add waring headers of the form
          846  *        X-warning: <reason>
          847  *  for any headers that looked like they might be forged.
          848  *
          849  *  return byte count of new headers
          850  */
          851 static int
          852 forgedheaderwarnings(void)
          853 {
          854         int nbytes;
          855         Field *f;
          856 
          857         nbytes = 0;
          858 
          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");
          862 
          863         /*
          864          *  check Sender: field.  If it's OK, ignore the others because this is an
          865          *  exploded mailing list.
          866          */
          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");
          871                         else
          872                                 return nbytes;
          873                 }
          874         }
          875 
          876         /* check From: */
          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");
          880         }
          881         return nbytes;
          882 }
          883 
          884 /*
          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
          890  */
          891 int
          892 pipemsg(int *byteswritten)
          893 {
          894         int status;
          895         char *cp;
          896         String *line;
          897         String *hdr;
          898         int n, nbytes;
          899         int sawdot;
          900         Field *f;
          901         Node *p;
          902         Link *l;
          903 
          904         pipesig(&status);        /* set status to 1 on write to closed pipe */
          905         sawdot = 0;
          906         status = 0;
          907 
          908         /*
          909          *  add a 'From ' line as envelope
          910          */
          911         nbytes = 0;
          912         nbytes += Bprint(pp->std[0]->fp, "From %s %s remote from \n",
          913                         s_to_c(senders.first->p), thedate());
          914 
          915         /*
          916          *  add our own Received: stamp
          917          */
          918         nbytes += Bprint(pp->std[0]->fp, "Received: from %s ", him);
          919         if(nci->rsys)
          920                 nbytes += Bprint(pp->std[0]->fp, "([%s]) ", nci->rsys);
          921         nbytes += Bprint(pp->std[0]->fp, "by %s; %s\n", me, thedate());
          922 
          923         /*
          924          *  read first 16k obeying '.' escape.  we're assuming
          925          *  the header will all be there.
          926          */
          927         line = s_new();
          928         hdr = s_new();
          929         while(sawdot == 0 && s_len(hdr) < 16*1024){
          930                 n = getcrnl(s_reset(line), &bin);
          931 
          932                 /* eof or error ends the message */
          933                 if(n <= 0)
          934                         break;
          935 
          936                 /* a line with only a '.' ends the message */
          937                 cp = s_to_c(line);
          938                 if(n == 2 && *cp == '.' && *(cp+1) == '\n'){
          939                         sawdot = 1;
          940                         break;
          941                 }
          942 
          943                 s_append(hdr, *cp == '.' ? cp+1 : cp);
          944         }
          945 
          946         /*
          947           *  parse header
          948          */
          949         yyinit(s_to_c(hdr), s_len(hdr));
          950         yyparse();
          951 
          952         /*
          953           *  Look for masquerades.  Let Sender: trump From: to allow mailing list
          954          *  forwarded messages.
          955          */
          956         if(fflag)
          957                 nbytes += forgedheaderwarnings();
          958 
          959         /*
          960          *  add an orginator and/or destination if either is missing
          961          */
          962         if(originator == 0){
          963                 if(senders.last == nil)
          964                         Bprint(pp->std[0]->fp, "From: /dev/null@%s\n", him);
          965                 else
          966                         Bprint(pp->std[0]->fp, "From: %s\n", s_to_c(senders.last->p));
          967         }
          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));
          974                 }
          975                 Bprint(pp->std[0]->fp, "\n");
          976         }
          977 
          978         /*
          979          *  add sender's domain to any domainless addresses
          980          *  (to avoid forging local addresses)
          981          */
          982         cp = s_to_c(hdr);
          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";
          988                         status = 1;
          989                 }
          990         }
          991         if(cp == nil){
          992                 piperror = "sender domain";
          993                 status = 1;
          994         }
          995 
          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";
          999                 status = 1;
         1000         }
         1001         s_free(hdr);
         1002 
         1003         /*
         1004          *  pass rest of message to mailer.  take care of '.'
         1005          *  escapes.
         1006          */
         1007         while(sawdot == 0){
         1008                 n = getcrnl(s_reset(line), &bin);
         1009 
         1010                 /* eof or error ends the message */
         1011                 if(n <= 0)
         1012                         break;
         1013 
         1014                 /* a line with only a '.' ends the message */
         1015                 cp = s_to_c(line);
         1016                 if(n == 2 && *cp == '.' && *(cp+1) == '\n'){
         1017                         sawdot = 1;
         1018                         break;
         1019                 }
         1020                 nbytes += n;
         1021                 if(status == 0 && Bwrite(pp->std[0]->fp, *cp == '.' ? cp+1 : cp, n) < 0){
         1022                         piperror = "write error 3";
         1023                         status = 1;
         1024                 }
         1025         }
         1026         s_free(line);
         1027         if(sawdot == 0){
         1028                 /* message did not terminate normally */
         1029                 snprint(pipbuf, sizeof pipbuf, "network eof: %r");
         1030                 piperror = pipbuf;
         1031                 syskillpg(pp->pid);
         1032                 status = 1;
         1033         }
         1034 
         1035         if(status == 0 && Bflush(pp->std[0]->fp) < 0){
         1036                 piperror = "write error 4";
         1037                 status = 1;
         1038         }
         1039         stream_free(pp->std[0]);
         1040         pp->std[0] = 0;
         1041         *byteswritten = nbytes;
         1042         pipesigoff();
         1043         if(status && !piperror)
         1044                 piperror = "write on closed pipe";
         1045         return status;
         1046 }
         1047 
         1048 char*
         1049 firstline(char *x)
         1050 {
         1051         static char buf[128];
         1052         char *p;
         1053 
         1054         strncpy(buf, x, sizeof(buf));
         1055         buf[sizeof(buf)-1] = 0;
         1056         p = strchr(buf, '\n');
         1057         if(p)
         1058                 *p = 0;
         1059         return buf;
         1060 }
         1061 
         1062 int
         1063 sendermxcheck(void)
         1064 {
         1065         char *cp, *senddom, *user;
         1066         char *who;
         1067         int pid;
         1068         Waitmsg *w;
         1069         static char *validate;
         1070 
         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");
         1076                         return -1;
         1077                 }
         1078                 return 0;
         1079         }
         1080 
         1081         if(validate == nil)
         1082                 validate = unsharp("#9/mail/lib/validatesender");
         1083         if(access(validate, AEXEC) < 0)
         1084                 return 0;
         1085 
         1086         senddom = strdup(who);
         1087         if((cp = strchr(senddom, '!')) == nil){
         1088                 werrstr("rejected: domainless sender %s", who);
         1089                 free(senddom);
         1090                 return -1;
         1091         }
         1092         *cp++ = 0;
         1093         user = cp;
         1094 
         1095         switch(pid = fork()){
         1096         case -1:
         1097                 werrstr("deferred: fork: %r");
         1098                 return -1;
         1099         case 0:
         1100                 /*
         1101                  * Could add an option with the remote IP address
         1102                  * to allow validatesender to implement SPF eventually.
         1103                  */
         1104                 execl(validate, "validatesender",
         1105                         "-n", nci->root, senddom, user, nil);
         1106                 threadexitsall("exec validatesender: %r");
         1107         default:
         1108                 break;
         1109         }
         1110 
         1111         free(senddom);
         1112         w = wait();
         1113         if(w == nil){
         1114                 werrstr("deferred: wait failed: %r");
         1115                 return -1;
         1116         }
         1117         if(w->pid != pid){
         1118                 werrstr("deferred: wait returned wrong pid %d != %d", w->pid, pid);
         1119                 free(w);
         1120                 return -1;
         1121         }
         1122         if(w->msg[0] == 0){
         1123                 free(w);
         1124                 return 0;
         1125         }
         1126         /*
         1127          * skip over validatesender 143123132: prefix from rc.
         1128          */
         1129         cp = strchr(w->msg, ':');
         1130         if(cp && *(cp+1) == ' ')
         1131                 werrstr("%s", cp+2);
         1132         else
         1133                 werrstr("%s", w->msg);
         1134         free(w);
         1135         return -1;
         1136 }
         1137 
         1138 void
         1139 data(void)
         1140 {
         1141         String *cmd;
         1142         String *err;
         1143         int status, nbytes;
         1144         char *cp, *ep;
         1145         char errx[ERRMAX];
         1146         Link *l;
         1147 
         1148         if(rejectcheck())
         1149                 return;
         1150         if(senders.last == 0){
         1151                 reply("503 Data without MAIL FROM:\r\n");
         1152                 rejectcount++;
         1153                 return;
         1154         }
         1155         if(rcvers.last == 0){
         1156                 reply("503 Data without RCPT TO:\r\n");
         1157                 rejectcount++;
         1158                 return;
         1159         }
         1160         if(sendermxcheck()){
         1161                 rerrstr(errx, sizeof errx);
         1162                 if(strncmp(errx, "rejected:", 9) == 0)
         1163                         reply("554 %s\r\n", errx);
         1164                 else
         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);
         1170                 rejectcount++;
         1171                 return;
         1172         }
         1173 
         1174         cmd = startcmd();
         1175         if(cmd == 0)
         1176                 return;
         1177 
         1178         reply("354 Input message; end with <CRLF>.<CRLF>\r\n");
         1179 
         1180         /*
         1181          *  allow 145 more minutes to move the data
         1182          */
         1183         alarm(145*60*1000);
         1184 
         1185         status = pipemsg(&nbytes);
         1186 
         1187         /*
         1188          *  read any error messages
         1189          */
         1190         err = s_new();
         1191         while(s_read_line(pp->std[2]->fp, err))
         1192                 ;
         1193 
         1194         alarm(0);
         1195         atnotify(catchalarm, 0);
         1196 
         1197         status |= proc_wait(pp);
         1198         if(debug){
         1199                 seek(2, 0, 2);
         1200                 fprint(2, "%d status %ux\n", getpid(), status);
         1201                 if(*s_to_c(err))
         1202                         fprint(2, "%d error %s\n", getpid(), s_to_c(err));
         1203         }
         1204 
         1205         /*
         1206          *  if process terminated abnormally, send back error message
         1207          */
         1208         if(status){
         1209                 int code;
         1210 
         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)));
         1214                         code = 554;
         1215                 } else {
         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)));
         1222                         code = 450;
         1223                 }
         1224                 for(cp = s_to_c(err); ep = strchr(cp, '\n'); cp = ep){
         1225                         *ep++ = 0;
         1226                         reply("%d-%s\r\n", code, cp);
         1227                 }
         1228                 reply("%d mail process terminated abnormally\r\n", code);
         1229         } else {
         1230                 /*
         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.
         1234                  */
         1235                 if(*s_to_c(err))
         1236                         syslog(0, "smtpd",
         1237                                 "%s returned good status, but said: %s",
         1238                                 s_to_c(mailer), s_to_c(err));
         1239 
         1240                 if(filterstate == BLOCKED)
         1241                         reply("554 we believe this is spam.  we don't accept it.\r\n");
         1242                 else
         1243                 if(filterstate == DELAY)
         1244                         reply("554 There will be a delay in delivery of this message.\r\n");
         1245                 else {
         1246                         reply("250 sent\r\n");
         1247                         logcall(nbytes);
         1248                 }
         1249         }
         1250         proc_free(pp);
         1251         pp = 0;
         1252         s_free(cmd);
         1253         s_free(err);
         1254 
         1255         listfree(&senders);
         1256         listfree(&rcvers);
         1257 }
         1258 
         1259 /*
         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
         1265  * is issued.
         1266  */
         1267 int
         1268 rejectcheck(void)
         1269 {
         1270 
         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");
         1275         }
         1276         if(hardreject){
         1277                 rejectcount++;
         1278                 reply("554 We don't accept mail from dial-up ports.\r\n");
         1279         }
         1280         return hardreject;
         1281 }
         1282 
         1283 /*
         1284  *  create abs path of the mailer
         1285  */
         1286 String*
         1287 mailerpath(char *p)
         1288 {
         1289         String *s;
         1290 
         1291         if(p == nil)
         1292                 return nil;
         1293         if(*p == '/')
         1294                 return s_copy(p);
         1295         s = s_new();
         1296         s_append(s, UPASBIN);
         1297         s_append(s, "/");
         1298         s_append(s, p);
         1299         return s;
         1300 }
         1301 
         1302 String *
         1303 s_dec64(String *sin)
         1304 {
         1305         String *sout;
         1306         int lin, lout;
         1307         lin = s_len(sin);
         1308 
         1309         /*
         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.
         1312          */
         1313         if (*(s_to_c(sin)+lin-1) == '\n')
         1314                 lin--;
         1315         sout = s_newalloc(lin+1);
         1316         lout = dec64((uchar *)s_to_c(sout), lin, s_to_c(sin), lin);
         1317         if (lout < 0) {
         1318                 s_free(sout);
         1319                 return nil;
         1320         }
         1321         sout->ptr = sout->base + lout;
         1322         s_terminate(sout);
         1323         return sout;
         1324 }
         1325 
         1326 void
         1327 starttls(void)
         1328 {
         1329         uchar *cert;
         1330         int certlen, fd;
         1331         TLSconn *conn;
         1332 
         1333         conn = mallocz(sizeof *conn, 1);
         1334         cert = readcert(tlscert, &certlen);
         1335         if (conn == nil || cert == nil) {
         1336                 if (conn != nil)
         1337                         free(conn);
         1338                 reply("454 TLS not available\r\n");
         1339                 return;
         1340         }
         1341         reply("220 Go ahead make my day\r\n");
         1342         conn->cert = cert;
         1343         conn->certlen = certlen;
         1344         fd = tlsServer(Bfildes(&bin), conn);
         1345         if (fd < 0) {
         1346                 free(cert);
         1347                 free(conn);
         1348                 syslog(0, "smtpd", "TLS start-up failed with %s", him);
         1349 
         1350                 /* force the client to hang up */
         1351                 close(Bfildes(&bin));                /* probably fd 0 */
         1352                 close(1);
         1353                 threadexitsall("tls failed");
         1354         }
         1355         Bterm(&bin);
         1356         Binit(&bin, fd, OREAD);
         1357         if (dup(fd, 1) < 0)
         1358                 fprint(2, "dup of %d failed: %r\n", fd);
         1359         passwordinclear = 1;
         1360         syslog(0, "smtpd", "started TLS with %s", him);
         1361 }
         1362 
         1363 void
         1364 auth(String *mech, String *resp)
         1365 {
         1366         Chalstate *chs = nil;
         1367         AuthInfo *ai = 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;
         1373         char *user, *pass;
         1374 
         1375         if (rejectcheck())
         1376                 goto bomb_out;
         1377 
         1378          syslog(0, "smtpd", "auth(%s, %s) from %s", s_to_c(mech),
         1379                 "(protected)", him);
         1380 
         1381         if (authenticated) {
         1382         bad_sequence:
         1383                 rejectcount++;
         1384                 reply("503 Bad sequence of commands\r\n");
         1385                 goto bomb_out;
         1386         }
         1387         if (cistrcmp(s_to_c(mech), "plain") == 0) {
         1388 
         1389                 if (!passwordinclear) {
         1390                         rejectcount++;
         1391                         reply("538 Encryption required for requested authentication mechanism\r\n");
         1392                         goto bomb_out;
         1393                 }
         1394                 s_resp1_64 = resp;
         1395                 if (s_resp1_64 == nil) {
         1396                         reply("334 \r\n");
         1397                         s_resp1_64 = s_new();
         1398                         if (getcrnl(s_resp1_64, &bin) <= 0) {
         1399                                 goto bad_sequence;
         1400                         }
         1401                 }
         1402                 s_resp1 = s_dec64(s_resp1_64);
         1403                 if (s_resp1 == nil) {
         1404                         rejectcount++;
         1405                         reply("501 Cannot decode base64\r\n");
         1406                         goto bomb_out;
         1407                 }
         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));
         1414                 goto windup;
         1415         }
         1416         else if (cistrcmp(s_to_c(mech), "login") == 0) {
         1417 
         1418                 if (!passwordinclear) {
         1419                         rejectcount++;
         1420                         reply("538 Encryption required for requested authentication mechanism\r\n");
         1421                         goto bomb_out;
         1422                 }
         1423                 if (resp == nil) {
         1424                         reply("334 VXNlcm5hbWU6\r\n");
         1425                         s_resp1_64 = s_new();
         1426                         if (getcrnl(s_resp1_64, &bin) <= 0)
         1427                                 goto bad_sequence;
         1428                 }
         1429                 reply("334 UGFzc3dvcmQ6\r\n");
         1430                 s_resp2_64 = s_new();
         1431                 if (getcrnl(s_resp2_64, &bin) <= 0)
         1432                         goto bad_sequence;
         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) {
         1437                         rejectcount++;
         1438                         reply("501 Cannot decode base64\r\n");
         1439                         goto bomb_out;
         1440                 }
         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));
         1444         windup:
         1445                 if (authenticated)
         1446                         reply("235 Authentication successful\r\n");
         1447                 else {
         1448                         rejectcount++;
         1449                         reply("535 Authentication failed\r\n");
         1450                 }
         1451                 goto bomb_out;
         1452         }
         1453         else if (cistrcmp(s_to_c(mech), "cram-md5") == 0) {
         1454                 char *resp;
         1455                 int chal64n;
         1456                 char *t;
         1457 
         1458                 chs = auth_challenge("proto=cram role=server");
         1459                 if (chs == nil) {
         1460                         rejectcount++;
         1461                         reply("501 Couldn't get CRAM-MD5 challenge\r\n");
         1462                         goto bomb_out;
         1463                 }
         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)
         1470                         goto bad_sequence;
         1471                 s_resp1 = s_dec64(s_resp1_64);
         1472                 if (s_resp1 == nil) {
         1473                         rejectcount++;
         1474                         reply("501 Cannot decode base64\r\n");
         1475                         goto bomb_out;
         1476                 }
         1477                 /* should be of form <user><space><response> */
         1478                 resp = s_to_c(s_resp1);
         1479                 t = strchr(resp, ' ');
         1480                 if (t == nil) {
         1481                         rejectcount++;
         1482                         reply("501 Poorly formed CRAM-MD5 response\r\n");
         1483                         goto bomb_out;
         1484                 }
         1485                 *t++ = 0;
         1486                 chs->user = resp;
         1487                 chs->resp = t;
         1488                 chs->nresp = strlen(t);
         1489                 ai = auth_response(chs);
         1490                 authenticated = ai != nil;
         1491                 goto windup;
         1492         }
         1493         rejectcount++;
         1494         reply("501 Unrecognised authentication type %s\r\n", s_to_c(mech));
         1495 bomb_out:
         1496         if (ai)
         1497                 auth_freeAI(ai);
         1498         if (chs)
         1499                 auth_freechal(chs);
         1500         if (scratch)
         1501                 free(scratch);
         1502         if (s_resp1)
         1503                 s_free(s_resp1);
         1504         if (s_resp2)
         1505                 s_free(s_resp2);
         1506         if (s_resp1_64)
         1507                 s_free(s_resp1_64);
         1508         if (s_resp2_64)
         1509                 s_free(s_resp2_64);
         1510 }