Back to index

courier  0.68.2
sendmail.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 1998 - 2004 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 **
00005 */
00006 
00007 #include      "courier.h"
00008 #include      "rw.h"
00009 #include      "rfc822.h"
00010 #include      "comsubmitclient.h"
00011 #include      "maxlongsize.h"
00012 #include      "numlib/numlib.h"
00013 #include      "sbindir.h"
00014 #include      <pwd.h>
00015 #include      <stdio.h>
00016 #include      <ctype.h>
00017 #include      <signal.h>
00018 #if    HAVE_SYSEXITS_H
00019 #include      <sysexits.h>
00020 #else
00021 
00022 #define       EX_NOUSER     67
00023 #define       EX_TEMPFAIL   75
00024 #define       EX_NOPERM     77
00025 #endif
00026 
00027 #if    HAVE_LOCALE_H
00028 #include      <locale.h>
00029 #endif
00030 
00031 #if    HAVE_UNISTD_H
00032 #include      <unistd.h>
00033 #endif
00034 #include      <stdlib.h>
00035 #include      <string.h>
00036 #include      <ctype.h>
00037 
00038 extern void esmtpd();
00039 
00040 
00041 static struct passwd *mypwd()
00042 {
00043 static struct passwd *pwd=0;
00044 int    first_pwd=1;
00045 
00046        if (first_pwd)
00047        {
00048               first_pwd=0;
00049               pwd=getpwuid(getuid());
00050        }
00051 
00052        return (pwd);
00053 }
00054 
00055 static const char *env(const char *envname)
00056 {
00057 const char *p=getenv(envname);
00058 
00059        if (p && !*p) p=0;
00060        return (p);
00061 }
00062 
00063 static char *rewrite_env_sender(const char *from)
00064 {
00065 const char *host;
00066 
00067        if (!from)
00068        {
00069               if ((from=env("MAILSUSER")) == 0 &&
00070                      (from=env("MAILUSER")) == 0 &&
00071                      (from=env("LOGNAME")) == 0 &&
00072                      (from=env("USER")) == 0)
00073               {
00074               struct passwd *pw=mypwd();
00075 
00076                      from=pw ? pw->pw_name:"nobody";
00077               }
00078 
00079               if ((host=env("MAILSHOST")) != 0 ||
00080                      (host=env("MAILHOST")) != 0)
00081               {
00082               char   *p=courier_malloc(strlen(from)+strlen(host)+2);
00083 
00084                      return (strcat(strcat(strcpy(p, from), "@"), host));
00085               }
00086        }
00087        return (strcpy(courier_malloc(strlen(from)+1), from));
00088 }
00089 
00090 static char *rewrite_from(const char *, const char *, const char *,
00091               const char *);
00092 
00093 static struct rfc822t *tokenize_name(const char *name)
00094 {
00095 struct rfc822t *p=rw_rewrite_tokenize(name);
00096 struct rfc822token *t;
00097 
00098        for (t=p->tokens; t; t=t->next)
00099               if (t->token) break;
00100        if (t == 0)   return (p);   /* Only atoms */
00101        if (p->tokens == 0)  return (p);
00102 
00103        p->tokens->token='"';
00104        p->tokens->next=0;
00105        p->tokens->ptr=name;
00106        p->tokens->len=strlen(name);
00107        return (p);
00108 }
00109 
00110 static char *get_gecos()
00111 {
00112 struct passwd *pw=mypwd();
00113 char   *p, *q;
00114 
00115        if (!pw || !pw->pw_gecos)   return (0);
00116 
00117        p=strcpy(courier_malloc(strlen(pw->pw_gecos)+1), pw->pw_gecos);
00118 
00119        if ((q=strchr(p, ',')) != 0)       *q=0;
00120        return (p);
00121 }
00122 
00123 static void rewrite_headers(const char *From)
00124 {
00125 int    seen_from=0;
00126 char   headerbuf[5000];
00127 int    c, i;
00128 const char *mailuser, *mailuser2, *mailhost;
00129 char   *p;
00130 char   *pfrom=From ? strcpy(courier_malloc(strlen(From)+1), From):0;
00131 
00132        if ((mailuser=env("MAILUSER")) == 0 &&
00133               (mailuser=env("LOGNAME")) == 0)
00134               mailuser=env("USER");
00135        mailuser2=env("MAILUSER");
00136        mailhost=env("MAILHOST");
00137 
00138        while (fgets(headerbuf, sizeof(headerbuf), stdin))
00139        {
00140        char   *p=strchr(headerbuf, '\n');
00141 
00142               if (p)
00143               {
00144                      *p=0;
00145                      if (p == headerbuf || strcmp(headerbuf, "\r") == 0)
00146                             break;
00147               }
00148 
00149 #if HAVE_STRNCASECMP
00150               if (strncasecmp(headerbuf, "from:", 5))
00151 #else
00152               if (strnicmp(headerbuf, "from:", 5))
00153 #endif
00154               {
00155                      fprintf(submit_to, "%s", headerbuf);
00156                      if (!p)
00157                             while ((c=getchar()) != EOF && c != '\n')
00158                                    putc(c, submit_to);
00159                      putc('\n', submit_to);
00160                      continue;
00161               }
00162               if (!p)
00163                      while ((c=getchar()) != EOF && c != '\n')
00164                             ;      /* I don't care */
00165               if (seen_from)       continue;     /* Screwit */
00166               seen_from=1;
00167 
00168               i=strlen(headerbuf);
00169               for (;;)
00170               {
00171                      c=getchar();
00172                      if (c != EOF) ungetc(c, stdin);
00173                      if (c == EOF || c == '\r' || c == '\n')   break;
00174                      if (!isspace((int)(unsigned char)c))      break;
00175                      while ((c=getchar()) != EOF && c != '\n')
00176                      {
00177                             if (i < sizeof(headerbuf)-1)
00178                                    headerbuf[i++]=c;
00179                      }
00180                      headerbuf[i]=0;
00181               }
00182 
00183               p=rewrite_from(headerbuf+5, mailuser2, mailhost, pfrom);
00184               fprintf(submit_to, "From: %s\n", p);
00185               free(p);
00186        }
00187        if (!seen_from)
00188        {
00189               if (!mailuser)
00190               {
00191               struct passwd *pw=mypwd();
00192 
00193                      mailuser=pw ? pw->pw_name:"nobody";
00194               }
00195 
00196               if (!pfrom)
00197               {
00198                      if ( !(From=env("MAILNAME")) && !(From=env("NAME")))
00199                      {
00200                             pfrom=get_gecos();
00201                      }
00202                      else   pfrom=strcpy(courier_malloc(strlen(From)+1),
00203                                         From);
00204               }
00205 
00206               p=rewrite_from(NULL, mailuser, mailhost, pfrom);
00207               fprintf(submit_to, "From: %s\n", p);
00208               free(p);
00209        }
00210        putc('\n', submit_to);
00211        if (pfrom)    free(pfrom);
00212 }
00213 
00214 static char *rewrite_from(const char *oldfrom, const char *newuser,
00215        const char *newhost, const char *newname)
00216 {
00217 struct rfc822t *rfct;
00218 struct rfc822a *rfca;
00219 struct rfc822t *usert, *hostt, *namet;
00220 struct rfc822token attoken, **tp;
00221 char   *p;
00222 const char *q;
00223 char   *gecosname=0;
00224 
00225        if (!oldfrom)
00226        {
00227        char   *p=courier_malloc(
00228                      (newuser ? strlen(newuser):0)+
00229                      (newhost ? strlen(newhost):0)+4);
00230               strcpy(p, "<");
00231               if (newuser)  strcat(p, newuser);
00232               if (newuser && newhost)
00233                      strcat(strcat(p, "@"), newhost);
00234               strcat(p, ">");
00235               if (newname)
00236               {
00237               char *q, *r;
00238 
00239                      namet=tokenize_name(newname);
00240                      q=rfc822_gettok(namet->tokens);
00241                      rfc822t_free(namet);
00242                      r=courier_malloc(strlen(p)+strlen(q)+2);
00243                      strcat(strcat(strcpy(r, q), " "), p);
00244                      free(p);
00245                      p=r;
00246                      free(q);
00247               }
00248               return (p);
00249        }
00250 
00251        if ((rfct=rfc822t_alloc_new(oldfrom, NULL, NULL)) == 0 ||
00252               (rfca=rfc822a_alloc(rfct)) == 0)
00253        {
00254               clog_msg_errno();
00255               return(0);
00256        }
00257 
00258        if ((q=env("MAILNAME")) || (q=env("NAME")))
00259               newname=q;
00260 
00261        if (!newname && rfca->naddrs == 0)
00262               newname=gecosname=get_gecos();
00263 
00264        if ((rfca->naddrs == 0 || rfca->addrs[0].tokens == 0) && newuser == 0)
00265        {
00266        struct passwd *pw=mypwd();
00267 
00268               if (pw)       newuser=pw->pw_name;
00269        }
00270 
00271        namet=newname ? tokenize_name(newname):0;
00272        usert=newuser ? rw_rewrite_tokenize(newuser):0;
00273        hostt=newhost ? rw_rewrite_tokenize(newhost):0;
00274 
00275        if (rfca->naddrs == 0 || rfca->addrs[0].tokens == 0)
00276        {
00277        struct rfc822addr a;
00278        struct rfc822a       fakea;
00279 
00280               if (hostt)
00281               {
00282               struct rfc822token *t;
00283 
00284                      attoken.token='@';
00285                      attoken.next=hostt->tokens;
00286                      attoken.ptr=0;
00287                      attoken.len=0;
00288 
00289                      for (t=usert->tokens; t->next; t=t->next)
00290                             ;
00291                      t->next=&attoken;
00292               }
00293               fakea.naddrs=1;
00294               fakea.addrs= &a;
00295 
00296               if (!namet)   namet=tokenize_name("");
00297               if (!usert)   usert=rw_rewrite_tokenize("");
00298               a.name=namet->tokens;
00299               a.tokens=usert->tokens;
00300               p=rfc822_getaddrs(&fakea);
00301        }
00302        else
00303        {
00304        struct rfc822token *t, *u;
00305 
00306               rfca->naddrs=1;
00307               if (usert)
00308               {
00309                      for (t=rfca->addrs[0].tokens; t; t=t->next)
00310                             if (t->token == '@') break;
00311                      
00312                      for (u=usert->tokens; u->next; u=u->next)
00313                             ;
00314                      u->next=t;
00315                      rfca->addrs[0].tokens=usert->tokens;;
00316               }
00317 
00318               if (hostt && rfca->addrs[0].tokens)
00319               {
00320                      for (tp= &rfca->addrs[0].tokens; *tp;
00321                             tp= &(*tp)->next)
00322                             if ( (*tp)->token == '@')   break;
00323                      *tp=&attoken;
00324                      attoken.token='@';
00325                      attoken.next=hostt->tokens;
00326                      attoken.ptr=0;
00327                      attoken.len=0;
00328               }
00329               if (namet)
00330                      rfca->addrs[0].name=namet->tokens;
00331 
00332               p=rfc822_getaddrs(rfca);
00333        }
00334 
00335        if (!p)       clog_msg_errno();
00336 
00337        if (usert)    rfc822t_free(usert);
00338        if (hostt)    rfc822t_free(hostt);
00339        if (namet)    rfc822t_free(namet);
00340        rfc822t_free(rfct);
00341        rfc822a_free(rfca);
00342        if (gecosname)       free(gecosname);
00343        return (p);
00344 }
00345 
00346 static void exit_submit(int errcode)
00347 {
00348        if (errcode == -5)
00349               exit(EX_NOUSER);
00350 
00351        exit(EX_TEMPFAIL);
00352 }
00353 
00354 int main(int argc, char **argv)
00355 {
00356 const char *from=0;
00357 const char *From=0;
00358 const char *ret=0, *dsn=0, *security=0, *envid=0;
00359 int    argn, argp;
00360 int    errflag=0;
00361 int    c;
00362 char   *args[6];
00363 char   *envs[7];
00364 int    envp;
00365 int    tostdout=0;
00366 char   frombuf[NUMBUFSIZE+30];
00367 int    doverp=0;
00368 int    bcconly=0;
00369 char   ubuf[NUMBUFSIZE];
00370 char   *uucprmail=0;
00371 char   *s;
00372 char   ret_buf[2], security_buf[40];
00373 int     submit_errcode;
00374 
00375        /*
00376        ** Immediately drop uid, force GID to MAILGID
00377        ** The reason we can't just setgid ourselves to MAILGID is because
00378        ** that only sets the effective uid.  We need to also set both
00379        ** real and effective GIDs, otherwise maildrop filtering will fail,
00380        ** because the real gid won't be trusted.
00381        */
00382        setgid(MAILGID);
00383        setuid(getuid());
00384        signal(SIGCHLD, SIG_DFL);
00385        signal(SIGPIPE, SIG_IGN);
00386        argn=1;
00387 
00388        putenv("AUTHMODULES="); /* See module.local/local.c */
00389 
00390        putenv("LANG=en_US");
00391        putenv("CHARSET=iso-8859-1");
00392        putenv("MM_CHARSET=iso-8859-1");
00393 
00394 #if HAVE_SETLOCALE
00395        setlocale(LC_ALL, "C");
00396 #endif
00397 
00398        /* Only uucp can run rmail (this better be installed setgid) */
00399 
00400        if ((s=strrchr(argv[0], '/')) != 0)       ++s;
00401        else   s=argv[0];
00402        if (strcmp(s, "rmail") == 0)
00403        {
00404        struct passwd *p=mypwd();
00405        char   *uu_machine, *uu_user;
00406 
00407               if (!p || strcmp(p->pw_name, "uucp"))
00408               {
00409                      fprintf(stderr, "rmail: permission denied.\n");
00410                      exit(EX_NOPERM);
00411               }
00412 
00413               uu_machine=getenv("UU_MACHINE");
00414               uu_user=getenv("UU_USER");
00415 
00416               if (!uu_machine || !uu_user)
00417               {
00418                      fprintf(stderr,
00419                             "rmail: UU_MACHINE!UU_USER required.\n");
00420                      exit(EX_NOPERM);
00421               }
00422               uucprmail=malloc(strlen(uu_machine)+strlen(uu_user)+2);
00423               if (!uucprmail)
00424               {
00425                      perror("malloc");
00426                      exit(EX_TEMPFAIL);
00427               }
00428               strcat(strcat(strcpy(uucprmail, uu_machine), "!"), uu_user);
00429        }
00430 
00431        while (argn < argc)
00432        {
00433        const char *arg;
00434        int    c;
00435 
00436               if (argv[argn][0] != '-')   break;
00437 
00438               if (strcmp(argv[argn], "-") == 0)
00439               {
00440                      ++argn;
00441                      break;
00442               }
00443 
00444               if (uucprmail && strncmp(argv[argn], "-f", 2)
00445 
00446               /*
00447               ** Ok, obviously no UUCP version will give me the following
00448               ** options, must I can hope, can't I?
00449               */
00450                      && strcmp(argv[argn], "-verp")
00451                      && strncmp(argv[argn], "-N", 2)
00452                      && strncmp(argv[argn], "-R", 2)
00453                      && strncmp(argv[argn], "-V", 2)
00454 
00455                      /* Ignore the ignorable */
00456 
00457                      && strncmp(argv[argn], "-o", 2)
00458                      && strncmp(argv[argn], "-t", 2)
00459 
00460                      )
00461               {
00462                      fprintf(stderr, "rmail: invalid option %s\n",
00463                             argv[argn]);
00464                      exit(EX_NOPERM);
00465               }
00466 
00467               if (strcmp(argv[argn], "-verp") == 0)
00468               {
00469                      doverp=1;
00470                      ++argn;
00471                      continue;
00472               }
00473 
00474               if (strcmp(argv[argn], "-bcc") == 0)
00475               {
00476                      bcconly=1;
00477                      ++argn;
00478                      continue;
00479               }
00480 
00481               if (strcmp(argv[argn], "-bs") == 0)
00482               {
00483                      esmtpd();
00484               }
00485               switch (c=argv[argn][1])    {
00486               case 'o':
00487               case 'f':
00488               case 'F':
00489               case 'R':
00490               case 'N':
00491               case 'S':
00492               case 'V':
00493                      break;
00494               case 'n':
00495                      tostdout=1;
00496                      ++argn;
00497                      continue;
00498               default:
00499                      ++argn;
00500                      continue;
00501               }
00502               arg=argv[argn]+2;
00503               if (!*arg && argn+1 < argc)
00504                      arg=argv[++argn];
00505               ++argn;
00506 
00507               if (c == 'f')
00508                      from=arg;
00509               else if (c == 'F')
00510                      From=arg;
00511               else if (c == 'N')
00512                      dsn=arg;
00513               else if (c == 'S')
00514               {
00515                      char *q;
00516 
00517                      if (strcasecmp(arg, "NONE") &&
00518                          strcasecmp(arg, "STARTTLS"))
00519                      {
00520                             fprintf(stderr, "sendmail: invalid option"
00521                                    " -S %s\n",
00522                                    arg);
00523                             exit(EX_NOPERM);
00524                      }
00525                      strcpy(security_buf, arg);
00526 
00527                      for (q=security_buf; *q; q++)
00528                             *q=toupper((int)(unsigned char)*q);
00529 
00530                      security=security_buf;
00531               }
00532               else if (c == 'R')
00533               {
00534                      ret_buf[0]= toupper((int)(unsigned char)*arg);
00535                      ret_buf[1]= 0;
00536                      ret=ret_buf;
00537               }
00538               else if (c == 'V')
00539                      envid=arg;
00540        }
00541 
00542        sprintf(frombuf, "uid %s", libmail_str_uid_t(getuid(), ubuf));
00543        argp=0;
00544        args[argp++]="submit";
00545        if (bcconly)
00546               args[argp++]="-bcc";
00547 
00548        if (uucprmail)
00549        {
00550               if (argn >= argc)
00551               {
00552                      fprintf(stderr, "rmail: missing recipients\n");
00553                      exit(EX_NOPERM);
00554               }
00555 
00556               args[argp++]="uucp";
00557               s=malloc(sizeof("unknown; uucp ()")+strlen(uucprmail));
00558               if (!s)
00559               {
00560                      perror("malloc");
00561                      exit(EX_TEMPFAIL);
00562               }
00563               strcat(strcat(strcpy(s, "unknown; uucp ("), uucprmail), ")");
00564               args[argp++]=s;
00565        }
00566        else
00567        {
00568               args[argp++]="local";
00569               args[argp++]="dns; localhost (localhost [127.0.0.1])";
00570               args[argp++]=frombuf;
00571        }
00572        args[argp++]=0;
00573        envp=0;
00574 
00575        clog_open_stderr("sendmail");
00576        if (ret || security || doverp)
00577        {
00578               envs[envp]=strcat(strcat(strcpy(courier_malloc(strlen(ret ?
00579                      ret:"")+strlen(security ? security:"") + 30),
00580                                           "DSNRET="), (ret ? ret:"")),
00581                               (doverp ? "V":""));
00582               if (security)
00583                      strcat(strcat(strcat(envs[envp], "S{"),
00584                                   security), "}");
00585 
00586               ++envp;
00587        }
00588 
00589        if (dsn)
00590        {
00591               envs[envp]=strcat(strcpy(courier_malloc(strlen(dsn)+11),
00592                      "DSNNOTIFY="), dsn);
00593               ++envp;
00594        }
00595        if (envid)
00596        {
00597               envs[envp]=strcat(strcpy(courier_malloc(strlen(envid)+10),
00598                      "DSNENVID="), envid);
00599               ++envp;
00600        }
00601        envs[envp++]="TCPREMOTEHOST=localhost";
00602        envs[envp++]="TCPREMOTEIP=127.0.0.1";
00603 
00604        if (!uucprmail)
00605               envs[envp++]=strcat(strcpy(courier_malloc(strlen(frombuf)+
00606                             sizeof("TCPREMOTEINFO=")),
00607                             "TCPREMOTEINFO="), frombuf);
00608        envs[envp]=0;
00609 
00610        if (!tostdout && submit_fork(args, envs, submit_print_stdout))
00611        {
00612               fprintf(stderr, "sendmail: Service temporarily unavailable.\n");
00613               exit(EX_TEMPFAIL);
00614        }
00615 
00616        if (tostdout)
00617               submit_to=stdout;
00618        else
00619        {
00620               if (uucprmail)
00621               {
00622                      submit_write_message(from ? from:"");
00623               }
00624               else
00625               {
00626               char   *p;
00627 
00628                      p=rewrite_env_sender(from);
00629                      submit_write_message(p);
00630                      free(p);
00631               }
00632               if ((submit_errcode=submit_readrcprinterr()) != 0)
00633               {
00634                      exit_submit(submit_errcode);
00635               }
00636        }
00637 
00638        while (!tostdout && argn < argc)
00639        {
00640               submit_write_message(argv[argn]);
00641               if ((errflag=submit_readrcprinterr()) != 0)
00642               {
00643                      fprintf(stderr, "%s: invalid address.\n", argv[argn]);
00644               }
00645               ++argn;
00646        }
00647        if (errflag)  exit_submit(errflag);
00648        if (!tostdout)
00649               putc('\n', submit_to);
00650 
00651        if (!uucprmail)
00652               rewrite_headers(From);
00653 
00654        while ((c=getchar()) != EOF)
00655        {
00656               putc(c, submit_to);
00657        }
00658        fflush(submit_to);
00659        signal(SIGINT, SIG_IGN);
00660        signal(SIGTERM, SIG_IGN);
00661        signal(SIGHUP, SIG_IGN);
00662 
00663        errflag=0;
00664        if (ferror(submit_to) || (!tostdout && (
00665               fclose(submit_to) ||
00666               (errflag=submit_readrcprinterr()) || submit_wait()))
00667               )
00668        {
00669               fprintf(stderr, "sendmail: Unable to submit message.\n");
00670               if (errflag)
00671                      exit_submit(errflag);
00672               exit(EX_TEMPFAIL);
00673        }
00674        exit(0);
00675 }