1 # include <pwd.h> 2 # include "sendmail.h" 3 # include <sys/stat.h> 4 5 SCCSID(@(#)recipient.c 3.53 05/20/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 setpwent(); 345 while ((pw = getpwent()) != NULL) 346 { 347 char buf[MAXNAME]; 348 extern bool sameword(); 349 350 if (strcmp(pw->pw_name, name) == 0) 351 return (pw); 352 buildfname(pw->pw_gecos, pw->pw_name, buf); 353 if (index(buf, ' ') != NULL && sameword(buf, name)) 354 { 355 message(Arpa_Info, "sending to login name %s", pw->pw_name); 356 return (pw); 357 } 358 } 359 return (NULL); 360 } 361 /* 362 ** WRITABLE -- predicate returning if the file is writable. 363 ** 364 ** This routine must duplicate the algorithm in sys/fio.c. 365 ** Unfortunately, we cannot use the access call since we 366 ** won't necessarily be the real uid when we try to 367 ** actually open the file. 368 ** 369 ** Notice that ANY file with ANY execute bit is automatically 370 ** not writable. This is also enforced by mailfile. 371 ** 372 ** Parameters: 373 ** s -- pointer to a stat struct for the file. 374 ** 375 ** Returns: 376 ** TRUE -- if we will be able to write this file. 377 ** FALSE -- if we cannot write this file. 378 ** 379 ** Side Effects: 380 ** none. 381 */ 382 383 bool 384 writable(s) 385 register struct stat *s; 386 { 387 int euid, egid; 388 int bits; 389 390 if (bitset(0111, s->st_mode)) 391 return (FALSE); 392 euid = getruid(); 393 egid = getrgid(); 394 if (geteuid() == 0) 395 { 396 if (bitset(S_ISUID, s->st_mode)) 397 euid = s->st_uid; 398 if (bitset(S_ISGID, s->st_mode)) 399 egid = s->st_gid; 400 } 401 402 if (euid == 0) 403 return (TRUE); 404 bits = S_IWRITE; 405 if (euid != s->st_uid) 406 { 407 bits >>= 3; 408 if (egid != s->st_gid) 409 bits >>= 3; 410 } 411 return ((s->st_mode & bits) != 0); 412 } 413 /* 414 ** INCLUDE -- handle :include: specification. 415 ** 416 ** Parameters: 417 ** fname -- filename to include. 418 ** msg -- message to print in verbose mode. 419 ** ctladdr -- address template to use to fill in these 420 ** addresses -- effective user/group id are 421 ** the important things. 422 ** sendq -- a pointer to the head of the send queue 423 ** to put these addresses in. 424 ** 425 ** Returns: 426 ** none. 427 ** 428 ** Side Effects: 429 ** reads the :include: file and sends to everyone 430 ** listed in that file. 431 */ 432 433 include(fname, msg, ctladdr, sendq) 434 char *fname; 435 char *msg; 436 ADDRESS *ctladdr; 437 ADDRESS **sendq; 438 { 439 char buf[MAXLINE]; 440 register FILE *fp; 441 char *oldto = CurEnv->e_to; 442 char *oldfilename = FileName; 443 int oldlinenumber = LineNumber; 444 445 fp = fopen(fname, "r"); 446 if (fp == NULL) 447 { 448 usrerr("Cannot open %s", fname); 449 return; 450 } 451 if (getctladdr(ctladdr) == NULL) 452 { 453 struct stat st; 454 455 if (fstat(fileno(fp), &st) < 0) 456 syserr("Cannot fstat %s!", fname); 457 ctladdr->q_uid = st.st_uid; 458 ctladdr->q_gid = st.st_gid; 459 ctladdr->q_flags |= QGOODUID; 460 } 461 462 /* read the file -- each line is a comma-separated list. */ 463 FileName = fname; 464 LineNumber = 0; 465 while (fgets(buf, sizeof buf, fp) != NULL) 466 { 467 register char *p = index(buf, '\n'); 468 469 if (p != NULL) 470 *p = '\0'; 471 if (buf[0] == '\0') 472 continue; 473 CurEnv->e_to = oldto; 474 message(Arpa_Info, "%s to %s", msg, buf); 475 AliasLevel++; 476 sendtolist(buf, ctladdr, sendq); 477 AliasLevel--; 478 } 479 480 (void) fclose(fp); 481 FileName = oldfilename; 482 LineNumber = oldlinenumber; 483 } 484 /* 485 ** SENDTOARGV -- send to an argument vector. 486 ** 487 ** Parameters: 488 ** argv -- argument vector to send to. 489 ** 490 ** Returns: 491 ** none. 492 ** 493 ** Side Effects: 494 ** puts all addresses on the argument vector onto the 495 ** send queue. 496 */ 497 498 sendtoargv(argv) 499 register char **argv; 500 { 501 register char *p; 502 extern bool sameword(); 503 504 while ((p = *argv++) != NULL) 505 { 506 if (argv[0] != NULL && argv[1] != NULL && sameword(argv[0], "at")) 507 { 508 char nbuf[MAXNAME]; 509 510 if (strlen(p) + strlen(argv[1]) + 2 > sizeof nbuf) 511 usrerr("address overflow"); 512 else 513 { 514 (void) strcpy(nbuf, p); 515 (void) strcat(nbuf, "@"); 516 (void) strcat(nbuf, argv[1]); 517 p = newstr(nbuf); 518 argv += 2; 519 } 520 } 521 sendtolist(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 522 } 523 } 524 /* 525 ** GETCTLADDR -- get controlling address from an address header. 526 ** 527 ** If none, get one corresponding to the effective userid. 528 ** 529 ** Parameters: 530 ** a -- the address to find the controller of. 531 ** 532 ** Returns: 533 ** the controlling address. 534 ** 535 ** Side Effects: 536 ** none. 537 */ 538 539 ADDRESS * 540 getctladdr(a) 541 register ADDRESS *a; 542 { 543 while (a != NULL && !bitset(QGOODUID, a->q_flags)) 544 a = a->q_alias; 545 return (a); 546 } 547