1/************************************************************************
2 *	From_ line routines used by procmail				*
3 *									*
4 *	Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands	*
5 *	Copyright (c) 2000, Philip Guenther, The United States		*
6 *							of America	*
7 *	#include "../README"						*
8 ************************************************************************/
9#ifdef RCS
10static /*const*/char rcsid[]=
11 "$Id: from.c,v 1.2 2000/10/27 20:03:58 guenther Exp $";
12#endif
13#include "procmail.h"
14#include "robust.h"
15#include "shell.h"
16#include "memblk.h"
17#include "common.h"
18#include "misc.h"				/* for nlog */
19#include "from.h"
20
21static int privs;			     /* can we change the From_ line */
22static const char From_[]=FROM,Fakefield[]=FAKE_FIELD,
23 attemptst[]="Attempt to fake stamp by";
24
25int eqFrom_(a)const char*const a;
26{ return !strncmp(a,From_,STRLEN(From_));
27}
28
29const char*skipFrom_(startchar,tobesentp)const char*startchar;long*tobesentp;
30{ if(eqFrom_(startchar))
31   { long tobesent;char c;
32     tobesent= *tobesentp;
33     do
34	while(c= *startchar++,--tobesent&&c!='\n');
35     while(*startchar=='>');
36     *tobesentp=tobesent;
37   }
38  return startchar;
39}
40			  /* tries to locate the timestamp on the From_ line */
41static char*findtstamp(start,end)const char*start,*end;
42{ end-=25;
43  if(*start==' '&&(++start==end||*start==' '&&++start==end))
44     return (char*)start-1;
45  start=skpspace(start);start+=strcspn(start," \t\n");	/* jump over address */
46  if(skpspace(start)>=end)			       /* enough space left? */
47     return (char*)start;	 /* no, too small for a timestamp, stop here */
48  while(!(end[13]==':'&&end[16]==':')&&--end>start);	  /* search for :..: */
49  ;{ int spc=0;						 /* found it perhaps */
50     while(end-->start)		      /* now skip over the space to the left */
51      { switch(*end)
52	 { case ' ':case '\t':spc=1;
53	      continue;
54	 }
55	if(!spc)
56	   continue;
57	break;
58      }
59     return (char*)end+1;	   /* this should be right after the address */
60   }
61}
62
63size_t ctime2buf2 P((void))
64{ time_t t=time((time_t*)0);				 /* the current time */
65  buf2[0]=buf2[1]=' ';strcpy(buf2+2,ctime(&t));
66  return strlen(buf2);
67}
68
69void makeFrom(from,invoker)const char*from,*const invoker;
70{ static const char mdaemon[]=MAILERDAEMON;
71  const char*fwhom;char*rstart;size_t lfr,linv;int tstamp,extra,r;
72  if(Deliverymode!=2)
73   { tstamp=from&&*from==REFRESH_TIME&&!from[1];
74     fwhom=from;
75   }
76  else
77   { tstamp=0;
78     fwhom= *from?from:mdaemon;
79   }
80  if(from&&!tstamp)
81   { if(privs!=1&&!strcmp(from,invoker))
82	privs=1;	  /* if -f user is the same as the invoker, allow it */
83     else if(privs==-1&&from)
84      { if(verbose)
85	   nlog(insufprivs);			      /* ignore the bogus -f */
86	syslog(LOG_ERR,slogstr,attemptst,invoker);from=0;
87	fwhom=invoker;
88      }
89   }
90  else
91     fwhom=invoker;
92  makeblock(&themail,2*linebuf+(lfr=strlen(fwhom))+(linv=strlen(invoker)));
93  private(1);					    /* we're not yet sharing */
94  rstart=thebody=themail.p;
95  if(!Deliverymode&&!from)	       /* need to peek for a leading From_ ? */
96      return;							     /* nope */
97  r=ctime2buf2();
98  lfr+=STRLEN(From_)+r;			   /* length of requested From_ line */
99  if(tstamp)
100     tstamp=r;					   /* save time stamp length */
101  if(privs>0)						 /* privileged user? */
102     linv=0;				 /* yes, so no need to insert >From_ */
103  else
104     linv+=STRLEN(Fakefield)+r;			    /* length of >From_ line */
105  extra=0;
106  if(Deliverymode!=2)					      /* if not LMTP */
107   { while(1==(r=rread(STDIN,themail.p,1)))	  /* then read in first line */
108	if(themail.p[0]!='\n')			    /* skip leading newlines */
109	   break;
110     if(r>0&&STRLEN(From_)<=(extra=1+rread(	      /* is it a From_ line? */
111      STDIN,rstart+1,(int)(linebuf-2-1)))&&eqFrom_(themail.p))
112      { rstart[extra]='\0';
113	if(!(rstart=strchr(rstart,'\n')))
114	 { do					     /* drop long From_ line */
115	    { if((extra=rread(STDIN,themail.p,(int)(linebuf-2)))<=0)
116		 break;
117	      themail.p[extra]='\0';		  /* terminate it for strchr */
118	    }
119	   while(!(rstart=strchr(themail.p,'\n')));
120	   extra=rstart?extra-(++rstart-themail.p):0;
121	 }
122	else
123	 { size_t tfrl= ++rstart-themail.p; /* length of existing From_ line */
124	   extra-=tfrl;					     /* demarcate it */
125	   if(Deliverymode&&privs<0)
126	    { if(verbose)			  /* discard the bogus From_ */
127		 nlog(insufprivs);
128	      syslog(LOG_ERR,slogstr,attemptst,fwhom);
129	    }
130	   else
131	    { if(tstamp)
132		 lfr=findtstamp(themail.p+STRLEN(From_),rstart)
133		  -themail.p+tstamp;
134	      else if(!from)		       /* leave the From_ line alone */
135		 if(linv)				      /* fake alert? */
136		    lfr=tfrl;	     /* yes, so separate From_ from the rest */
137		 else
138		    lfr=0,extra+=tfrl;		/* no, tack it onto the rest */
139	      goto got_from;
140	    }
141	 }
142      }
143   }
144  tstamp=0;			   /* no existing From_, so nothing to stamp */
145  if(!from)							  /* no -f ? */
146     linv=0;					  /* then it can't be a fake */
147got_from:
148  filled=lfr+linv+extra;			    /* From_ + >From_ + rest */
149  if(lfr||linv)			     /* move read text beyond our From_ line */
150   { r= *rstart;tmemmove(themail.p+lfr+linv,rstart,extra);
151     rstart=themail.p+lfr;		      /* skip the From_ line, if any */
152     if(!linv)						    /* no fake alert */
153      { rstart[-tstamp]='\0';			       /* where do we append */
154	if(!tstamp)			 /* no timestamp, so generate it all */
155	   strcat(strcpy(themail.p,From_),fwhom);		/* From user */
156      }
157     else
158      { if(lfr)					/* did we skip a From_ line? */
159	   if(tstamp)			 /* copy the timestamp over the tail */
160	      strcpy(rstart-tstamp,buf2);
161	   else if(from)				 /* whole new From_? */
162	      strcat(strcat(strcpy(themail.p,From_),fwhom),buf2);
163	strcat(strcpy(rstart,Fakefield),invoker);	       /* fake alert */
164      }					  /* overwrite the trailing \0 again */
165     strcat(themail.p,buf2);themail.p[lfr+linv]=r;
166   }
167}
168
169void checkprivFrom_(euid,logname,override)uid_t euid;const char*logname;
170 int override;
171{ static const char*const trusted_ids[]=TRUSTED_IDS;
172  privs=1;					/* assume they're privileged */
173  if(Deliverymode&&*trusted_ids&&uid!=euid)
174   { struct group*grp;const char*const*kp;
175     if(logname)			      /* check out the invoker's uid */
176	for(kp=trusted_ids;*kp;kp++)
177	   if(!strcmp(logname,*kp))	    /* is it amongst the privileged? */
178	      goto privileged;
179     if(grp=getgrgid(gid))		      /* check out the invoker's gid */
180	for(logname=grp->gr_name,kp=trusted_ids;*kp;kp++)
181	   if(!strcmp(logname,*kp))	      /* is it among the privileged? */
182	      goto privileged;
183     privs= -override;		/* override only matters when not privileged */
184   }
185privileged:
186  endgrent();
187}
188