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

Defined functions

finduser defined in line 345; used 2 times
getctladdr defined in line 564; used 5 times
include defined in line 458; used 2 times
sendtoargv defined in line 523; used 1 times
writable defined in line 408; used 2 times

Defined variables

SccsId defined in line 12; never used

Defined macros

MAXRCRSN defined in line 41; used 1 times
Last modified: 1986-01-09
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 1858
Valid CSS Valid XHTML 1.0 Strict