14174Seric # include <pwd.h> 24627Seric # include "sendmail.h" 34329Seric # include <sys/stat.h> 44174Seric 5*6906Seric SCCSID(@(#)recipient.c 3.35 05/22/82); 64174Seric 74174Seric /* 84174Seric ** SENDTO -- Designate a send list. 94174Seric ** 104174Seric ** The parameter is a comma-separated list of people to send to. 114174Seric ** This routine arranges to send to all of them. 124174Seric ** 134174Seric ** Parameters: 144174Seric ** list -- the send list. 154174Seric ** copyf -- the copy flag; passed to parse. 164399Seric ** ctladdr -- the address template for the person to 174399Seric ** send to -- effective uid/gid are important. 185006Seric ** This is typically the alias that caused this 195006Seric ** expansion. 205006Seric ** sendq -- a pointer to the head of a queue to put 215006Seric ** these people into. 224174Seric ** 234174Seric ** Returns: 244998Seric ** none 254174Seric ** 264174Seric ** Side Effects: 274174Seric ** none. 284174Seric */ 294174Seric 304174Seric # define MAXRCRSN 10 314174Seric 325006Seric sendto(list, copyf, ctladdr, sendq) 334174Seric char *list; 344174Seric int copyf; 354399Seric ADDRESS *ctladdr; 365198Seric ADDRESS **sendq; 374174Seric { 384174Seric register char *p; 394319Seric bool more; /* set if more addresses to send to */ 404324Seric ADDRESS *al; /* list of addresses to send to */ 414423Seric bool firstone; /* set on first address sent */ 424444Seric bool selfref; /* set if this list includes ctladdr */ 434174Seric 444324Seric # ifdef DEBUG 454324Seric if (Debug > 1) 464444Seric { 474444Seric printf("sendto: %s\n ctladdr=", list); 484444Seric printaddr(ctladdr, FALSE); 494444Seric } 504324Seric # endif DEBUG 514324Seric 524174Seric more = TRUE; 534423Seric firstone = TRUE; 544444Seric selfref = FALSE; 554324Seric al = NULL; 564174Seric for (p = list; more; ) 574174Seric { 584319Seric register char *q; 594319Seric register char c; 604319Seric ADDRESS *a; 614319Seric 624174Seric /* find the end of this address */ 634174Seric while (*p == ' ' || *p == '\t') 644174Seric p++; 654174Seric q = p; 664174Seric while ((c = *p++) != '\0' && c != ',' && c != '\n') 674174Seric continue; 684174Seric more = c != '\0'; 694174Seric *--p = '\0'; 704174Seric if (more) 714174Seric p++; 724324Seric if (*q == '\0') 734324Seric continue; 744174Seric 754174Seric /* parse the address */ 764174Seric if ((a = parse(q, (ADDRESS *) NULL, copyf)) == NULL) 774174Seric continue; 784324Seric a->q_next = al; 794399Seric a->q_alias = ctladdr; 804444Seric 815706Seric /* 825706Seric ** If this address should have a host alias, take care 835706Seric ** of it now. 845706Seric */ 855706Seric 865706Seric if (strcmp(a->q_mailer->m_name, "xlate") == 0) 875706Seric { 885706Seric register char *newaddr; 895706Seric extern char *hostalias(); 905706Seric 915706Seric newaddr = hostalias(a); 925706Seric if (newaddr == NULL) 935706Seric { 945706Seric /* couldn't do anything with it */ 955706Seric giveresponse(EX_NOUSER, TRUE, a->q_mailer); 965706Seric continue; 975706Seric } 985706Seric 995706Seric if (Verbose) 1005706Seric printf("%s... translated to %s\n", a->q_paddr, newaddr); 1015706Seric 1025706Seric /* change the print address so the message looks good */ 1035706Seric a->q_paddr = newaddr; 1045706Seric 1055706Seric /* call ourselves recursively to send to our friends */ 1065706Seric sendto(newaddr, copyf > 0 ? 0 : copyf, ctladdr, sendq); 1075706Seric continue; 1085706Seric } 1095706Seric 1104444Seric /* see if this should be marked as a primary address */ 1114423Seric if (ctladdr == NULL || 1124423Seric (firstone && !more && bitset(QPRIMARY, ctladdr->q_flags))) 1134423Seric a->q_flags |= QPRIMARY; 1144444Seric 1154444Seric /* put on send queue or suppress self-reference */ 1164444Seric if (ctladdr != NULL && sameaddr(ctladdr, a, FALSE)) 1174444Seric selfref = TRUE; 1184444Seric else 1194444Seric al = a; 1204423Seric firstone = FALSE; 1214324Seric } 1224324Seric 1234444Seric /* if this alias doesn't include itself, delete ctladdr */ 1244444Seric if (!selfref && ctladdr != NULL) 1254444Seric ctladdr->q_flags |= QDONTSEND; 1264444Seric 1274324Seric /* arrange to send to everyone on the local send list */ 1284324Seric while (al != NULL) 1294324Seric { 1304324Seric register ADDRESS *a = al; 1314324Seric 1324324Seric al = a->q_next; 1335006Seric recipient(a, sendq); 1344993Seric 1354998Seric /* arrange to inherit full name */ 1364998Seric if (a->q_fullname == NULL && ctladdr != NULL) 1374998Seric a->q_fullname = ctladdr->q_fullname; 1384174Seric } 1394324Seric 140*6906Seric CurEnv->e_to = NULL; 1414174Seric } 1424174Seric /* 1434174Seric ** RECIPIENT -- Designate a message recipient 1444174Seric ** 1454174Seric ** Saves the named person for future mailing. 1464174Seric ** 1474174Seric ** Parameters: 1484174Seric ** a -- the (preparsed) address header for the recipient. 1495006Seric ** sendq -- a pointer to the head of a queue to put the 1505006Seric ** recipient in. Duplicate supression is done 1515006Seric ** in this queue. 1524174Seric ** 1534174Seric ** Returns: 1544998Seric ** none. 1554174Seric ** 1564174Seric ** Side Effects: 1574174Seric ** none. 1584174Seric */ 1594174Seric 1605006Seric recipient(a, sendq) 1614174Seric register ADDRESS *a; 1625006Seric register ADDRESS **sendq; 1634174Seric { 1644174Seric register ADDRESS *q; 1654319Seric ADDRESS **pq; 1664174Seric register struct mailer *m; 1674399Seric extern ADDRESS *getctladdr(); 1684627Seric extern bool safefile(); 1694174Seric 170*6906Seric CurEnv->e_to = a->q_paddr; 1714600Seric m = a->q_mailer; 1724174Seric errno = 0; 1734174Seric # ifdef DEBUG 1744174Seric if (Debug) 1754444Seric { 1764444Seric printf("\nrecipient: "); 1774444Seric printaddr(a, FALSE); 1784444Seric } 1794174Seric # endif DEBUG 1804174Seric 1814174Seric /* break aliasing loops */ 1824174Seric if (AliasLevel > MAXRCRSN) 1834174Seric { 1844174Seric usrerr("aliasing/forwarding loop broken"); 1854998Seric return; 1864174Seric } 1874174Seric 1884174Seric /* 1894627Seric ** Finish setting up address structure. 1904174Seric */ 1914174Seric 1924627Seric a->q_timeout = TimeOut; 1934627Seric 1944627Seric /* do sickly crude mapping for program mailing, etc. */ 1954600Seric if (a->q_mailer == LocalMailer) 1964174Seric { 1974174Seric if (a->q_user[0] == '|') 1984174Seric { 1994600Seric a->q_mailer = m = ProgMailer; 2004174Seric a->q_user++; 2015316Seric if (a->q_alias == NULL && Debug == 0 && !QueueRun && !ForceMail) 2024217Seric { 2034217Seric usrerr("Cannot mail directly to programs"); 2044217Seric a->q_flags |= QDONTSEND; 2054217Seric } 2064174Seric } 2074174Seric } 2084174Seric 2094174Seric /* 2104419Seric ** Look up this person in the recipient list. 2114419Seric ** If they are there already, return, otherwise continue. 2124419Seric ** If the list is empty, just add it. Notice the cute 2134419Seric ** hack to make from addresses suppress things correctly: 2144419Seric ** the QDONTSEND bit will be set in the send list. 2154419Seric ** [Please note: the emphasis is on "hack."] 2164174Seric */ 2174174Seric 2185006Seric for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next) 2194174Seric { 2204319Seric if (!ForceMail && sameaddr(q, a, FALSE)) 2214174Seric { 2224174Seric # ifdef DEBUG 2234319Seric if (Debug) 2244444Seric { 2254444Seric printf("%s in sendq: ", a->q_paddr); 2264444Seric printaddr(q, FALSE); 2274444Seric } 2284174Seric # endif DEBUG 2294998Seric if (Verbose && !bitset(QDONTSEND, a->q_flags)) 2304324Seric message(Arpa_Info, "duplicate suppressed"); 2314423Seric if (!bitset(QPRIMARY, q->q_flags)) 2324423Seric q->q_flags |= a->q_flags; 2334998Seric return; 2344174Seric } 2354319Seric } 2364174Seric 2374319Seric /* add address on list */ 2384319Seric *pq = a; 2394174Seric a->q_next = NULL; 2404247Seric if (DontSend) 2414247Seric a->q_flags |= QDONTSEND; 2424174Seric 2434174Seric /* 2444174Seric ** Alias the name and handle :include: specs. 2454174Seric */ 2464174Seric 2474600Seric if (a->q_mailer == LocalMailer) 2484174Seric { 2494174Seric if (strncmp(a->q_user, ":include:", 9) == 0) 2504174Seric { 2514174Seric a->q_flags |= QDONTSEND; 2525316Seric if (a->q_alias == NULL && Debug == 0 && !QueueRun && !ForceMail) 2534399Seric usrerr("Cannot mail directly to :include:s"); 2544399Seric else 2554399Seric { 2564399Seric if (Verbose) 2574399Seric message(Arpa_Info, "including file %s", &a->q_user[9]); 2585006Seric include(&a->q_user[9], " sending", a, sendq); 2594399Seric } 2604174Seric } 2614174Seric else 2625006Seric alias(a, sendq); 2634174Seric } 2644174Seric 2654174Seric /* 2664174Seric ** If the user is local and still being sent, verify that 2674174Seric ** the address is good. If it is, try to forward. 2684174Seric ** If the address is already good, we have a forwarding 2694174Seric ** loop. This can be broken by just sending directly to 2704174Seric ** the user (which is probably correct anyway). 2714174Seric */ 2724174Seric 2734600Seric if (!bitset(QDONTSEND, a->q_flags) && a->q_mailer == LocalMailer) 2744174Seric { 2754174Seric char buf[MAXNAME]; 2764201Seric register char *p; 2774329Seric struct stat stb; 2784329Seric extern bool writable(); 2794399Seric bool quoted = FALSE; 2804174Seric 2814174Seric strcpy(buf, a->q_user); 2824399Seric for (p = buf; *p != '\0' && !quoted; p++) 2834399Seric { 2844998Seric if (!isascii(*p) && (*p & 0377) != (SPACESUB & 0377)) 2854399Seric quoted = TRUE; 2864399Seric } 2874174Seric stripquotes(buf, TRUE); 2884174Seric 2894174Seric /* see if this is to a file */ 2905600Seric if (buf[0] == '/') 2914174Seric { 2925600Seric p = rindex(buf, '/'); 2934201Seric /* check if writable or creatable */ 2945316Seric if (a->q_alias == NULL && Debug == 0 && !QueueRun && !ForceMail) 2954399Seric { 2964399Seric usrerr("Cannot mail directly to files"); 2974399Seric a->q_flags |= QDONTSEND; 2984399Seric } 2994399Seric else if ((stat(buf, &stb) >= 0) ? (!writable(&stb)) : 3004539Seric (*p = '\0', !safefile(buf, getruid(), S_IWRITE|S_IEXEC))) 3014174Seric { 3024174Seric a->q_flags |= QBADADDR; 3034174Seric giveresponse(EX_CANTCREAT, TRUE, m); 3044174Seric } 3054174Seric } 3064174Seric else 3074174Seric { 3084174Seric register struct passwd *pw; 3094373Seric extern struct passwd *finduser(); 3104373Seric 3114407Seric /* warning -- finduser may trash buf */ 3124373Seric pw = finduser(buf); 3134174Seric if (pw == NULL) 3144174Seric { 3154174Seric a->q_flags |= QBADADDR; 3164174Seric giveresponse(EX_NOUSER, TRUE, m); 3174174Seric } 3184174Seric else 3194174Seric { 3204993Seric char nbuf[MAXNAME]; 3214993Seric 3224376Seric if (strcmp(a->q_user, pw->pw_name) != 0) 3234376Seric { 3244376Seric a->q_user = newstr(pw->pw_name); 3254376Seric strcpy(buf, pw->pw_name); 3264376Seric } 3274174Seric a->q_home = newstr(pw->pw_dir); 3284213Seric a->q_uid = pw->pw_uid; 3294399Seric a->q_gid = pw->pw_gid; 3304404Seric a->q_flags |= QGOODUID; 3314998Seric buildfname(pw->pw_gecos, pw->pw_name, nbuf); 3324993Seric if (nbuf[0] != '\0') 3334993Seric a->q_fullname = newstr(nbuf); 3344399Seric if (!quoted) 3355006Seric forward(a, sendq); 3364174Seric } 3374174Seric } 3384174Seric } 3394174Seric } 3404174Seric /* 3414373Seric ** FINDUSER -- find the password entry for a user. 3424373Seric ** 3434373Seric ** This looks a lot like getpwnam, except that it may want to 3444373Seric ** do some fancier pattern matching in /etc/passwd. 3454373Seric ** 3464373Seric ** Parameters: 3474373Seric ** name -- the name to match against. 3484373Seric ** 3494373Seric ** Returns: 3504373Seric ** A pointer to a pw struct. 3514373Seric ** NULL if name is unknown or ambiguous. 3524373Seric ** 3534373Seric ** Side Effects: 3544407Seric ** may modify name. 3554373Seric */ 3564373Seric 3574373Seric struct passwd * 3584373Seric finduser(name) 3594373Seric char *name; 3604373Seric { 3614376Seric extern struct passwd *getpwent(); 3624376Seric register struct passwd *pw; 3634407Seric register char *p; 3644373Seric 3654407Seric /* 3664407Seric ** Make name canonical. 3674407Seric */ 3684407Seric 3694407Seric for (p = name; *p != '\0'; p++) 3704407Seric { 3714407Seric if (*p == (SPACESUB & 0177) || *p == '_') 3724407Seric *p = ' '; 3734407Seric } 3744407Seric 3754376Seric setpwent(); 3764376Seric while ((pw = getpwent()) != NULL) 3774376Seric { 3784998Seric char buf[MAXNAME]; 3794993Seric extern bool sameword(); 3804376Seric 3814376Seric if (strcmp(pw->pw_name, name) == 0) 3824376Seric return (pw); 3834998Seric buildfname(pw->pw_gecos, pw->pw_name, buf); 3844407Seric if (index(buf, ' ') != NULL && sameword(buf, name)) 3854381Seric { 3864377Seric if (Verbose) 3874998Seric message(Arpa_Info, "sending to login name %s", 3884998Seric pw->pw_name); 3894376Seric return (pw); 3904377Seric } 3914376Seric } 3924376Seric return (NULL); 3934373Seric } 3944373Seric /* 3954329Seric ** WRITABLE -- predicate returning if the file is writable. 3964329Seric ** 3974329Seric ** This routine must duplicate the algorithm in sys/fio.c. 3984329Seric ** Unfortunately, we cannot use the access call since we 3994329Seric ** won't necessarily be the real uid when we try to 4004329Seric ** actually open the file. 4014329Seric ** 4024329Seric ** Notice that ANY file with ANY execute bit is automatically 4034329Seric ** not writable. This is also enforced by mailfile. 4044329Seric ** 4054329Seric ** Parameters: 4064329Seric ** s -- pointer to a stat struct for the file. 4074329Seric ** 4084329Seric ** Returns: 4094329Seric ** TRUE -- if we will be able to write this file. 4104329Seric ** FALSE -- if we cannot write this file. 4114329Seric ** 4124329Seric ** Side Effects: 4134329Seric ** none. 4144329Seric */ 4154329Seric 4164329Seric bool 4174329Seric writable(s) 4184329Seric register struct stat *s; 4194329Seric { 4204329Seric int euid, egid; 4214329Seric int bits; 4224329Seric 4234329Seric if (bitset(0111, s->st_mode)) 4244329Seric return (FALSE); 4254329Seric euid = getruid(); 4264329Seric egid = getrgid(); 4274329Seric if (geteuid() == 0) 4284329Seric { 4294329Seric if (bitset(S_ISUID, s->st_mode)) 4304329Seric euid = s->st_uid; 4314329Seric if (bitset(S_ISGID, s->st_mode)) 4324329Seric egid = s->st_gid; 4334329Seric } 4344329Seric 4354329Seric if (euid == 0) 4364329Seric return (TRUE); 4374329Seric bits = S_IWRITE; 4384329Seric if (euid != s->st_uid) 4394329Seric { 4404329Seric bits >>= 3; 4414329Seric if (egid != s->st_gid) 4424329Seric bits >>= 3; 4434329Seric } 4444329Seric return ((s->st_mode & bits) != 0); 4454329Seric } 4464329Seric /* 4474174Seric ** INCLUDE -- handle :include: specification. 4484174Seric ** 4494174Seric ** Parameters: 4504174Seric ** fname -- filename to include. 4514176Seric ** msg -- message to print in verbose mode. 4524399Seric ** ctladdr -- address template to use to fill in these 4534399Seric ** addresses -- effective user/group id are 4544399Seric ** the important things. 4555006Seric ** sendq -- a pointer to the head of the send queue 4565006Seric ** to put these addresses in. 4574174Seric ** 4584174Seric ** Returns: 4594174Seric ** none. 4604174Seric ** 4614174Seric ** Side Effects: 4624174Seric ** reads the :include: file and sends to everyone 4634174Seric ** listed in that file. 4644174Seric */ 4654174Seric 4665006Seric include(fname, msg, ctladdr, sendq) 4674174Seric char *fname; 4684176Seric char *msg; 4694399Seric ADDRESS *ctladdr; 4705006Seric ADDRESS **sendq; 4714174Seric { 4724174Seric char buf[MAXLINE]; 4734174Seric register FILE *fp; 474*6906Seric char *oldto = CurEnv->e_to; 4754174Seric 4764174Seric fp = fopen(fname, "r"); 4774174Seric if (fp == NULL) 4784174Seric { 4794174Seric usrerr("Cannot open %s", fname); 4804174Seric return; 4814174Seric } 4824406Seric if (getctladdr(ctladdr) == NULL) 4834406Seric { 4844406Seric struct stat st; 4854174Seric 4864406Seric if (fstat(fileno(fp), &st) < 0) 4874406Seric syserr("Cannot fstat %s!", fname); 4884406Seric ctladdr->q_uid = st.st_uid; 4894406Seric ctladdr->q_gid = st.st_gid; 4904406Seric ctladdr->q_flags |= QGOODUID; 4914406Seric } 4924406Seric 4934174Seric /* read the file -- each line is a comma-separated list. */ 4944174Seric while (fgets(buf, sizeof buf, fp) != NULL) 4954174Seric { 4964174Seric register char *p = index(buf, '\n'); 4974174Seric 4984174Seric if (p != NULL) 4994174Seric *p = '\0'; 5004174Seric if (buf[0] == '\0') 5014174Seric continue; 502*6906Seric CurEnv->e_to = oldto; 5034174Seric if (Verbose) 5044176Seric message(Arpa_Info, "%s to %s", msg, buf); 5054176Seric AliasLevel++; 5065006Seric sendto(buf, 1, ctladdr, sendq); 5074176Seric AliasLevel--; 5084174Seric } 5094174Seric 5104319Seric (void) fclose(fp); 5114174Seric } 5124324Seric /* 5134324Seric ** SENDTOARGV -- send to an argument vector. 5144324Seric ** 5154324Seric ** Parameters: 5164324Seric ** argv -- argument vector to send to. 5174324Seric ** 5184324Seric ** Returns: 5194324Seric ** none. 5204324Seric ** 5214324Seric ** Side Effects: 5224324Seric ** puts all addresses on the argument vector onto the 5234324Seric ** send queue. 5244324Seric */ 5254324Seric 5264324Seric sendtoargv(argv) 5274324Seric register char **argv; 5284324Seric { 5294324Seric register char *p; 5304324Seric extern bool sameword(); 5314324Seric 5324324Seric while ((p = *argv++) != NULL) 5334324Seric { 5344324Seric if (argv[0] != NULL && argv[1] != NULL && sameword(argv[0], "at")) 5354324Seric { 5364324Seric char nbuf[MAXNAME]; 5374324Seric 5384324Seric if (strlen(p) + strlen(argv[1]) + 2 > sizeof nbuf) 5394324Seric usrerr("address overflow"); 5404324Seric else 5414324Seric { 5424324Seric (void) strcpy(nbuf, p); 5434324Seric (void) strcat(nbuf, "@"); 5444324Seric (void) strcat(nbuf, argv[1]); 5454324Seric p = newstr(nbuf); 5464324Seric argv += 2; 5474324Seric } 5484324Seric } 549*6906Seric sendto(p, 0, (ADDRESS *) NULL, &CurEnv->e_sendqueue); 5504324Seric } 5514324Seric } 5524399Seric /* 5534399Seric ** GETCTLADDR -- get controlling address from an address header. 5544399Seric ** 5554399Seric ** If none, get one corresponding to the effective userid. 5564399Seric ** 5574399Seric ** Parameters: 5584399Seric ** a -- the address to find the controller of. 5594399Seric ** 5604399Seric ** Returns: 5614399Seric ** the controlling address. 5624399Seric ** 5634399Seric ** Side Effects: 5644399Seric ** none. 5654399Seric */ 5664399Seric 5674399Seric ADDRESS * 5684399Seric getctladdr(a) 5694399Seric register ADDRESS *a; 5704399Seric { 5714404Seric while (a != NULL && !bitset(QGOODUID, a->q_flags)) 5724399Seric a = a->q_alias; 5734399Seric return (a); 5744399Seric } 575