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