tsmtpd.c - plan9port - [fork] Plan 9 from user space
git clone git://src.adamsgaard.dk/plan9port
Log
Files
Refs
README
LICENSE
---
tsmtpd.c (31322B)
---
     1 #include "common.h"
     2 #include "smtpd.h"
     3 #include "smtp.h"
     4 #include 
     5 #include 
     6 #include 
     7 #include 
     8 #include 
     9 #include 
    10 #include 
    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 = "";
   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: 
   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 .\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  */
  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 }