Advertisement
Guest User

Untitled

a guest
Jun 28th, 2017
124
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 31.53 KB | None | 0 0
  1. /************************************************************************
  2.  *  procmail - The autonomous mail processor            *
  3.  *                                  *
  4.  *  It has been designed to be able to be run suid root and (in *
  5.  *  case your mail spool area is *not* world writable) sgid     *
  6.  *  mail (or daemon), without creating security holes.      *
  7.  *                                  *
  8.  *  Seems to be perfect.                        *
  9.  *                                  *
  10.  *  Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands *
  11.  *  Copyright (c) 1999-2001, Philip Guenther, The United States *
  12.  *                          of America  *
  13.  *  #include "../README"                        *
  14.  ************************************************************************/
  15. #ifdef RCS
  16. static /*const*/char rcsid[]=
  17.  "$Id: procmail.c,v 1.1.1.3 2003/10/14 23:13:23 rbraun Exp $";
  18. #endif
  19. #include "../patchlevel.h"
  20. #include "procmail.h"
  21. #include "acommon.h"
  22. #include "sublib.h"
  23. #include "robust.h"
  24. #include "shell.h"
  25. #include "misc.h"
  26. #include "memblk.h"
  27. #include "pipes.h"
  28. #include "common.h"
  29. #include "cstdio.h"
  30. #include "exopen.h"
  31. #include "mcommon.h"
  32. #include "goodies.h"
  33. #include "locking.h"
  34. #include "mailfold.h"
  35. #include "lastdirsep.h"
  36. #include "authenticate.h"
  37. #include "lmtp.h"
  38. #include "foldinfo.h"
  39. #include "variables.h"
  40. #include "comsat.h"
  41. #include "from.h"
  42.  
  43. static const char*const nullp,exflags[]=RECFLAGS,drcfile[]="Rcfile:",
  44.  pmusage[]=PM_USAGE,*etcrc=ETCRC,misrecpt[]="Missing recipient\n",
  45.  extrns[]="Extraneous ",ignrd[]=" ignored\n",pardir[]=chPARDIR,
  46.  defspath[]=DEFSPATH,defmaildir[]=DEFmaildir;
  47. char*buf,*buf2,*loclock;
  48. const char shell[]="SHELL",lockfile[]="LOCKFILE",newline[]="\n",binsh[]=BinSh,
  49.  unexpeof[]="Unexpected EOL\n",*const*gargv,*const*restargv= &nullp,*sgetcp,
  50.  pmrc[]=PROCMAILRC,*rcfile,dirsep[]=DIRSEP,devnull[]=DevNull,empty[]="",
  51.  lgname[]="LOGNAME",executing[]="Executing",oquote[]=" \"",cquote[]="\"\n",
  52.  procmailn[]="procmail",whilstwfor[]=" whilst waiting for ",home[]="HOME",
  53.  host[]="HOST",*defdeflock=empty,*argv0=empty,curdir[]={chCURDIR,'\0'},
  54.  slogstr[]="%s \"%s\"",conflicting[]="Conflicting ",orgmail[]="ORGMAIL",
  55.  insufprivs[]="Insufficient privileges\n",defpath[]=DEFPATH,
  56.  exceededlb[]="Exceeded LINEBUF\n",errwwriting[]="Error while writing to",
  57.  Version[]=VERSION;
  58. int retval=EX_CANTCREAT,retvl2=EXIT_SUCCESS,sh,pwait,rc= -1,
  59.  privileged=priv_START,lexitcode=EXIT_SUCCESS,ignwerr,crestarg,savstdout,
  60.  berkeley,mailfilter,erestrict,Deliverymode,ifdepth;   /* depth of outermost */
  61. struct dyna_array ifstack;
  62. size_t linebuf=mx(DEFlinebuf,1024/*STRLEN(systm_mbox)<<1*/);
  63. volatile int nextexit,lcking;              /* if termination is imminent */
  64. pid_t thepid;
  65. long filled,lastscore;         /* the length of the mail, and the last score */
  66. memblk themail;                          /* the mail */
  67. char*thebody;                     /* the body of the message */
  68. uid_t uid;
  69. gid_t gid,sgid;
  70.  
  71. static auth_identity*savepass(spass,uid)auth_identity*const spass;
  72.  const uid_t uid;
  73. { const auth_identity*tpass;
  74.   if(auth_filledid(spass)&&auth_whatuid(spass)==uid)
  75.      goto ret;
  76.   if(tpass=auth_finduid(uid,0))               /* save by copying */
  77.    { auth_copyid(spass,tpass);
  78. ret: return spass;
  79.    }
  80.   return (auth_identity*)0;
  81. }
  82.  
  83. #define rct_ABSOLUTE    0               /* rctypes for tryopen() */
  84. #define rct_CURRENT 1
  85. #define rct_DEFAULT 2
  86.  
  87. #define rcs_DELIVERED   1            /* rc exit codes for mainloop() */
  88. #define rcs_EOF     2
  89. #define rcs_HOST    3
  90.  
  91. static void
  92.  usage P((void));
  93. static int
  94.  tryopen P((const int delay_setid,const int rctype,const int dowarning)),
  95.  mainloop P((void));
  96.  
  97. int main(argc,argv)int argc;const char*const argv[];
  98. { register char*chp,*chp2;
  99. #if 0               /* enable this if you want to trace procmail */
  100.   kill(getpid(),SIGSTOP);/*raise(SIGSTOP);*/
  101. #endif
  102.   newid();
  103.   ;{ int presenviron,override;char*fromwhom=0;
  104.      const char*idhint=0;gid_t egid=getegid();
  105.      presenviron=Deliverymode=mailfilter=override=0;
  106.      Openlog(procmailn,LOG_PID,LOG_MAIL);         /* for the syslogd */
  107.      if(argc)                  /* sanity check, any argument at all? */
  108.       { Deliverymode=!!strncmp(lastdirsep(argv0=argv[0]),procmailn,
  109.      STRLEN(procmailn));
  110.     for(argc=0;(chp2=(char*)argv[++argc])&&*chp2=='-';)
  111.        for(;;)                     /* processing options */
  112.         { switch(*++chp2)
  113.            { case VERSIONOPT:
  114.             usage();
  115.             return EXIT_SUCCESS;
  116.          case HELPOPT1:case HELPOPT2:elog(pmusage);elog(PM_HELP);
  117.             elog(PM_QREFERENCE);
  118.             return EX_USAGE;
  119.          case PRESERVOPT:presenviron=1;
  120.             continue;
  121.          case MAILFILTOPT:mailfilter=1;
  122.             continue;
  123.          case OVERRIDEOPT:override=1;
  124.             continue;
  125.          case BERKELEYOPT:case ALTBERKELEYOPT:berkeley=1;
  126.             continue;
  127.          case TEMPFAILOPT:retval=EX_TEMPFAIL;
  128.             continue;
  129.          case FROMWHOPT:case ALTFROMWHOPT:
  130.             if(*++chp2)
  131.                fromwhom=chp2;
  132.             else if(chp2=(char*)argv[argc+1])
  133.                argc++,fromwhom=chp2;
  134.             else
  135.                nlog("Missing name\n");
  136.             break;
  137.          case ARGUMENTOPT:
  138.           { static struct dyna_array newargv;
  139.             if(*++chp2)
  140.                goto setarg;
  141.             else if(chp2=(char*)argv[argc+1])
  142.              { argc++;
  143. setarg:            app_valp(newargv,(const char*)chp2);
  144.                restargv=&(acc_valp(newargv,0));
  145.                crestarg++;
  146.              }
  147.             else
  148.                nlog("Missing argument\n");
  149.             break;
  150.           }
  151.          case DELIVEROPT:
  152.             if(!*(chp= ++chp2)&&!(chp=(char*)argv[++argc]))
  153.              { nlog(misrecpt);
  154.                break;
  155.              }
  156.             else
  157.              { Deliverymode=1;
  158.                goto last_option;
  159.              }
  160.          case LMTPOPT:
  161. #ifdef LMTP
  162.             Deliverymode=2;
  163.             goto last_option;
  164. #else
  165.             nlog("LMTP support not enabled in this binary\n");
  166.             return EX_USAGE;
  167. #endif
  168.          case '-':
  169.             if(!*++chp2)
  170.              { argc++;
  171.                goto last_option;
  172.              }
  173.          default:nlog("Unrecognised options:");logqnl(chp2);
  174.             elog(pmusage);elog("Processing continued\n");
  175.          case '\0':;
  176.            }
  177.           break;
  178.         }
  179.       }
  180.      if(Deliverymode==1&&!(chp=chp2))
  181.     nlog(misrecpt),Deliverymode=0;
  182. last_option:
  183.      switch(Deliverymode)
  184.       { case 0:
  185.        idhint=getenv(lgname);
  186.        if(mailfilter&&crestarg)
  187.         { crestarg=0;                /* -m will supersede -a */
  188. conflopt:     nlog(conflicting);elog("options\n");elog(pmusage);
  189.         }
  190.        break;
  191. #ifdef LMTP
  192.     case 2:
  193.        if(fromwhom)
  194.         { fromwhom=0;                 /* -z disables -f, */
  195.           goto confldopt;                   /* -p and -m */
  196.         }
  197. #endif
  198.     case 1:
  199.        if(presenviron||mailfilter)
  200. confldopt:  { presenviron=mailfilter=0;         /* -d disables -p and -m */
  201.           goto conflopt;
  202.         }
  203.        break;
  204.     default:                       /* this cannot happen */
  205.        abort();
  206.       }
  207.      cleanupenv(presenviron);
  208.      ;{ auth_identity*pass,*passinvk;auth_identity*spassinvk;
  209.     uid_t euid=geteuid();
  210.     uid=getuid();gid=getgid();
  211.     spassinvk=auth_newid();passinvk=savepass(spassinvk,uid);   /* are we */
  212.     checkprivFrom_(euid,passinvk?auth_username(passinvk):0,override);
  213.     doumask(INIT_UMASK);           /* allowed to set the From_ line? */
  214.     while((savstdout=rdup(STDOUT))<=STDERR)
  215.      { rclose(savstdout);              /* move stdout out of the way */
  216.        if(0>(savstdout=opena(devnull)))
  217.           goto nodevnull;
  218.        syslog(LOG_ALERT,"Descriptor %d was not open\n",savstdout);
  219.      }
  220.     fclose(stdout);rclose(STDOUT);          /* just to make sure */
  221.     if(0>opena(devnull))
  222. nodevnull:
  223.      { writeerr(devnull);syslog(LOG_EMERG,slogstr,errwwriting,devnull);
  224.        return EX_OSFILE;              /* couldn't open /dev/null */
  225.      }
  226. #ifdef console
  227.     opnlog(console);
  228. #endif
  229.     setbuf(stdin,(char*)0);allocbuffers(linebuf,1);
  230. #ifdef SIGXCPU
  231.     signal(SIGXCPU,SIG_IGN);signal(SIGXFSZ,SIG_IGN);
  232. #endif
  233. #ifdef SIGLOST
  234.     signal(SIGLOST,SIG_IGN);
  235. #endif
  236. #if DEFverbose
  237.     verboff();verbon();
  238. #else
  239.     verbon();verboff();
  240. #endif
  241. #ifdef SIGCHLD
  242.     signal(SIGCHLD,SIG_DFL);
  243. #endif
  244.     signal(SIGPIPE,SIG_IGN);
  245.     setcomsat(empty);             /* turn on biff by default */
  246.     ultstr(0,(unsigned long)uid,buf);filled=0;
  247.     if(!passinvk||!(chp2=(char*)auth_username(passinvk)))
  248.        chp2=buf;
  249. #ifdef LMTP
  250.     if(Deliverymode==2)
  251.      { auth_identity**rcpts,**lastrcpt,**currcpt;
  252.        currcpt=rcpts=lmtp(&lastrcpt,chp2);
  253.        if(currcpt+1!=lastrcpt)     /* if there's more than one recipient */
  254.           lockblock(&themail);     /* then no one can write to the block */
  255.        else                  /* otherwise the only recipient */
  256.           private(1);               /* gets the original */
  257.        while(currcpt<lastrcpt)
  258.         { if(!(pidchild=sfork()))
  259.            { setupsigs();
  260.          pass= *currcpt++;
  261.          while(currcpt<lastrcpt)
  262.             auth_freeid(*currcpt++);
  263.          freeoverread();
  264.          free(rcpts);newid();gargv=&nullp;
  265.          goto dorcpt;
  266.            }
  267.           if(forkerr(pidchild,procmailn))
  268.          lexitcode=EX_OSERR;
  269.           else
  270.          waitfor(pidchild);
  271.           lmtpresponse(lexitcode);
  272.           pidchild=0;
  273.           auth_freeid(*currcpt++);
  274.         }
  275.        free(rcpts);
  276.        flushoverread();      /* pass upwards the extra LMTP data */
  277.        exit(EXIT_SUCCESS);
  278.      }
  279. #endif
  280.     setupsigs();
  281.     makeFrom(fromwhom,chp2);
  282.     readmail(0,0L);               /* read in the mail completely */
  283.     if(Deliverymode)
  284.      { if(argv[argc+1])          /* more than one recipient? */
  285.         { private(0);                   /* time to share */
  286.           lockblock(&themail);
  287.         }
  288.        else
  289.           private(1);
  290.        do             /* chp should point to the first recipient */
  291.         { chp2=chp;
  292.           if(argv[++argc])            /* more than one recipient */
  293.          if(pidchild=sfork())
  294.           { if(forkerr(pidchild,procmailn)||
  295.                waitfor(pidchild)!=EXIT_SUCCESS)
  296.                retvl2=retval;
  297.             pidchild=0;           /* loop for the next recipient */
  298.           }
  299.          else
  300.           { newid();
  301.             while(argv[++argc]);    /* skip till end of command line */
  302.             break;
  303.           }
  304.         }
  305.        while(chp=(char*)argv[argc]);
  306.      }
  307.     gargv=argv+argc;             /* save it for nextrcfile() */
  308.     if(Deliverymode)    /* try recipient without changing case first */
  309.      { if(!(pass=auth_finduser(chp2,-1)))       /* chp2 is the recipient */
  310.         { static const char unkuser[]="Unknown user";
  311.           nlog(unkuser);logqnl(chp2);syslog(LOG_ERR,slogstr,unkuser,chp2);
  312.           return EX_NOUSER;         /* we don't handle strangers */
  313.         }
  314. dorcpt:    if(enoughprivs(passinvk,euid,egid,auth_whatuid(pass),
  315.         auth_whatgid(pass)))
  316.           goto Setuser;
  317.        nlog(insufprivs);
  318.        syslog(LOG_CRIT,"Insufficient privileges to deliver to \"%s\"\n",
  319.         chp2);
  320.        return EX_NOPERM;          /* need more mana, decline the request */
  321.      }
  322.     else
  323.      { int commandlinerc=nextrcfile();
  324.        if(presenviron)            /* preserving the environment? */
  325.           etcrc=0;                  /* don't read etcrc then */
  326.        if(commandlinerc)                 /* command-line rcfile? */
  327.         { etcrc=0;               /* forget etcrc and comsat: */
  328.           setcomsat(DEFcomsat);         /* the internal flag */
  329.           if(!presenviron)           /* and usually the variable */
  330.          setdef(scomsat,DEFcomsat);
  331.         }
  332.        if(mailfilter)
  333.         { if(!commandlinerc)
  334.            { nlog("Missing rcfile\n");
  335.          return EX_NOINPUT;
  336.            }
  337. #ifdef ETCRCS
  338.           ;{ static const char etcrcs[]=ETCRCS;
  339.          if(!strncmp(etcrcs,rcfile,STRLEN(etcrcs)))
  340.           { struct stat stbuf; /* path starts with /etc/procmailrcs/ */
  341.            /*
  342.             *   although the filename starts with /etc/procmailrcs/
  343.             *   we will now check if it does not contain any backward
  344.             *   references which would allow it to escape the secure
  345.             *   tree; look for /../ sequences
  346.             */
  347.             for(chp=(char*)rcfile+STRLEN(etcrcs)-1;
  348.             chp;               /* any devious embedded /../? */
  349.             chp=strpbrk(chp,dirsep))
  350.                if(!strncmp(pardir,++chp,STRLEN(pardir))&&
  351.               (chp+=STRLEN(pardir),strchr(dirsep,*chp)))
  352.               goto nospecial;     /* yes, security violation */
  353. #ifdef CAN_chown
  354.             *(chp=strcpy(buf2,etcrcs)+STRLEN(etcrcs))=chCURDIR;
  355.             *++chp='\0';
  356. #endif
  357.            /*
  358.             *   so far, so good, now verify the credentials down to the
  359.             *   last bit
  360.             */
  361.             if(presenviron||              /* -p is dangerous */
  362.                commandlinerc!=2||     /* so are variable assignments */
  363. #ifdef CAN_chown          /* anyone can chown in this filesystem so: */
  364.                stat(buf2,&stbuf)||       /* the /etc/procmailrcs */
  365.                !S_ISDIR(stbuf.st_mode)||    /* directory must be */
  366.                stbuf.st_uid!=ROOT_uid&&         /* owned by root */
  367.             chown(buf2,ROOT_uid,stbuf.st_gid)||
  368.                stbuf.st_mode&(S_IXGRP|S_IXOTH)&&   /* and accessible */
  369.             chmod(buf2,S_IRWXU)||          /* to no one else */
  370. #endif
  371.                lstat(rcfile,&stbuf)||       /* it seems to exist */
  372.                !enoughprivs(passinvk,euid,egid,stbuf.st_uid,
  373.             stbuf.st_gid)||        /* can we do this at all? */
  374.                S_ISDIR(stbuf.st_mode)||       /* no directories! */
  375.                !savepass(spassinvk,stbuf.st_uid)     /* user exists? */
  376.               )
  377. nospecial:       { static const char densppr[]=
  378.             "Denying special privileges for";
  379.                nlog(densppr);logqnl(rcfile);
  380.                syslog(LOG_ALERT,slogstr,densppr,rcfile);
  381.              }
  382.             else                /* no security violation */
  383.                mailfilter=2,passinvk=spassinvk;
  384.           }                   /* accept new identity */
  385.            }
  386. #endif
  387.         }
  388.      }
  389.     pass=passinvk;
  390.     if(passinvk&&idhint)          /* if same uid as $LOGNAME, use latter */
  391.      { auth_identity*idpass=auth_finduser((char*)idhint,0);
  392.        if(idpass)
  393.         { if(auth_whatuid(passinvk)==auth_whatuid(idpass))
  394.          pass=idpass;
  395.         }
  396.      }
  397.     if(pass)          /* set preferred uid to the intended recipient */
  398. Setuser: { gid=auth_whatgid(pass);uid=auth_whatuid(pass);
  399.        if(euid==ROOT_uid&&(chp=(char*)auth_username(pass))&&*chp)
  400.           initgroups(chp,gid);
  401.        endgrent();
  402.      }
  403.     else                      /* user could not be found */
  404.        setids();   /* to prevent security holes, drop any privileges now */
  405.     initdefenv(pass,buf,!presenviron||!mailfilter);      /* override */
  406.     endpwent();auth_freeid(spassinvk);     /* environment by default */
  407.       }
  408.      if(buildpath(orgmail,fdefault,(char*)0))   /* setup DEFAULT and ORGMAIL */
  409.       { fdefault=empty;            /* uh, Houston, we have a problem */
  410.     goto nix_sysmbox;
  411.       }
  412.      /*
  413.       * Processing point of proposed /etc/procmail.conf file
  414.       */
  415.      fdefault=tstrdup(buf);sgid=egid;
  416.      chp=(char*)tgetenv(orgmail);
  417.      if(mailfilter||!screenmailbox(chp,egid,Deliverymode))
  418. nix_sysmbox:
  419.       { sputenv(orgmail);        /* nix delivering to system mailbox */
  420.     if(privileged)                 /* don't need this any longer */
  421.        privileged=priv_DONTNEED;
  422.     if(!strcmp(chp,fdefault))           /* DEFAULT the same? */
  423.        free((char*)fdefault),fdefault=empty;         /* so panic */
  424.       }                     /* bad news, be conservative */
  425.    }
  426.   doumask(INIT_UMASK);
  427.   while(chp=(char*)argv[argc])         /* interpret command line specs first */
  428.    { argc++;
  429.      if(!asenvcpy(chp)&&mailfilter)
  430.       { gargv= &nullp;               /* stop at the first rcfile */
  431.     for(restargv=argv+argc;restargv[crestarg];crestarg++);
  432.     break;
  433.       }
  434.    }
  435.   if(etcrc)       /* do we start with an /etc/procmailrc file first? */
  436.    { if(0<=bopen(etcrc))
  437.       { yell(drcfile,etcrc);
  438. #if !DEFverbose
  439.     if(privileged)
  440.        verbose=0;               /* no peeking in /etc/procmailrc */
  441. #endif
  442.     eputenv(defspath,buf);                /* use the secure PATH */
  443.     if(mainloop()==rcs_DELIVERED)              /* run the rcfile */
  444.        goto mailed;
  445.     eputenv(defpath,buf);        /* switch back to the insecure PATH */
  446.       }
  447.    }
  448.   erestrict=mailfilter!=2;            /* possibly restrict execs now */
  449.   if(rcfile&&!mailfilter)                /* "procmail rcfile..." */
  450.    { int rctype,dowarning;   /* only warn about the first missing rcfile */
  451.      for(dowarning=1;;)
  452.       { rctype=rct_ABSOLUTE;
  453.     if(strchr(dirsep,*rcfile))              /* absolute? */
  454.        strcpy(buf,rcfile);
  455.     else if(*rcfile==chCURDIR&&strchr(dirsep,rcfile[1]))   /* ./ prefix? */
  456.        strcpy(buf,rcfile),rctype=rct_CURRENT;
  457.     else                 /* prepend default procmailrc directory */
  458.        if(buildpath(maildir,defmaildir,rcfile))
  459.           break;
  460.     if(tryopen(0,rctype,dowarning))
  461.      { register int rcs=mainloop();                /* run it */
  462.        if(rcs==rcs_DELIVERED)
  463.           goto mailed;                   /* success! */
  464.        if(rcs==rcs_EOF)
  465.           break;                     /* normal end of rcfile */
  466.        if(!nextrcfile())                       /* none left? */
  467.           goto mailed;                   /* then out */
  468.      }
  469.     else                      /* not available? try the next */
  470.      { dowarning=0;             /* suppress further messages */
  471.        if(!nextrcfile())                       /* none left? */
  472.           break;                         /* then out */
  473.      }
  474.       }
  475.    }
  476.   else
  477.    { int rctype;
  478.      if(!rcfile)                /* no rcfile on the command line */
  479.       { rctype=rct_DEFAULT;
  480.     if(buildpath("default rcfile",pmrc,(char*)0))
  481.        goto nomore_rc;
  482.       }
  483.      else                         /* mailfilter mode */
  484.       { rctype=strchr(dirsep,*rcfile)?rct_ABSOLUTE:rct_CURRENT;
  485.     strcpy(buf,rcfile);
  486.       }
  487.      if(tryopen(mailfilter==2,rctype,DEFverbose||mailfilter))
  488.     if(mainloop()!=rcs_EOF)
  489.        goto mailed;
  490.    }
  491. nomore_rc:
  492.   if(ifstack.vals)
  493.      free(ifstack.vals);
  494.   ;{ int succeed;
  495.      concon('\n');succeed=0;
  496.      if(*(chp=(char*)fdefault))                  /* DEFAULT set? */
  497.       { int len;
  498.     setuid(uid);               /* make sure we have enough space */
  499.     if(linebuf<(len=strlen(chp)+strlen(lockext)+UNIQnamelen))
  500.        allocbuffers(linebuf=len,1);    /* to perform the lock & delivery */
  501.     if(writefolder(chp,(char*)0,themail.p,filled,0,1))    /* default */
  502.        succeed=1;
  503.       }                            /* if all else failed */
  504.      if(!succeed&&*(chp2=(char*)tgetenv(orgmail))&&strcmp(chp2,chp))
  505.       { rawnonl=0;
  506.     if(writefolder(chp2,(char*)0,themail.p,filled,0,0))   /* don't panic */
  507.        succeed=1;                     /* try the last resort */
  508.       }
  509.      if(succeed)                     /* should we panic now? */
  510. mailed: retval=EXIT_SUCCESS;          /* we're home free, mail delivered */
  511.    }
  512.   unlock(&loclock);Terminate();
  513. }
  514.  
  515. static void usage P((void))
  516. { elog(procmailn);elog(Version);
  517.   elog("\nLocking strategies:\tdotlocking");
  518. #ifndef NOfcntl_lock
  519.   elog(", fcntl()");                    /* a peek under the hood */
  520. #endif
  521. #ifdef USElockf
  522.   elog(", lockf()");
  523. #endif
  524. #ifdef USEflock
  525.   elog(", flock()");
  526. #endif
  527.   elog("\nDefault rcfile:\t\t");elog(pmrc);
  528. #ifdef GROUP_PER_USER
  529.   elog("\n\tIt may be writable by your primary group");
  530. #endif
  531.   elog("\nYour system mailbox:\t");
  532.   elog(auth_mailboxname(auth_finduid(getuid(),0)));
  533.   elog(newline);
  534. #ifdef USE_MMAP
  535.   elog("\nLarge messages will be memory mapped during processing.\n");
  536. #endif
  537. }
  538.  
  539. /*
  540.  *  if we happen to be still running as root, and the rcfile
  541.  *  is mounted on a secure NFS-partition, we might not be able
  542.  *  to access it, so check if we can stat it or don't need any
  543.  *  sgid privileges, if yes, drop all privs and set uid to
  544.  *  the recipient beforehand
  545.  */
  546. static int tryopen(delay_setid,rctype,dowarning)
  547.  const int delay_setid,rctype,dowarning;
  548. { struct stat stbuf;
  549.   if(!delay_setid&&privileged&&   /* if we can setid now and we haven't yet, */
  550.       (privileged==priv_DONTNEED||!stat(buf,&stbuf))) /* and we either don't */
  551.      setids();     /* need the privileges or it's accessible, then setid now */
  552.   if(0>bopen(buf))                 /* try opening the rcfile */
  553.    { if(dowarning)
  554. rerr:   readerr(buf);
  555.      return 0;
  556.    }
  557.   if(!delay_setid&&privileged)         /* if we're not supposed to delay */
  558.    { closerc();            /* and we haven't changed yet, then close it, */
  559.      setids();               /* transmogrify to prevent peeking, */
  560.      if(0>bopen(buf))                       /* and try again */
  561.     goto rerr;         /* they couldn't read it, so it was bogus */
  562.    }
  563. #ifndef NOfstat
  564.   if(fstat(rc,&stbuf))                      /* the right way */
  565. #else
  566.   if(stat(buf,&stbuf))                    /* the best we can */
  567. #endif
  568.    { static const char susprcf[]="Suspicious rcfile";
  569. suspicious_rc:
  570.      closerc();nlog(susprcf);logqnl(buf);
  571.      syslog(LOG_ALERT,slogstr,susprcf,buf);
  572.      goto rerr;
  573.    }
  574.   if(delay_setid)            /* change now if we haven't already */
  575.      setids();
  576.   if(rctype==rct_CURRENT)     /* opened rcfile in the current directory? */
  577.    { if(!didchd)
  578.     setmaildir(curdir);
  579.    }
  580.   else
  581.     /*
  582.      * OK, so now we have opened an absolute rcfile, but for security
  583.      * reasons we only accept it if it is owned by the recipient or
  584.      * root and is not world writable, and the directory it is in is
  585.      * not world writable or has the sticky bit set.  If this is the
  586.      * default rcfile then we also outlaw group writability.
  587.      */
  588.    { register char*chp=lastdirsep(buf),c;
  589.      c= *chp;
  590.      if(((stbuf.st_uid!=uid&&stbuf.st_uid!=ROOT_uid||          /* check uid, */
  591.       (stbuf.st_mode&S_IWOTH)||           /* writable by others, */
  592.       rctype==rct_DEFAULT&&        /* if the default then also check */
  593.        (stbuf.st_mode&S_IWGRP)&&           /* for writable by group, */
  594.        (NO_CHECK_stgid||stbuf.st_gid!=gid)
  595.      )&&strcmp(devnull,buf)||        /* /dev/null is a special case, */
  596.     (*chp='\0',stat(buf,&stbuf))||           /* check the directory, */
  597. #ifndef CAN_chown                  /* sticky and can't chown */
  598.     !(stbuf.st_mode&S_ISVTX)&&         /* means we don't care if */
  599. #endif                       /* it's group or world writable */
  600.     ((stbuf.st_mode&(S_IWOTH|S_IXOTH))==(S_IWOTH|S_IXOTH)||
  601.      rctype==rct_DEFAULT&&
  602.       (stbuf.st_mode&(S_IWGRP|S_IXGRP))==(S_IWGRP|S_IXGRP)&&
  603.       (NO_CHECK_stgid||stbuf.st_gid!=gid))))
  604.       { *chp=c;
  605.     goto suspicious_rc;
  606.       }
  607.      *chp=c;
  608.    }
  609.   yell(drcfile,buf);
  610.   /*
  611.    *    Chdir now if we haven't already
  612.    */
  613.   if(!didchd)                      /* have we done this already? */
  614.    { const char*chp;
  615.      if(buildpath(maildir,defmaildir,(char*)0))
  616.     exit(EX_OSERR);        /* something was wrong: give up the ghost */
  617.      if(chdir(chp=buf))
  618.       { chderr(buf);              /* no, well, then try an initial chdir */
  619.     chp=tgetenv(home);
  620.     if(!strcmp(chp,buf)||chdir(chp))
  621.        chderr(chp),chp=curdir;      /* that didn't work, use "." */
  622.       }
  623.      setmaildir(chp);
  624.    }
  625.   return 1;                      /* we're good to go */
  626. }
  627.  
  628. static int mainloop P((void))
  629. { int lastsucc,lastcond,prevcond,i,skiprc;register char*chp,*tolock;
  630.   lastsucc=lastcond=prevcond=skiprc=0;
  631.   tolock=0;
  632.   do
  633.    { unlock(&loclock);              /* unlock any local lockfile */
  634.      goto commint;
  635.      do
  636.       { skipline();
  637. commint:do skipspace();                   /* skip whitespace */
  638.     while(testB('\n'));
  639.       }
  640.      while(testB('#'));                    /* no comment :-) */
  641.      if(testB(':'))                    /* check for a recipe */
  642.       { int locknext,succeed;char*startchar;long tobesent;
  643.     static char flags[maxindex(exflags)];
  644.     do
  645.      { int nrcond;
  646.        if(readparse(buf,getb,0,skiprc))
  647.           return rcs_EOF;                 /* give up on this one */
  648.        ;{ char*temp;             /* so that chp isn't forced */
  649.           nrcond=strtol(buf,&temp,10);chp=temp;       /* into memory */
  650.         }
  651.        if(chp==buf)                  /* no number parsed */
  652.           nrcond= -1;
  653.        if(tolock)        /* clear temporary buffer for lockfile name */
  654.           free(tolock);
  655.        for(i=maxindex(flags);i;i--)           /* clear the flags */
  656.           flags[i]=0;
  657.        for(tolock=0,locknext=0;;)
  658.         { chp=skpspace(chp);
  659.           switch(i= *chp++)
  660.            { default:
  661.             ;{ char*flg;
  662.                if(!(flg=strchr(exflags,i)))     /* a valid flag? */
  663.             { chp--;
  664.               break;
  665.             }
  666.                flags[flg-exflags]=1;             /* set the flag */
  667.              }
  668.          case '\0':
  669.             if(chp!=Tmnate)     /* if not the real end, skip */
  670.                continue;
  671.             break;
  672.          case ':':locknext=1;       /* yep, local lockfile specified */
  673.             if(*chp||++chp!=Tmnate)
  674.                tolock=tstrdup(chp),chp=strchr(chp,'\0')+1;
  675.            }
  676.           concatenate(chp);skipped(chp);        /* display leftovers */
  677.           break;
  678.         }                     /* parse & test the conditions */
  679.        i=conditions(flags,prevcond,lastsucc,lastcond,skiprc!=0,nrcond);
  680.        if(!skiprc)
  681.         { if(!flags[ALSO_NEXT_RECIPE]&&!flags[ALSO_N_IF_SUCC])
  682.          lastcond=i==1;        /* save the outcome for posterity */
  683.           if(!prevcond||!flags[ELSE_DO])
  684.          prevcond=i==1;       /* ditto for `else if' like constructs */
  685.         }
  686.      }
  687.     while(i==2);                 /* missing in action, reiterate */
  688.     startchar=themail.p;tobesent=filled;
  689.     if(flags[PASS_HEAD])                /* body, header or both? */
  690.      { if(!flags[PASS_BODY])
  691.           tobesent=thebody-themail.p;
  692.      }
  693.     else if(flags[PASS_BODY])
  694.        tobesent-=(startchar=thebody)-themail.p;
  695.     Stdout=0;succeed=sh=0;
  696.     pwait=flags[WAIT_EXIT]|flags[WAIT_EXIT_QUIET]<<1;
  697.     ignwerr=flags[IGNORE_WRITERR];skipspace();
  698.     if(i)
  699.        zombiecollect(),concon('\n');
  700. progrm: if(testB('!'))                   /* forward the mail */
  701.      { if(!i)
  702.           skiprc|=1;
  703.        if(strlcpy(buf,sendmail,linebuf)>=linebuf)
  704.           goto fail;
  705.        chp=strchr(buf,'\0');
  706.        if(*flagsendmail)
  707.         { char*q;int got=0;
  708.           if(!(q=simplesplit(chp+1,flagsendmail,buf+linebuf-1,&got)))
  709.          goto fail;
  710.           *(chp=q)='\0';
  711.         }
  712.        if(readparse(chp+1,getb,0,skiprc))
  713.           goto fail;
  714.        if(i)
  715.         { if(startchar==themail.p)
  716.            { startchar[filled]='\0';             /* just in case */
  717.          startchar=(char*)skipFrom_(startchar,&tobesent);
  718.            }      /* leave off leading From_ -- it confuses some mailers */
  719.           goto forward;
  720.         }
  721.        skiprc&=~1;
  722.      }
  723.     else if(testB('|'))                 /* pipe the mail */
  724.      { chp=buf2;
  725.        if(getlline(chp,buf2+linebuf))    /* get the command to start */
  726.           goto commint;
  727.        if(i)
  728.         { metaparse(buf2);
  729.           if(!sh&&buf+1==Tmnate)              /* just a pipe symbol? */
  730.            { *buf='|';*(char*)(Tmnate++)='\0';        /* fake it */
  731.          goto tostdout;
  732.            }
  733. forward:      if(locknext)
  734.            { if(!tolock)       /* an explicit lockfile specified already */
  735.           { *buf2='\0';     /* find the implicit lockfile ('>>name') */
  736.             for(chp=buf;i= *chp++;)
  737.                if(i=='>'&&*chp=='>')
  738.             { chp=skpspace(chp+1);
  739.               tmemmove(buf2,chp,i=strcspn(chp,EOFName));
  740.               buf2[i]='\0';
  741.               if(sh)     /* expand any environment variables */
  742.                { chp=tstrdup(buf);sgetcp=buf2;
  743.                  if(readparse(buf,sgetc,0,0))
  744.                   { *buf2='\0';
  745.                 goto nolock;
  746.                   }
  747.                  strcpy(buf2,buf);strcpy(buf,chp);free(chp);
  748.                }
  749.               break;
  750.             }
  751.             if(!*buf2)
  752. nolock:          { nlog("Couldn't determine implicit lockfile from");
  753.                logqnl(buf);
  754.              }
  755.           }
  756.          lcllock(tolock,buf2);
  757.          if(!pwait)     /* try and protect the user from his */
  758.             pwait=2;               /* blissful ignorance :-) */
  759.            }
  760.           rawnonl=flags[RAW_NONL];
  761.           if(flags[CONTINUE]&&(flags[FILTER]||Stdout))
  762.          nlog(extrns),elog("copy-flag"),elog(ignrd);
  763.           inittmout(buf);
  764.           if(flags[FILTER])
  765.            { if(startchar==themail.p&&tobesent!=filled)   /* if only 'h' */
  766.           { if(!pipthrough(buf,startchar,tobesent))
  767.                readmail(1,tobesent),succeed=!pipw;
  768.           }
  769.          else if(!pipthrough(buf,startchar,tobesent))
  770.           { filled=startchar-themail.p;
  771.             readmail(0,tobesent);
  772.             succeed=!pipw;
  773.           }
  774.            }
  775.           else if(Stdout)             /* capturing stdout again? */
  776.          succeed=!pipthrough(buf,startchar,tobesent);
  777.           else if(!pipin(buf,startchar,tobesent,1))   /* regular program */
  778.            { succeed=1;
  779.          if(flags[CONTINUE])
  780.             goto logsetlsucc;
  781.          else
  782.             goto frmailed;
  783.            }
  784.           goto setlsucc;
  785.         }
  786.      }
  787.     else if(testB(EOF))
  788.        nlog("Incomplete recipe\n");
  789.     else           /* dump the mail into a mailbox file or directory */
  790.      { int ofiltflag;char*end=buf+linebuf-4;    /* reserve some room */
  791.        if(ofiltflag=flags[FILTER])
  792.           flags[FILTER]=0,nlog(extrns),elog("filter-flag"),elog(ignrd);
  793.        if(chp=gobenv(buf,end))     /* can it be an environment name? */
  794.         { if(chp==end)
  795.            { getlline(buf,buf+linebuf);
  796.          goto fail;
  797.            }
  798.           if(skipspace())
  799.          chp++;            /* keep pace with argument breaks */
  800.           if(testB('='))              /* is it really an assignment? */
  801.            { int c;
  802.          *chp++='=';*chp='\0';
  803.          if(skipspace())
  804.             chp++;
  805.          ungetb(c=getb());
  806.          switch(c)
  807.           { case '!':case '|':            /* ok, it's a pipe */
  808.                if(i)
  809.               Stdout = tstrdup(buf);
  810.                goto progrm;
  811.           }
  812.            }
  813.         }            /* find the end, start of a nesting recipe? */
  814.        else if((chp=strchr(buf,'\0'))==buf&&
  815.            testB('{')&&
  816.            (*chp++='{',*chp='\0',testB(' ')||             /* } } */
  817.             testB('\t')||
  818.             testB('\n')))
  819.         { if(locknext&&!flags[CONTINUE])
  820.          nlog(extrns),elog("locallockfile"),elog(ignrd);
  821.           if(flags[PASS_BODY])
  822.          nlog(extrns),elog("deliver-body flag"),elog(ignrd);
  823.           if(flags[PASS_HEAD])
  824.          nlog(extrns),elog("deliver-head flag"),elog(ignrd);
  825.           if(flags[IGNORE_WRITERR])
  826.          nlog(extrns),elog("ignore-write-error flag"),elog(ignrd);
  827.           if(flags[RAW_NONL])
  828.          nlog(extrns),elog("raw-mode flag"),elog(ignrd);
  829.           if(!i)                        /* no match? */
  830.          skiprc+=2;           /* increase the skipping level */
  831.           else
  832.            { app_vali(ifstack,prevcond);            /* push prevcond */
  833.          app_vali(ifstack,lastcond);            /* push lastcond */
  834.          if(locknext)
  835.           { lcllock(tolock,"");
  836.             if(!pwait)      /* try and protect the user from his */
  837.                pwait=2;            /* blissful ignorance :-) */
  838.           }
  839.          succeed=1;
  840.          if(flags[CONTINUE])
  841.           { yell("Forking",procmailn);
  842.             private(0);               /* can't share anymore */
  843.             inittmout(procmailn);onguard();
  844.             if(!(pidchild=sfork()))        /* clone yourself */
  845.              { if(loclock)        /* lockfiles are not inherited */
  846.               free(loclock),loclock=0;
  847.                if(globlock)
  848.               free(globlock),globlock=0;         /* clear up the */
  849.                newid();offguard();duprcs();   /* identity crisis */
  850.              }
  851.             else
  852.              { offguard();
  853.                if(forkerr(pidchild,procmailn))
  854.               succeed=0;           /* tsk, tsk, no cloning today */
  855.                else
  856.             { int excode;     /* wait for our significant other? */
  857.               if(pwait&&
  858.                  (excode=waitfor(pidchild))!=EXIT_SUCCESS)
  859.                { if(!(pwait&2)||verbose)     /* do we report it? */
  860.                 progerr(procmailn,excode,pwait&2);
  861.                  succeed=0;
  862.                }
  863.               pidchild=0;skiprc+=2;      /* skip over the braces */
  864.               ifstack.filled-=2;        /* retract the stack */
  865.             }
  866.              }
  867.           }
  868.          goto setlsucc;         /* definitely no logabstract */
  869.            }
  870.           continue;
  871.         }
  872.        if(!i)                       /* no match? */
  873.           skiprc|=1;          /* temporarily disable subprograms */
  874.        if(readparse(chp,getb,0,skiprc))
  875. fail:       { succeed=0;
  876.           goto setlsucc;
  877.         }
  878.        if(i)
  879.         { if(ofiltflag)        /* protect who use bogus filter-flags */
  880.          startchar=themail.p,tobesent=filled;       /* whole message */
  881. tostdout:     rawnonl=flags[RAW_NONL];
  882.           if(locknext)           /* write to a file or directory */
  883.          lcllock(tolock,buf);
  884.           inittmout(buf);         /* to break messed-up kernel locks */
  885.           if(writefolder(buf,strchr(buf,'\0')+1,startchar,tobesent,
  886.           ignwerr,0)&&
  887.          (succeed=1,!flags[CONTINUE]))
  888. frmailed:      { if(ifstack.vals)
  889.             free(ifstack.vals);
  890.          return rcs_DELIVERED;
  891.            }
  892. logsetlsucc:  if(succeed&&flags[CONTINUE]&&lgabstract==2)
  893.          logabstract(tgetenv(lastfolder));
  894. setlsucc:     rawnonl=0;lastsucc=succeed;lasttell= -1;         /* for comsat */
  895.           resettmout();           /* clear any pending timer */
  896.         }
  897.        skiprc&=~1;                   /* reenable subprograms */
  898.      }
  899.       } /* { */
  900.      else if(testB('}'))                    /* end block */
  901.       { if(skiprc>1)                        /* just skipping */
  902.        skiprc-=2;                      /* decrease level */
  903.     else if(ifstack.filled>ifdepth)       /* restore lastcond from stack */
  904.      { lastcond=acc_vali(ifstack,--ifstack.filled);
  905.        prevcond=acc_vali(ifstack,--ifstack.filled);      /* prevcond */
  906.      }                            /* as well */
  907.     else
  908.        nlog("Closing brace unexpected\n");            /* stack empty */
  909.       }
  910.      else                   /* then it must be an assignment */
  911.       { char*end=buf+linebuf;
  912.     if(!(chp=gobenv(buf,end)))
  913.      { if(!*buf)                    /* skip a word first */
  914.           getbl(buf,end);                     /* then a line */
  915.        skipped(buf);                /* display leftovers */
  916.        continue;
  917.      }
  918.     if(chp==end)                      /* overflow => give up */
  919.        break;
  920.     skipspace();
  921.     if(testB('='))                 /* removal or assignment? */
  922.      { *chp='=';
  923.        if(readparse(++chp,getb,1,skiprc))
  924.           continue;
  925.      }
  926.     else
  927.        *++chp='\0';              /* throw in a second terminator */
  928.     if(!skiprc)
  929.      { const char*p;
  930.        p=sputenv(buf);
  931.        chp[-1]='\0';
  932.        asenv(p);
  933.      }
  934.       }
  935.      if(rc<0)                  /* abnormal exit from the rcfile? */
  936.     return rcs_HOST;
  937.    }
  938.   while(!testB(EOF)||(skiprc=0,poprc()));
  939.   return rcs_EOF;
  940. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement