1 # include <pwd.h>
2 # include <sys/types.h>
3 # include <sys/stat.h>
4 # include "sendmail.h"
5 
6 static char SccsId[] = "@(#)recipient.c	3.27	10/23/81";
7 
8 /*
9 **  SENDTO -- Designate a send list.
10 **
11 **	The parameter is a comma-separated list of people to send to.
12 **	This routine arranges to send to all of them.
13 **
14 **	Parameters:
15 **		list -- the send list.
16 **		copyf -- the copy flag; passed to parse.
17 **		ctladdr -- the address template for the person to
18 **			send to -- effective uid/gid are important.
19 **
20 **	Returns:
21 **		none
22 **
23 **	Side Effects:
24 **		none.
25 */
26 
27 # define MAXRCRSN	10
28 
29 sendto(list, copyf, ctladdr)
30 	char *list;
31 	int copyf;
32 	ADDRESS *ctladdr;
33 {
34 	register char *p;
35 	bool more;		/* set if more addresses to send to */
36 	ADDRESS *al;		/* list of addresses to send to */
37 	bool firstone;		/* set on first address sent */
38 	bool selfref;		/* set if this list includes ctladdr */
39 
40 # ifdef DEBUG
41 	if (Debug > 1)
42 	{
43 		printf("sendto: %s\n   ctladdr=", list);
44 		printaddr(ctladdr, FALSE);
45 	}
46 # endif DEBUG
47 
48 	more = TRUE;
49 	firstone = TRUE;
50 	selfref = FALSE;
51 	al = NULL;
52 	for (p = list; more; )
53 	{
54 		register char *q;
55 		register char c;
56 		ADDRESS *a;
57 
58 		/* find the end of this address */
59 		while (*p == ' ' || *p == '\t')
60 			p++;
61 		q = p;
62 		while ((c = *p++) != '\0' && c != ',' && c != '\n')
63 			continue;
64 		more = c != '\0';
65 		*--p = '\0';
66 		if (more)
67 			p++;
68 		if (*q == '\0')
69 			continue;
70 
71 		/* parse the address */
72 		if ((a = parse(q, (ADDRESS *) NULL, copyf)) == NULL)
73 			continue;
74 		a->q_next = al;
75 		a->q_alias = ctladdr;
76 
77 		/* see if this should be marked as a primary address */
78 		if (ctladdr == NULL ||
79 		    (firstone && !more && bitset(QPRIMARY, ctladdr->q_flags)))
80 			a->q_flags |= QPRIMARY;
81 
82 		/* put on send queue or suppress self-reference */
83 		if (ctladdr != NULL && sameaddr(ctladdr, a, FALSE))
84 			selfref = TRUE;
85 		else
86 			al = a;
87 		firstone = FALSE;
88 	}
89 
90 	/* if this alias doesn't include itself, delete ctladdr */
91 	if (!selfref && ctladdr != NULL)
92 		ctladdr->q_flags |= QDONTSEND;
93 
94 	/* arrange to send to everyone on the local send list */
95 	while (al != NULL)
96 	{
97 		register ADDRESS *a = al;
98 
99 		al = a->q_next;
100 		recipient(a);
101 	}
102 
103 	To = NULL;
104 }
105 /*
106 **  RECIPIENT -- Designate a message recipient
107 **
108 **	Saves the named person for future mailing.
109 **
110 **	Parameters:
111 **		a -- the (preparsed) address header for the recipient.
112 **
113 **	Returns:
114 **		none.
115 **
116 **	Side Effects:
117 **		none.
118 */
119 
120 recipient(a)
121 	register ADDRESS *a;
122 {
123 	register ADDRESS *q;
124 	ADDRESS **pq;
125 	register struct mailer *m;
126 	extern ADDRESS *getctladdr();
127 
128 	To = a->q_paddr;
129 	m = a->q_mailer;
130 	errno = 0;
131 # ifdef DEBUG
132 	if (Debug)
133 	{
134 		printf("\nrecipient: ");
135 		printaddr(a, FALSE);
136 	}
137 # endif DEBUG
138 
139 	/* break aliasing loops */
140 	if (AliasLevel > MAXRCRSN)
141 	{
142 		usrerr("aliasing/forwarding loop broken");
143 		return;
144 	}
145 
146 	/*
147 	**  Do sickly crude mapping for program mailing, etc.
148 	*/
149 
150 	if (a->q_mailer == LocalMailer)
151 	{
152 		if (a->q_user[0] == '|')
153 		{
154 			a->q_mailer = m = ProgMailer;
155 			a->q_user++;
156 			if (a->q_alias == NULL && Debug == 0)
157 			{
158 				usrerr("Cannot mail directly to programs");
159 				a->q_flags |= QDONTSEND;
160 			}
161 		}
162 	}
163 
164 	/*
165 	**  Look up this person in the recipient list.
166 	**	If they are there already, return, otherwise continue.
167 	**	If the list is empty, just add it.  Notice the cute
168 	**	hack to make from addresses suppress things correctly:
169 	**	the QDONTSEND bit will be set in the send list.
170 	**	[Please note: the emphasis is on "hack."]
171 	*/
172 
173 	for (pq = &m->m_sendq; (q = *pq) != NULL; pq = &q->q_next)
174 	{
175 		if (!ForceMail && sameaddr(q, a, FALSE))
176 		{
177 # ifdef DEBUG
178 			if (Debug)
179 			{
180 				printf("%s in sendq: ", a->q_paddr);
181 				printaddr(q, FALSE);
182 			}
183 # endif DEBUG
184 			if (Verbose && !bitset(QDONTSEND, a->q_flags))
185 				message(Arpa_Info, "duplicate suppressed");
186 			if (!bitset(QPRIMARY, q->q_flags))
187 				q->q_flags |= a->q_flags;
188 			return;
189 		}
190 	}
191 
192 	/* add address on list */
193 	*pq = a;
194 	a->q_next = NULL;
195 	if (DontSend)
196 		a->q_flags |= QDONTSEND;
197 
198 	/*
199 	**  Alias the name and handle :include: specs.
200 	*/
201 
202 	if (a->q_mailer == LocalMailer)
203 	{
204 		if (strncmp(a->q_user, ":include:", 9) == 0)
205 		{
206 			a->q_flags |= QDONTSEND;
207 			if (a->q_alias == NULL && Debug == 0)
208 				usrerr("Cannot mail directly to :include:s");
209 			else
210 			{
211 				if (Verbose)
212 					message(Arpa_Info, "including file %s", &a->q_user[9]);
213 				include(&a->q_user[9], " sending", a);
214 			}
215 		}
216 		else
217 			alias(a);
218 	}
219 
220 	/*
221 	**  If the user is local and still being sent, verify that
222 	**  the address is good.  If it is, try to forward.
223 	**  If the address is already good, we have a forwarding
224 	**  loop.  This can be broken by just sending directly to
225 	**  the user (which is probably correct anyway).
226 	*/
227 
228 	if (!bitset(QDONTSEND, a->q_flags) && a->q_mailer == LocalMailer)
229 	{
230 		char buf[MAXNAME];
231 		register char *p;
232 		struct stat stb;
233 		extern bool writable();
234 		bool quoted = FALSE;
235 
236 		strcpy(buf, a->q_user);
237 		for (p = buf; *p != '\0' && !quoted; p++)
238 		{
239 			if (!isascii(*p))
240 				quoted = TRUE;
241 		}
242 		stripquotes(buf, TRUE);
243 
244 		/* see if this is to a file */
245 		if ((p = rindex(buf, '/')) != NULL)
246 		{
247 			/* check if writable or creatable */
248 			if (a->q_alias == NULL && Debug == 0)
249 			{
250 				usrerr("Cannot mail directly to files");
251 				a->q_flags |= QDONTSEND;
252 			}
253 			else if ((stat(buf, &stb) >= 0) ? (!writable(&stb)) :
254 			    (*p = '\0', !safefile(buf, getruid(), S_IWRITE|S_IEXEC)))
255 			{
256 				a->q_flags |= QBADADDR;
257 				giveresponse(EX_CANTCREAT, TRUE, m);
258 			}
259 		}
260 		else
261 		{
262 			register struct passwd *pw;
263 			extern struct passwd *finduser();
264 
265 			/* warning -- finduser may trash buf */
266 			pw = finduser(buf);
267 			if (pw == NULL)
268 			{
269 				a->q_flags |= QBADADDR;
270 				giveresponse(EX_NOUSER, TRUE, m);
271 			}
272 			else
273 			{
274 				if (strcmp(a->q_user, pw->pw_name) != 0)
275 				{
276 					a->q_user = newstr(pw->pw_name);
277 					strcpy(buf, pw->pw_name);
278 				}
279 				a->q_home = newstr(pw->pw_dir);
280 				a->q_uid = pw->pw_uid;
281 				a->q_gid = pw->pw_gid;
282 				a->q_flags |= QGOODUID;
283 				if (!quoted)
284 					forward(a);
285 			}
286 		}
287 	}
288 }
289 /*
290 **  FINDUSER -- find the password entry for a user.
291 **
292 **	This looks a lot like getpwnam, except that it may want to
293 **	do some fancier pattern matching in /etc/passwd.
294 **
295 **	Parameters:
296 **		name -- the name to match against.
297 **
298 **	Returns:
299 **		A pointer to a pw struct.
300 **		NULL if name is unknown or ambiguous.
301 **
302 **	Side Effects:
303 **		may modify name.
304 */
305 
306 struct passwd *
307 finduser(name)
308 	char *name;
309 {
310 	extern struct passwd *getpwent();
311 	register struct passwd *pw;
312 	register char *p;
313 
314 	/*
315 	**  Make name canonical.
316 	*/
317 
318 	for (p = name; *p != '\0'; p++)
319 	{
320 		if (*p == (SPACESUB & 0177) || *p == '_')
321 			*p = ' ';
322 	}
323 
324 	setpwent();
325 	while ((pw = getpwent()) != NULL)
326 	{
327 		char buf[MAXNAME];
328 		extern bool sameword();
329 
330 		if (strcmp(pw->pw_name, name) == 0)
331 			return (pw);
332 		buildfname(pw->pw_gecos, pw->pw_name, buf);
333 		if (index(buf, ' ') != NULL && sameword(buf, name))
334 		{
335 			if (Verbose)
336 				message(Arpa_Info, "sending to login name %s",
337 				    pw->pw_name);
338 			return (pw);
339 		}
340 	}
341 	return (NULL);
342 }
343 /*
344 **  WRITABLE -- predicate returning if the file is writable.
345 **
346 **	This routine must duplicate the algorithm in sys/fio.c.
347 **	Unfortunately, we cannot use the access call since we
348 **	won't necessarily be the real uid when we try to
349 **	actually open the file.
350 **
351 **	Notice that ANY file with ANY execute bit is automatically
352 **	not writable.  This is also enforced by mailfile.
353 **
354 **	Parameters:
355 **		s -- pointer to a stat struct for the file.
356 **
357 **	Returns:
358 **		TRUE -- if we will be able to write this file.
359 **		FALSE -- if we cannot write this file.
360 **
361 **	Side Effects:
362 **		none.
363 */
364 
365 bool
366 writable(s)
367 	register struct stat *s;
368 {
369 	int euid, egid;
370 	int bits;
371 
372 	if (bitset(0111, s->st_mode))
373 		return (FALSE);
374 	euid = getruid();
375 	egid = getrgid();
376 	if (geteuid() == 0)
377 	{
378 		if (bitset(S_ISUID, s->st_mode))
379 			euid = s->st_uid;
380 		if (bitset(S_ISGID, s->st_mode))
381 			egid = s->st_gid;
382 	}
383 
384 	if (euid == 0)
385 		return (TRUE);
386 	bits = S_IWRITE;
387 	if (euid != s->st_uid)
388 	{
389 		bits >>= 3;
390 		if (egid != s->st_gid)
391 			bits >>= 3;
392 	}
393 	return ((s->st_mode & bits) != 0);
394 }
395 /*
396 **  INCLUDE -- handle :include: specification.
397 **
398 **	Parameters:
399 **		fname -- filename to include.
400 **		msg -- message to print in verbose mode.
401 **		ctladdr -- address template to use to fill in these
402 **			addresses -- effective user/group id are
403 **			the important things.
404 **
405 **	Returns:
406 **		none.
407 **
408 **	Side Effects:
409 **		reads the :include: file and sends to everyone
410 **		listed in that file.
411 */
412 
413 include(fname, msg, ctladdr)
414 	char *fname;
415 	char *msg;
416 	ADDRESS *ctladdr;
417 {
418 	char buf[MAXLINE];
419 	register FILE *fp;
420 	char *oldto = To;
421 
422 	fp = fopen(fname, "r");
423 	if (fp == NULL)
424 	{
425 		usrerr("Cannot open %s", fname);
426 		return;
427 	}
428 	if (getctladdr(ctladdr) == NULL)
429 	{
430 		struct stat st;
431 
432 		if (fstat(fileno(fp), &st) < 0)
433 			syserr("Cannot fstat %s!", fname);
434 		ctladdr->q_uid = st.st_uid;
435 		ctladdr->q_gid = st.st_gid;
436 		ctladdr->q_flags |= QGOODUID;
437 	}
438 
439 	/* read the file -- each line is a comma-separated list. */
440 	while (fgets(buf, sizeof buf, fp) != NULL)
441 	{
442 		register char *p = index(buf, '\n');
443 
444 		if (p != NULL)
445 			*p = '\0';
446 		if (buf[0] == '\0')
447 			continue;
448 		To = oldto;
449 		if (Verbose)
450 			message(Arpa_Info, "%s to %s", msg, buf);
451 		AliasLevel++;
452 		sendto(buf, 1, ctladdr);
453 		AliasLevel--;
454 	}
455 
456 	(void) fclose(fp);
457 }
458 /*
459 **  SENDTOARGV -- send to an argument vector.
460 **
461 **	Parameters:
462 **		argv -- argument vector to send to.
463 **
464 **	Returns:
465 **		none.
466 **
467 **	Side Effects:
468 **		puts all addresses on the argument vector onto the
469 **			send queue.
470 */
471 
472 sendtoargv(argv)
473 	register char **argv;
474 {
475 	register char *p;
476 	extern bool sameword();
477 
478 	while ((p = *argv++) != NULL)
479 	{
480 		if (argv[0] != NULL && argv[1] != NULL && sameword(argv[0], "at"))
481 		{
482 			char nbuf[MAXNAME];
483 
484 			if (strlen(p) + strlen(argv[1]) + 2 > sizeof nbuf)
485 				usrerr("address overflow");
486 			else
487 			{
488 				(void) strcpy(nbuf, p);
489 				(void) strcat(nbuf, "@");
490 				(void) strcat(nbuf, argv[1]);
491 				p = newstr(nbuf);
492 				argv += 2;
493 			}
494 		}
495 		sendto(p, 0, NULL);
496 	}
497 }
498 /*
499 **  GETCTLADDR -- get controlling address from an address header.
500 **
501 **	If none, get one corresponding to the effective userid.
502 **
503 **	Parameters:
504 **		a -- the address to find the controller of.
505 **
506 **	Returns:
507 **		the controlling address.
508 **
509 **	Side Effects:
510 **		none.
511 */
512 
513 ADDRESS *
514 getctladdr(a)
515 	register ADDRESS *a;
516 {
517 	while (a != NULL && !bitset(QGOODUID, a->q_flags))
518 		a = a->q_alias;
519 	return (a);
520 }
521