ANSI
[unix-history] / usr / src / usr.sbin / sendmail / src / recipient.c
CommitLineData
b2a81223 1/*
dc45ba8c 2 * Copyright (c) 1983 Eric P. Allman
bee79b64
KB
3 * Copyright (c) 1988 Regents of the University of California.
4 * All rights reserved.
5 *
417f7a11 6 * %sccs.include.redist.c%
bee79b64 7 */
b2a81223
DF
8
9#ifndef lint
0df908a9 10static char sccsid[] = "@(#)recipient.c 5.19 (Berkeley) %G%";
bee79b64 11#endif /* not lint */
b2a81223 12
611050b6
KB
13# include <sys/types.h>
14# include <sys/stat.h>
6eefe4f6 15# include <pwd.h>
6eefe4f6 16# include "sendmail.h"
6eefe4f6 17
6eefe4f6 18/*
811a6cf3 19** SENDTOLIST -- Designate a send list.
6eefe4f6
EA
20**
21** The parameter is a comma-separated list of people to send to.
22** This routine arranges to send to all of them.
23**
abae7b2d
EA
24** The `ctladdr' is the address that expanded to be this one,
25** e.g., in an alias expansion. This is used for a number of
26** purposed, most notably inheritance of uid/gid for protection
27** purposes. It is also used to detect self-reference in group
28** expansions and the like.
29**
6eefe4f6
EA
30** Parameters:
31** list -- the send list.
1bf7c76b
EA
32** ctladdr -- the address template for the person to
33** send to -- effective uid/gid are important.
d4f42161
EA
34** This is typically the alias that caused this
35** expansion.
36** sendq -- a pointer to the head of a queue to put
37** these people into.
abae7b2d 38** qflags -- special flags to set in the q_flags field.
6eefe4f6
EA
39**
40** Returns:
abae7b2d 41** pointer to chain of addresses.
6eefe4f6
EA
42**
43** Side Effects:
44** none.
45*/
46
47# define MAXRCRSN 10
48
abae7b2d
EA
49ADDRESS *
50sendto(list, copyf, ctladdr, qflags)
6eefe4f6 51 char *list;
1bf7c76b 52 ADDRESS *ctladdr;
4e5e456f 53 ADDRESS **sendq;
abae7b2d 54 u_short qflags;
6eefe4f6
EA
55{
56 register char *p;
7b955214 57 register ADDRESS *al; /* list of addresses to send to */
92f12b98 58 bool firstone; /* set on first address sent */
331b7c9f 59 bool selfref; /* set if this list includes ctladdr */
d3f52e20 60 char delimiter; /* the address delimiter */
abae7b2d
EA
61 ADDRESS *sibl; /* sibling pointer in tree */
62 ADDRESS *prev; /* previous sibling */
d6b27179 63
9678c96d 64 if (tTd(25, 1))
331b7c9f
EA
65 {
66 printf("sendto: %s\n ctladdr=", list);
67 printaddr(ctladdr, FALSE);
68 }
6eefe4f6 69
7b955214 70 /* heuristic to determine old versus new style addresses */
a2983993
EA
71 if (ctladdr == NULL &&
72 (index(list, ',') != NULL || index(list, ';') != NULL ||
73 index(list, '<') != NULL || index(list, '(') != NULL))
e6f08ab1 74 CurEnv->e_flags &= ~EF_OLDSTYLE;
d3f52e20
EA
75 delimiter = ' ';
76 if (!bitset(EF_OLDSTYLE, CurEnv->e_flags) || ctladdr != NULL)
77 delimiter = ',';
7b955214 78
92f12b98 79 firstone = TRUE;
331b7c9f 80 selfref = FALSE;
d6b27179 81 al = NULL;
7b955214 82
506fc377 83 for (p = list; *p != '\0'; )
6eefe4f6 84 {
506fc377
EA
85 register ADDRESS *a;
86 extern char *DelimChar; /* defined in prescan */
6eefe4f6
EA
87
88 /* parse the address */
506fc377
EA
89 while (isspace(*p) || *p == ',')
90 p++;
d3f52e20 91 a = parseaddr(p, (ADDRESS *) NULL, 1, delimiter);
506fc377 92 p = DelimChar;
b9fadd5b
EA
93 if (a == NULL)
94 continue;
d6b27179 95 a->q_next = al;
1bf7c76b 96 a->q_alias = ctladdr;
abae7b2d
EA
97 if (ctladdr != NULL)
98 a->q_flags |= ctladdr->q_flags & ~QPRIMARY;
99 a->q_flags |= qflags;
331b7c9f
EA
100
101 /* see if this should be marked as a primary address */
92f12b98 102 if (ctladdr == NULL ||
506fc377 103 (firstone && *p == '\0' && bitset(QPRIMARY, ctladdr->q_flags)))
92f12b98 104 a->q_flags |= QPRIMARY;
331b7c9f
EA
105
106 /* put on send queue or suppress self-reference */
7338e3d4 107 if (ctladdr != NULL && sameaddr(ctladdr, a))
331b7c9f
EA
108 selfref = TRUE;
109 else
110 al = a;
92f12b98 111 firstone = FALSE;
d6b27179
EA
112 }
113
331b7c9f
EA
114 /* if this alias doesn't include itself, delete ctladdr */
115 if (!selfref && ctladdr != NULL)
116 ctladdr->q_flags |= QDONTSEND;
117
d6b27179 118 /* arrange to send to everyone on the local send list */
abae7b2d
EA
119 prev = sibl = NULL;
120 if (ctladdr != NULL)
121 prev = ctladdr->q_child;
d6b27179
EA
122 while (al != NULL)
123 {
124 register ADDRESS *a = al;
abae7b2d 125 extern ADDRESS *recipient();
d344c0b7 126 extern ADDRESS *recipient();
d6b27179
EA
127
128 al = a->q_next;
3fbc69d6 129 setctladdr(a);
abae7b2d
EA
130 sibl = recipient(a);
131 if (sibl != NULL)
132 {
133 extern ADDRESS *addrref();
134
135 /* inherit full name */
136 if (sibl->q_fullname == NULL && ctladdr != NULL)
137 sibl->q_fullname = ctladdr->q_fullname;
138
139 /* link tree together (but only if the node is new) */
140 if (sibl == a)
141 {
142 sibl->q_sibling = prev;
143 prev = sibl;
144 }
145 }
6eefe4f6 146 }
d6b27179 147
2654b031 148 CurEnv->e_to = NULL;
abae7b2d
EA
149 if (ctladdr != NULL)
150 ctladdr->q_child = prev;
151 return (prev);
152}
153\f/*
154** ADDRREF -- return pointer to address that references another address.
155**
156** Parameters:
157** a -- address to check.
158** r -- reference to find.
159**
160** Returns:
161** address of node in tree rooted at 'a' that references
162** 'r'.
163** NULL if no such node exists.
164**
165** Side Effects:
166** none.
167*/
168
169ADDRESS *
170addrref(a, r)
171 register ADDRESS *a;
172 register ADDRESS *r;
173{
174 register ADDRESS *q;
175
176 while (a != NULL)
177 {
178 if (a->q_child == r || a->q_sibling == r)
179 return (a);
180 q = addrref(a->q_child, r);
181 if (q != NULL)
182 return (q);
183 a = a->q_sibling;
184 }
185 return (NULL);
6eefe4f6
EA
186}
187\f/*
188** RECIPIENT -- Designate a message recipient
189**
190** Saves the named person for future mailing.
191**
192** Parameters:
193** a -- the (preparsed) address header for the recipient.
d4f42161
EA
194** sendq -- a pointer to the head of a queue to put the
195** recipient in. Duplicate supression is done
196** in this queue.
6eefe4f6
EA
197**
198** Returns:
abae7b2d 199** pointer to address actually inserted in send list.
6eefe4f6
EA
200**
201** Side Effects:
202** none.
203*/
204
0df908a9
KB
205extern ADDRESS *getctladdr();
206
d344c0b7 207ADDRESS *
abae7b2d 208ADDRESS *
d4f42161 209recipient(a, sendq)
6eefe4f6 210 register ADDRESS *a;
d4f42161 211 register ADDRESS **sendq;
6eefe4f6
EA
212{
213 register ADDRESS *q;
74c5fe7c 214 ADDRESS **pq;
6eefe4f6 215 register struct mailer *m;
98f46225
EA
216 register char *p;
217 bool quoted = FALSE; /* set if the addr has a quote bit */
218 char buf[MAXNAME]; /* unquoted image of the user name */
ed45aae1 219 extern bool safefile();
6eefe4f6 220
2654b031 221 CurEnv->e_to = a->q_paddr;
179c1218 222 m = a->q_mailer;
6eefe4f6 223 errno = 0;
9678c96d 224 if (tTd(26, 1))
331b7c9f
EA
225 {
226 printf("\nrecipient: ");
227 printaddr(a, FALSE);
228 }
6eefe4f6
EA
229
230 /* break aliasing loops */
231 if (AliasLevel > MAXRCRSN)
232 {
233 usrerr("aliasing/forwarding loop broken");
abae7b2d 234 return (NULL);
6eefe4f6
EA
235 }
236
237 /*
ed45aae1 238 ** Finish setting up address structure.
6eefe4f6
EA
239 */
240
0fe3917f 241 /* set the queue timeout */
ed45aae1
EA
242 a->q_timeout = TimeOut;
243
0fe3917f
EA
244 /* map user & host to lower case if requested on non-aliases */
245 if (a->q_alias == NULL)
246 loweraddr(a);
247
248 /* get unquoted user for file, program or user.name check */
98f46225
EA
249 (void) strcpy(buf, a->q_user);
250 for (p = buf; *p != '\0' && !quoted; p++)
251 {
252 if (!isascii(*p) && (*p & 0377) != (SpaceSub & 0377))
253 quoted = TRUE;
254 }
255 stripquotes(buf, TRUE);
256
ed45aae1 257 /* do sickly crude mapping for program mailing, etc. */
98f46225 258 if (m == LocalMailer && buf[0] == '|')
6eefe4f6 259 {
98f46225
EA
260 a->q_mailer = m = ProgMailer;
261 a->q_user++;
2b95e89d 262 if (a->q_alias == NULL && !QueueRun && !ForceMail)
6eefe4f6 263 {
315314bd 264 a->q_flags |= QDONTSEND|QBADADDR;
4c34b183 265 usrerr("Cannot mail directly to programs");
6eefe4f6
EA
266 }
267 }
268
269 /*
b9ca6d11
EA
270 ** Look up this person in the recipient list.
271 ** If they are there already, return, otherwise continue.
272 ** If the list is empty, just add it. Notice the cute
273 ** hack to make from addresses suppress things correctly:
274 ** the QDONTSEND bit will be set in the send list.
275 ** [Please note: the emphasis is on "hack."]
6eefe4f6
EA
276 */
277
d4f42161 278 for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next)
6eefe4f6 279 {
7338e3d4 280 if (!ForceMail && sameaddr(q, a))
6eefe4f6 281 {
9678c96d 282 if (tTd(26, 1))
331b7c9f
EA
283 {
284 printf("%s in sendq: ", a->q_paddr);
285 printaddr(q, FALSE);
286 }
abae7b2d 287 if (Verbose && !bitset(QDONTSEND|QPSEUDO, a->q_flags))
d6b27179 288 message(Arpa_Info, "duplicate suppressed");
92f12b98
EA
289 if (!bitset(QPRIMARY, q->q_flags))
290 q->q_flags |= a->q_flags;
abae7b2d
EA
291 if (!bitset(QPSEUDO, a->q_flags))
292 q->q_flags &= ~QPSEUDO;
293 return (q);
6eefe4f6 294 }
6eefe4f6 295 }
74c5fe7c
EA
296
297 /* add address on list */
298 *pq = a;
6eefe4f6 299 a->q_next = NULL;
06ddddfc 300 CurEnv->e_nrcpts++;
6eefe4f6
EA
301
302 /*
303 ** Alias the name and handle :include: specs.
304 */
305
98f46225 306 if (m == LocalMailer && !bitset(QDONTSEND, a->q_flags))
6eefe4f6
EA
307 {
308 if (strncmp(a->q_user, ":include:", 9) == 0)
309 {
310 a->q_flags |= QDONTSEND;
2b95e89d 311 if (a->q_alias == NULL && !QueueRun && !ForceMail)
315314bd
EA
312 {
313 a->q_flags |= QBADADDR;
1bf7c76b 314 usrerr("Cannot mail directly to :include:s");
315314bd 315 }
1bf7c76b
EA
316 else
317 {
91f69adf 318 message(Arpa_Info, "including file %s", &a->q_user[9]);
d4f42161 319 include(&a->q_user[9], " sending", a, sendq);
1bf7c76b 320 }
6eefe4f6
EA
321 }
322 else
d4f42161 323 alias(a, sendq);
6eefe4f6
EA
324 }
325
326 /*
327 ** If the user is local and still being sent, verify that
328 ** the address is good. If it is, try to forward.
329 ** If the address is already good, we have a forwarding
330 ** loop. This can be broken by just sending directly to
331 ** the user (which is probably correct anyway).
332 */
333
98f46225 334 if (!bitset(QDONTSEND, a->q_flags) && m == LocalMailer)
6eefe4f6 335 {
a0554f81
EA
336 struct stat stb;
337 extern bool writable();
6eefe4f6
EA
338
339 /* see if this is to a file */
d4d03212 340 if (buf[0] == '/')
6eefe4f6 341 {
d4d03212 342 p = rindex(buf, '/');
9d3b45b8 343 /* check if writable or creatable */
2b95e89d 344 if (a->q_alias == NULL && !QueueRun && !ForceMail)
1bf7c76b 345 {
315314bd 346 a->q_flags |= QDONTSEND|QBADADDR;
4c34b183 347 usrerr("Cannot mail directly to files");
1bf7c76b
EA
348 }
349 else if ((stat(buf, &stb) >= 0) ? (!writable(&stb)) :
f6a0cc15 350 (*p = '\0', !safefile(buf, getruid(), S_IWRITE|S_IEXEC)))
6eefe4f6
EA
351 {
352 a->q_flags |= QBADADDR;
baa0c390 353 giveresponse(EX_CANTCREAT, m, CurEnv);
6eefe4f6
EA
354 }
355 }
356 else
357 {
358 register struct passwd *pw;
ff3e3c1c
EA
359 extern struct passwd *finduser();
360
29c33ff6 361 /* warning -- finduser may trash buf */
ff3e3c1c 362 pw = finduser(buf);
6eefe4f6
EA
363 if (pw == NULL)
364 {
365 a->q_flags |= QBADADDR;
baa0c390 366 giveresponse(EX_NOUSER, m, CurEnv);
6eefe4f6
EA
367 }
368 else
369 {
abae7b2d
EA
370 char nbuf[MAXNAME];
371
f77d50ee
EA
372 char nbuf[MAXNAME];
373
2b5e3c25
EA
374 if (strcmp(a->q_user, pw->pw_name) != 0)
375 {
376 a->q_user = newstr(pw->pw_name);
f9566d23 377 (void) strcpy(buf, pw->pw_name);
2b5e3c25 378 }
6eefe4f6 379 a->q_home = newstr(pw->pw_dir);
9c3f729b 380 a->q_uid = pw->pw_uid;
1bf7c76b 381 a->q_gid = pw->pw_gid;
2c1457f0 382 a->q_flags |= QGOODUID;
f77d50ee
EA
383 buildfname(pw->pw_gecos, pw->pw_name, nbuf);
384 if (nbuf[0] != '\0')
385 a->q_fullname = newstr(nbuf);
abae7b2d
EA
386 fullname(pw, nbuf);
387 if (nbuf[0] != '\0')
388 a->q_fullname = newstr(nbuf);
1bf7c76b 389 if (!quoted)
d4f42161 390 forward(a, sendq);
6eefe4f6
EA
391 }
392 }
393 }
d344c0b7 394 return (a);
abae7b2d
EA
395
396 return (a);
6eefe4f6
EA
397}
398\f/*
ff3e3c1c
EA
399** FINDUSER -- find the password entry for a user.
400**
401** This looks a lot like getpwnam, except that it may want to
402** do some fancier pattern matching in /etc/passwd.
403**
7338e3d4
EA
404** This routine contains most of the time of many sendmail runs.
405** It deserves to be optimized.
406**
ff3e3c1c
EA
407** Parameters:
408** name -- the name to match against.
409**
410** Returns:
411** A pointer to a pw struct.
412** NULL if name is unknown or ambiguous.
413**
414** Side Effects:
29c33ff6 415** may modify name.
ff3e3c1c
EA
416*/
417
418struct passwd *
419finduser(name)
420 char *name;
421{
2b5e3c25 422 register struct passwd *pw;
29c33ff6 423 register char *p;
f74d8dce
EA
424 extern struct passwd *getpwent();
425 extern struct passwd *getpwnam();
29c33ff6 426
639d8b98 427 /* map upper => lower case */
29c33ff6
EA
428 for (p = name; *p != '\0'; p++)
429 {
639d8b98 430 if (isascii(*p) && isupper(*p))
577bce94 431 *p = tolower(*p);
29c33ff6 432 }
ff3e3c1c 433
639d8b98 434 /* look up this login name using fast path */
77b16ff1
EA
435 if ((pw = getpwnam(name)) != NULL)
436 return (pw);
437
438 /* search for a matching full name instead */
639d8b98
EA
439 for (p = name; *p != '\0'; p++)
440 {
441 if (*p == (SpaceSub & 0177) || *p == '_')
442 *p = ' ';
443 }
03388044 444 (void) setpwent();
2b5e3c25
EA
445 while ((pw = getpwent()) != NULL)
446 {
abae7b2d 447 char buf[MAXNAME];
2b5e3c25 448
abae7b2d 449 fullname(pw, buf);
ed73ef1d 450 if (index(buf, ' ') != NULL && !strcasecmp(buf, name))
36292825 451 {
abae7b2d
EA
452 message(Arpa_Info, "sending to %s <%s>",
453 buf, pw->pw_name);
2b5e3c25 454 return (pw);
36292825 455 }
2b5e3c25
EA
456 }
457 return (NULL);
ff3e3c1c
EA
458}
459\f/*
a0554f81
EA
460** WRITABLE -- predicate returning if the file is writable.
461**
462** This routine must duplicate the algorithm in sys/fio.c.
463** Unfortunately, we cannot use the access call since we
464** won't necessarily be the real uid when we try to
465** actually open the file.
466**
467** Notice that ANY file with ANY execute bit is automatically
468** not writable. This is also enforced by mailfile.
469**
470** Parameters:
471** s -- pointer to a stat struct for the file.
472**
473** Returns:
474** TRUE -- if we will be able to write this file.
475** FALSE -- if we cannot write this file.
476**
477** Side Effects:
478** none.
479*/
480
481bool
482writable(s)
483 register struct stat *s;
484{
485 int euid, egid;
486 int bits;
487
488 if (bitset(0111, s->st_mode))
489 return (FALSE);
490 euid = getruid();
491 egid = getrgid();
492 if (geteuid() == 0)
493 {
494 if (bitset(S_ISUID, s->st_mode))
495 euid = s->st_uid;
496 if (bitset(S_ISGID, s->st_mode))
497 egid = s->st_gid;
498 }
499
500 if (euid == 0)
501 return (TRUE);
502 bits = S_IWRITE;
503 if (euid != s->st_uid)
504 {
505 bits >>= 3;
506 if (egid != s->st_gid)
507 bits >>= 3;
508 }
509 return ((s->st_mode & bits) != 0);
510}
511\f/*
6eefe4f6
EA
512** INCLUDE -- handle :include: specification.
513**
514** Parameters:
515** fname -- filename to include.
23008226 516** msg -- message to print in verbose mode.
1bf7c76b
EA
517** ctladdr -- address template to use to fill in these
518** addresses -- effective user/group id are
519** the important things.
d4f42161
EA
520** sendq -- a pointer to the head of the send queue
521** to put these addresses in.
6eefe4f6
EA
522**
523** Returns:
524** none.
525**
526** Side Effects:
527** reads the :include: file and sends to everyone
528** listed in that file.
529*/
530
d4f42161 531include(fname, msg, ctladdr, sendq)
6eefe4f6 532 char *fname;
23008226 533 char *msg;
1bf7c76b 534 ADDRESS *ctladdr;
d4f42161 535 ADDRESS **sendq;
6eefe4f6
EA
536{
537 char buf[MAXLINE];
538 register FILE *fp;
2654b031 539 char *oldto = CurEnv->e_to;
7338e3d4
EA
540 char *oldfilename = FileName;
541 int oldlinenumber = LineNumber;
6eefe4f6 542
6eefe4f6
EA
543 fp = fopen(fname, "r");
544 if (fp == NULL)
545 {
546 usrerr("Cannot open %s", fname);
547 return;
548 }
962c3fb2
EA
549 if (getctladdr(ctladdr) == NULL)
550 {
551 struct stat st;
552
553 if (fstat(fileno(fp), &st) < 0)
554 syserr("Cannot fstat %s!", fname);
555 ctladdr->q_uid = st.st_uid;
556 ctladdr->q_gid = st.st_gid;
557 ctladdr->q_flags |= QGOODUID;
558 }
6eefe4f6
EA
559
560 /* read the file -- each line is a comma-separated list. */
7338e3d4
EA
561 FileName = fname;
562 LineNumber = 0;
6eefe4f6
EA
563 while (fgets(buf, sizeof buf, fp) != NULL)
564 {
565 register char *p = index(buf, '\n');
566
dd0758e5 567 LineNumber++;
6eefe4f6
EA
568 if (p != NULL)
569 *p = '\0';
570 if (buf[0] == '\0')
571 continue;
2654b031 572 CurEnv->e_to = oldto;
91f69adf 573 message(Arpa_Info, "%s to %s", msg, buf);
23008226 574 AliasLevel++;
abae7b2d 575 sendto(buf, 1, ctladdr, 0);
23008226 576 AliasLevel--;
6eefe4f6
EA
577 }
578
74c5fe7c 579 (void) fclose(fp);
7338e3d4
EA
580 FileName = oldfilename;
581 LineNumber = oldlinenumber;
6eefe4f6 582}
d6b27179
EA
583\f/*
584** SENDTOARGV -- send to an argument vector.
585**
586** Parameters:
587** argv -- argument vector to send to.
588**
589** Returns:
590** none.
591**
592** Side Effects:
593** puts all addresses on the argument vector onto the
594** send queue.
595*/
596
597sendtoargv(argv)
598 register char **argv;
599{
600 register char *p;
d6b27179
EA
601
602 while ((p = *argv++) != NULL)
603 {
ed73ef1d 604 if (argv[0] != NULL && argv[1] != NULL && !strcasecmp(argv[0], "at"))
d6b27179
EA
605 {
606 char nbuf[MAXNAME];
607
608 if (strlen(p) + strlen(argv[1]) + 2 > sizeof nbuf)
609 usrerr("address overflow");
610 else
611 {
612 (void) strcpy(nbuf, p);
613 (void) strcat(nbuf, "@");
614 (void) strcat(nbuf, argv[1]);
615 p = newstr(nbuf);
616 argv += 2;
617 }
618 }
abae7b2d 619 sendto(p, 0, (ADDRESS *) NULL, 0);
d6b27179
EA
620 }
621}
1bf7c76b
EA
622\f/*
623** GETCTLADDR -- get controlling address from an address header.
624**
625** If none, get one corresponding to the effective userid.
626**
627** Parameters:
628** a -- the address to find the controller of.
629**
630** Returns:
631** the controlling address.
632**
633** Side Effects:
634** none.
635*/
636
637ADDRESS *
638getctladdr(a)
639 register ADDRESS *a;
640{
2c1457f0 641 while (a != NULL && !bitset(QGOODUID, a->q_flags))
1bf7c76b 642 a = a->q_alias;
1bf7c76b
EA
643 return (a);
644}