14174Seric # include <pwd.h> 2*4627Seric # include "sendmail.h" 34329Seric # include <sys/stat.h> 44174Seric 5*4627Seric static char SccsId[] = "@(#)recipient.c 3.28 10/26/81"; 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. 184174Seric ** 194174Seric ** Returns: 204174Seric ** none 214174Seric ** 224174Seric ** Side Effects: 234174Seric ** none. 244174Seric */ 254174Seric 264174Seric # define MAXRCRSN 10 274174Seric 284399Seric sendto(list, copyf, ctladdr) 294174Seric char *list; 304174Seric int copyf; 314399Seric ADDRESS *ctladdr; 324174Seric { 334174Seric register char *p; 344319Seric bool more; /* set if more addresses to send to */ 354324Seric ADDRESS *al; /* list of addresses to send to */ 364423Seric bool firstone; /* set on first address sent */ 374444Seric bool selfref; /* set if this list includes ctladdr */ 384174Seric 394324Seric # ifdef DEBUG 404324Seric if (Debug > 1) 414444Seric { 424444Seric printf("sendto: %s\n ctladdr=", list); 434444Seric printaddr(ctladdr, FALSE); 444444Seric } 454324Seric # endif DEBUG 464324Seric 474174Seric more = TRUE; 484423Seric firstone = TRUE; 494444Seric selfref = FALSE; 504324Seric al = NULL; 514174Seric for (p = list; more; ) 524174Seric { 534319Seric register char *q; 544319Seric register char c; 554319Seric ADDRESS *a; 564319Seric 574174Seric /* find the end of this address */ 584174Seric while (*p == ' ' || *p == '\t') 594174Seric p++; 604174Seric q = p; 614174Seric while ((c = *p++) != '\0' && c != ',' && c != '\n') 624174Seric continue; 634174Seric more = c != '\0'; 644174Seric *--p = '\0'; 654174Seric if (more) 664174Seric p++; 674324Seric if (*q == '\0') 684324Seric continue; 694174Seric 704174Seric /* parse the address */ 714174Seric if ((a = parse(q, (ADDRESS *) NULL, copyf)) == NULL) 724174Seric continue; 734324Seric a->q_next = al; 744399Seric a->q_alias = ctladdr; 754444Seric 764444Seric /* see if this should be marked as a primary address */ 774423Seric if (ctladdr == NULL || 784423Seric (firstone && !more && bitset(QPRIMARY, ctladdr->q_flags))) 794423Seric a->q_flags |= QPRIMARY; 804444Seric 814444Seric /* put on send queue or suppress self-reference */ 824444Seric if (ctladdr != NULL && sameaddr(ctladdr, a, FALSE)) 834444Seric selfref = TRUE; 844444Seric else 854444Seric al = a; 864423Seric firstone = FALSE; 874324Seric } 884324Seric 894444Seric /* if this alias doesn't include itself, delete ctladdr */ 904444Seric if (!selfref && ctladdr != NULL) 914444Seric ctladdr->q_flags |= QDONTSEND; 924444Seric 934324Seric /* arrange to send to everyone on the local send list */ 944324Seric while (al != NULL) 954324Seric { 964324Seric register ADDRESS *a = al; 974324Seric 984324Seric al = a->q_next; 994174Seric recipient(a); 1004174Seric } 1014324Seric 1024174Seric To = NULL; 1034174Seric } 1044174Seric /* 1054174Seric ** RECIPIENT -- Designate a message recipient 1064174Seric ** 1074174Seric ** Saves the named person for future mailing. 1084174Seric ** 1094174Seric ** Parameters: 1104174Seric ** a -- the (preparsed) address header for the recipient. 1114174Seric ** 1124174Seric ** Returns: 1134174Seric ** none. 1144174Seric ** 1154174Seric ** Side Effects: 1164174Seric ** none. 1174174Seric */ 1184174Seric 1194174Seric recipient(a) 1204174Seric register ADDRESS *a; 1214174Seric { 1224174Seric register ADDRESS *q; 1234319Seric ADDRESS **pq; 1244174Seric register struct mailer *m; 1254399Seric extern ADDRESS *getctladdr(); 126*4627Seric extern bool safefile(); 1274174Seric 1284174Seric To = a->q_paddr; 1294600Seric m = a->q_mailer; 1304174Seric errno = 0; 1314174Seric # ifdef DEBUG 1324174Seric if (Debug) 1334444Seric { 1344444Seric printf("\nrecipient: "); 1354444Seric printaddr(a, FALSE); 1364444Seric } 1374174Seric # endif DEBUG 1384174Seric 1394174Seric /* break aliasing loops */ 1404174Seric if (AliasLevel > MAXRCRSN) 1414174Seric { 1424174Seric usrerr("aliasing/forwarding loop broken"); 1434174Seric return; 1444174Seric } 1454174Seric 1464174Seric /* 147*4627Seric ** Finish setting up address structure. 1484174Seric */ 1494174Seric 150*4627Seric a->q_timeout = TimeOut; 151*4627Seric 152*4627Seric /* do sickly crude mapping for program mailing, etc. */ 1534600Seric if (a->q_mailer == LocalMailer) 1544174Seric { 1554174Seric if (a->q_user[0] == '|') 1564174Seric { 1574600Seric a->q_mailer = m = ProgMailer; 1584174Seric a->q_user++; 159*4627Seric if (a->q_alias == NULL && Debug == 0 && !QueueRun) 1604217Seric { 1614217Seric usrerr("Cannot mail directly to programs"); 1624217Seric a->q_flags |= QDONTSEND; 1634217Seric } 1644174Seric } 1654174Seric } 1664174Seric 1674174Seric /* 1684419Seric ** Look up this person in the recipient list. 1694419Seric ** If they are there already, return, otherwise continue. 1704419Seric ** If the list is empty, just add it. Notice the cute 1714419Seric ** hack to make from addresses suppress things correctly: 1724419Seric ** the QDONTSEND bit will be set in the send list. 1734419Seric ** [Please note: the emphasis is on "hack."] 1744174Seric */ 1754174Seric 1764319Seric for (pq = &m->m_sendq; (q = *pq) != NULL; pq = &q->q_next) 1774174Seric { 1784319Seric if (!ForceMail && sameaddr(q, a, FALSE)) 1794174Seric { 1804174Seric # ifdef DEBUG 1814319Seric if (Debug) 1824444Seric { 1834444Seric printf("%s in sendq: ", a->q_paddr); 1844444Seric printaddr(q, FALSE); 1854444Seric } 1864174Seric # endif DEBUG 1874319Seric if (Verbose && !bitset(QDONTSEND, a->q_flags)) 1884324Seric message(Arpa_Info, "duplicate suppressed"); 1894423Seric if (!bitset(QPRIMARY, q->q_flags)) 1904423Seric q->q_flags |= a->q_flags; 1914319Seric return; 1924174Seric } 1934319Seric } 1944174Seric 1954319Seric /* add address on list */ 1964319Seric *pq = a; 1974174Seric a->q_next = NULL; 1984247Seric if (DontSend) 1994247Seric a->q_flags |= QDONTSEND; 2004174Seric 2014174Seric /* 2024174Seric ** Alias the name and handle :include: specs. 2034174Seric */ 2044174Seric 2054600Seric if (a->q_mailer == LocalMailer) 2064174Seric { 2074174Seric if (strncmp(a->q_user, ":include:", 9) == 0) 2084174Seric { 2094174Seric a->q_flags |= QDONTSEND; 210*4627Seric if (a->q_alias == NULL && Debug == 0 && !QueueRun) 2114399Seric usrerr("Cannot mail directly to :include:s"); 2124399Seric else 2134399Seric { 2144399Seric if (Verbose) 2154399Seric message(Arpa_Info, "including file %s", &a->q_user[9]); 2164399Seric include(&a->q_user[9], " sending", a); 2174399Seric } 2184174Seric } 2194174Seric else 2204174Seric alias(a); 2214174Seric } 2224174Seric 2234174Seric /* 2244174Seric ** If the user is local and still being sent, verify that 2254174Seric ** the address is good. If it is, try to forward. 2264174Seric ** If the address is already good, we have a forwarding 2274174Seric ** loop. This can be broken by just sending directly to 2284174Seric ** the user (which is probably correct anyway). 2294174Seric */ 2304174Seric 2314600Seric if (!bitset(QDONTSEND, a->q_flags) && a->q_mailer == LocalMailer) 2324174Seric { 2334174Seric char buf[MAXNAME]; 2344201Seric register char *p; 2354329Seric struct stat stb; 2364329Seric extern bool writable(); 2374399Seric bool quoted = FALSE; 2384174Seric 2394174Seric strcpy(buf, a->q_user); 2404399Seric for (p = buf; *p != '\0' && !quoted; p++) 2414399Seric { 2424399Seric if (!isascii(*p)) 2434399Seric quoted = TRUE; 2444399Seric } 2454174Seric stripquotes(buf, TRUE); 2464174Seric 2474174Seric /* see if this is to a file */ 2484201Seric if ((p = rindex(buf, '/')) != NULL) 2494174Seric { 2504201Seric /* check if writable or creatable */ 251*4627Seric if (a->q_alias == NULL && Debug == 0 && !QueueRun) 2524399Seric { 2534399Seric usrerr("Cannot mail directly to files"); 2544399Seric a->q_flags |= QDONTSEND; 2554399Seric } 2564399Seric else if ((stat(buf, &stb) >= 0) ? (!writable(&stb)) : 2574539Seric (*p = '\0', !safefile(buf, getruid(), S_IWRITE|S_IEXEC))) 2584174Seric { 2594174Seric a->q_flags |= QBADADDR; 2604174Seric giveresponse(EX_CANTCREAT, TRUE, m); 2614174Seric } 2624174Seric } 2634174Seric else 2644174Seric { 2654174Seric register struct passwd *pw; 2664373Seric extern struct passwd *finduser(); 2674373Seric 2684407Seric /* warning -- finduser may trash buf */ 2694373Seric pw = finduser(buf); 2704174Seric if (pw == NULL) 2714174Seric { 2724174Seric a->q_flags |= QBADADDR; 2734174Seric giveresponse(EX_NOUSER, TRUE, m); 2744174Seric } 2754174Seric else 2764174Seric { 2774376Seric if (strcmp(a->q_user, pw->pw_name) != 0) 2784376Seric { 2794376Seric a->q_user = newstr(pw->pw_name); 2804376Seric strcpy(buf, pw->pw_name); 2814376Seric } 2824174Seric a->q_home = newstr(pw->pw_dir); 2834213Seric a->q_uid = pw->pw_uid; 2844399Seric a->q_gid = pw->pw_gid; 2854404Seric a->q_flags |= QGOODUID; 2864399Seric if (!quoted) 2874174Seric forward(a); 2884174Seric } 2894174Seric } 2904174Seric } 2914174Seric } 2924174Seric /* 2934373Seric ** FINDUSER -- find the password entry for a user. 2944373Seric ** 2954373Seric ** This looks a lot like getpwnam, except that it may want to 2964373Seric ** do some fancier pattern matching in /etc/passwd. 2974373Seric ** 2984373Seric ** Parameters: 2994373Seric ** name -- the name to match against. 3004373Seric ** 3014373Seric ** Returns: 3024373Seric ** A pointer to a pw struct. 3034373Seric ** NULL if name is unknown or ambiguous. 3044373Seric ** 3054373Seric ** Side Effects: 3064407Seric ** may modify name. 3074373Seric */ 3084373Seric 3094373Seric struct passwd * 3104373Seric finduser(name) 3114373Seric char *name; 3124373Seric { 3134376Seric extern struct passwd *getpwent(); 3144376Seric register struct passwd *pw; 3154407Seric register char *p; 3164373Seric 3174407Seric /* 3184407Seric ** Make name canonical. 3194407Seric */ 3204407Seric 3214407Seric for (p = name; *p != '\0'; p++) 3224407Seric { 3234407Seric if (*p == (SPACESUB & 0177) || *p == '_') 3244407Seric *p = ' '; 3254407Seric } 3264407Seric 3274376Seric setpwent(); 3284376Seric while ((pw = getpwent()) != NULL) 3294376Seric { 3304376Seric char buf[MAXNAME]; 3314376Seric extern bool sameword(); 3324376Seric 3334376Seric if (strcmp(pw->pw_name, name) == 0) 3344376Seric return (pw); 3354376Seric buildfname(pw->pw_gecos, pw->pw_name, buf); 3364407Seric if (index(buf, ' ') != NULL && sameword(buf, name)) 3374381Seric { 3384377Seric if (Verbose) 3394381Seric message(Arpa_Info, "sending to login name %s", 3404381Seric pw->pw_name); 3414376Seric return (pw); 3424377Seric } 3434376Seric } 3444376Seric return (NULL); 3454373Seric } 3464373Seric /* 3474329Seric ** WRITABLE -- predicate returning if the file is writable. 3484329Seric ** 3494329Seric ** This routine must duplicate the algorithm in sys/fio.c. 3504329Seric ** Unfortunately, we cannot use the access call since we 3514329Seric ** won't necessarily be the real uid when we try to 3524329Seric ** actually open the file. 3534329Seric ** 3544329Seric ** Notice that ANY file with ANY execute bit is automatically 3554329Seric ** not writable. This is also enforced by mailfile. 3564329Seric ** 3574329Seric ** Parameters: 3584329Seric ** s -- pointer to a stat struct for the file. 3594329Seric ** 3604329Seric ** Returns: 3614329Seric ** TRUE -- if we will be able to write this file. 3624329Seric ** FALSE -- if we cannot write this file. 3634329Seric ** 3644329Seric ** Side Effects: 3654329Seric ** none. 3664329Seric */ 3674329Seric 3684329Seric bool 3694329Seric writable(s) 3704329Seric register struct stat *s; 3714329Seric { 3724329Seric int euid, egid; 3734329Seric int bits; 3744329Seric 3754329Seric if (bitset(0111, s->st_mode)) 3764329Seric return (FALSE); 3774329Seric euid = getruid(); 3784329Seric egid = getrgid(); 3794329Seric if (geteuid() == 0) 3804329Seric { 3814329Seric if (bitset(S_ISUID, s->st_mode)) 3824329Seric euid = s->st_uid; 3834329Seric if (bitset(S_ISGID, s->st_mode)) 3844329Seric egid = s->st_gid; 3854329Seric } 3864329Seric 3874329Seric if (euid == 0) 3884329Seric return (TRUE); 3894329Seric bits = S_IWRITE; 3904329Seric if (euid != s->st_uid) 3914329Seric { 3924329Seric bits >>= 3; 3934329Seric if (egid != s->st_gid) 3944329Seric bits >>= 3; 3954329Seric } 3964329Seric return ((s->st_mode & bits) != 0); 3974329Seric } 3984329Seric /* 3994174Seric ** INCLUDE -- handle :include: specification. 4004174Seric ** 4014174Seric ** Parameters: 4024174Seric ** fname -- filename to include. 4034176Seric ** msg -- message to print in verbose mode. 4044399Seric ** ctladdr -- address template to use to fill in these 4054399Seric ** addresses -- effective user/group id are 4064399Seric ** the important things. 4074174Seric ** 4084174Seric ** Returns: 4094174Seric ** none. 4104174Seric ** 4114174Seric ** Side Effects: 4124174Seric ** reads the :include: file and sends to everyone 4134174Seric ** listed in that file. 4144174Seric */ 4154174Seric 4164399Seric include(fname, msg, ctladdr) 4174174Seric char *fname; 4184176Seric char *msg; 4194399Seric ADDRESS *ctladdr; 4204174Seric { 4214174Seric char buf[MAXLINE]; 4224174Seric register FILE *fp; 4234178Seric char *oldto = To; 4244174Seric 4254174Seric fp = fopen(fname, "r"); 4264174Seric if (fp == NULL) 4274174Seric { 4284174Seric usrerr("Cannot open %s", fname); 4294174Seric return; 4304174Seric } 4314406Seric if (getctladdr(ctladdr) == NULL) 4324406Seric { 4334406Seric struct stat st; 4344174Seric 4354406Seric if (fstat(fileno(fp), &st) < 0) 4364406Seric syserr("Cannot fstat %s!", fname); 4374406Seric ctladdr->q_uid = st.st_uid; 4384406Seric ctladdr->q_gid = st.st_gid; 4394406Seric ctladdr->q_flags |= QGOODUID; 4404406Seric } 4414406Seric 4424174Seric /* read the file -- each line is a comma-separated list. */ 4434174Seric while (fgets(buf, sizeof buf, fp) != NULL) 4444174Seric { 4454174Seric register char *p = index(buf, '\n'); 4464174Seric 4474174Seric if (p != NULL) 4484174Seric *p = '\0'; 4494174Seric if (buf[0] == '\0') 4504174Seric continue; 4514178Seric To = oldto; 4524174Seric if (Verbose) 4534176Seric message(Arpa_Info, "%s to %s", msg, buf); 4544176Seric AliasLevel++; 4554399Seric sendto(buf, 1, ctladdr); 4564176Seric AliasLevel--; 4574174Seric } 4584174Seric 4594319Seric (void) fclose(fp); 4604174Seric } 4614324Seric /* 4624324Seric ** SENDTOARGV -- send to an argument vector. 4634324Seric ** 4644324Seric ** Parameters: 4654324Seric ** argv -- argument vector to send to. 4664324Seric ** 4674324Seric ** Returns: 4684324Seric ** none. 4694324Seric ** 4704324Seric ** Side Effects: 4714324Seric ** puts all addresses on the argument vector onto the 4724324Seric ** send queue. 4734324Seric */ 4744324Seric 4754324Seric sendtoargv(argv) 4764324Seric register char **argv; 4774324Seric { 4784324Seric register char *p; 4794324Seric extern bool sameword(); 4804324Seric 4814324Seric while ((p = *argv++) != NULL) 4824324Seric { 4834324Seric if (argv[0] != NULL && argv[1] != NULL && sameword(argv[0], "at")) 4844324Seric { 4854324Seric char nbuf[MAXNAME]; 4864324Seric 4874324Seric if (strlen(p) + strlen(argv[1]) + 2 > sizeof nbuf) 4884324Seric usrerr("address overflow"); 4894324Seric else 4904324Seric { 4914324Seric (void) strcpy(nbuf, p); 4924324Seric (void) strcat(nbuf, "@"); 4934324Seric (void) strcat(nbuf, argv[1]); 4944324Seric p = newstr(nbuf); 4954324Seric argv += 2; 4964324Seric } 4974324Seric } 498*4627Seric sendto(p, 0, (ADDRESS *) NULL); 4994324Seric } 5004324Seric } 5014399Seric /* 5024399Seric ** GETCTLADDR -- get controlling address from an address header. 5034399Seric ** 5044399Seric ** If none, get one corresponding to the effective userid. 5054399Seric ** 5064399Seric ** Parameters: 5074399Seric ** a -- the address to find the controller of. 5084399Seric ** 5094399Seric ** Returns: 5104399Seric ** the controlling address. 5114399Seric ** 5124399Seric ** Side Effects: 5134399Seric ** none. 5144399Seric */ 5154399Seric 5164399Seric ADDRESS * 5174399Seric getctladdr(a) 5184399Seric register ADDRESS *a; 5194399Seric { 5204404Seric while (a != NULL && !bitset(QGOODUID, a->q_flags)) 5214399Seric a = a->q_alias; 5224399Seric return (a); 5234399Seric } 524