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