Commit | Line | Data |
---|---|---|
b2a81223 | 1 | /* |
dc45ba8c | 2 | * Copyright (c) 1983 Eric P. Allman |
c1f0acb8 KB |
3 | * Copyright (c) 1988, 1993 |
4 | * The Regents of the University of California. All rights reserved. | |
bee79b64 | 5 | * |
417f7a11 | 6 | * %sccs.include.redist.c% |
bee79b64 | 7 | */ |
b2a81223 DF |
8 | |
9 | #ifndef lint | |
92f2b65e | 10 | static char sccsid[] = "@(#)recipient.c 8.11 (Berkeley) %G%"; |
bee79b64 | 11 | #endif /* not lint */ |
b2a81223 | 12 | |
72041f93 | 13 | # include "sendmail.h" |
6eefe4f6 | 14 | # include <pwd.h> |
6eefe4f6 | 15 | |
6eefe4f6 | 16 | /* |
811a6cf3 | 17 | ** SENDTOLIST -- Designate a send list. |
6eefe4f6 EA |
18 | ** |
19 | ** The parameter is a comma-separated list of people to send to. | |
20 | ** This routine arranges to send to all of them. | |
21 | ** | |
abae7b2d EA |
22 | ** The `ctladdr' is the address that expanded to be this one, |
23 | ** e.g., in an alias expansion. This is used for a number of | |
24 | ** purposed, most notably inheritance of uid/gid for protection | |
25 | ** purposes. It is also used to detect self-reference in group | |
26 | ** expansions and the like. | |
27 | ** | |
6eefe4f6 EA |
28 | ** Parameters: |
29 | ** list -- the send list. | |
1bf7c76b EA |
30 | ** ctladdr -- the address template for the person to |
31 | ** send to -- effective uid/gid are important. | |
d4f42161 EA |
32 | ** This is typically the alias that caused this |
33 | ** expansion. | |
34 | ** sendq -- a pointer to the head of a queue to put | |
35 | ** these people into. | |
118e6693 | 36 | ** e -- the envelope in which to add these recipients. |
abae7b2d | 37 | ** qflags -- special flags to set in the q_flags field. |
6eefe4f6 EA |
38 | ** |
39 | ** Returns: | |
abae7b2d | 40 | ** pointer to chain of addresses. |
6eefe4f6 EA |
41 | ** |
42 | ** Side Effects: | |
43 | ** none. | |
44 | */ | |
45 | ||
46 | # define MAXRCRSN 10 | |
47 | ||
abae7b2d EA |
48 | ADDRESS * |
49 | sendto(list, copyf, ctladdr, qflags) | |
6eefe4f6 | 50 | char *list; |
1bf7c76b | 51 | ADDRESS *ctladdr; |
4e5e456f | 52 | ADDRESS **sendq; |
a4076aed | 53 | register ENVELOPE *e; |
abae7b2d | 54 | u_short qflags; |
6eefe4f6 EA |
55 | { |
56 | register char *p; | |
7b955214 | 57 | register ADDRESS *al; /* list of addresses to send to */ |
92f12b98 | 58 | bool firstone; /* set on first address sent */ |
d3f52e20 | 59 | char delimiter; /* the address delimiter */ |
1c7897ef | 60 | int naddrs; |
d1147db7 | 61 | char *oldto = e->e_to; |
abae7b2d EA |
62 | ADDRESS *sibl; /* sibling pointer in tree */ |
63 | ADDRESS *prev; /* previous sibling */ | |
d6b27179 | 64 | |
9678c96d | 65 | if (tTd(25, 1)) |
331b7c9f EA |
66 | { |
67 | printf("sendto: %s\n ctladdr=", list); | |
68 | printaddr(ctladdr, FALSE); | |
69 | } | |
6eefe4f6 | 70 | |
7b955214 | 71 | /* heuristic to determine old versus new style addresses */ |
a2983993 | 72 | if (ctladdr == NULL && |
f3d8f6d6 EA |
73 | (strchr(list, ',') != NULL || strchr(list, ';') != NULL || |
74 | strchr(list, '<') != NULL || strchr(list, '(') != NULL)) | |
a4076aed | 75 | e->e_flags &= ~EF_OLDSTYLE; |
d3f52e20 | 76 | delimiter = ' '; |
a4076aed | 77 | if (!bitset(EF_OLDSTYLE, e->e_flags) || ctladdr != NULL) |
d3f52e20 | 78 | delimiter = ','; |
7b955214 | 79 | |
92f12b98 | 80 | firstone = TRUE; |
d6b27179 | 81 | al = NULL; |
1c7897ef | 82 | naddrs = 0; |
7b955214 | 83 | |
506fc377 | 84 | for (p = list; *p != '\0'; ) |
6eefe4f6 | 85 | { |
9e2cf26f | 86 | auto char *delimptr; |
506fc377 | 87 | register ADDRESS *a; |
6eefe4f6 EA |
88 | |
89 | /* parse the address */ | |
2bee003d | 90 | while ((isascii(*p) && isspace(*p)) || *p == ',') |
506fc377 | 91 | p++; |
9e2cf26f EA |
92 | a = parseaddr(p, (ADDRESS *) NULL, 1, delimiter, &delimptr, e); |
93 | p = delimptr; | |
b9fadd5b EA |
94 | if (a == NULL) |
95 | continue; | |
d6b27179 | 96 | a->q_next = al; |
1bf7c76b | 97 | a->q_alias = ctladdr; |
abae7b2d EA |
98 | if (ctladdr != NULL) |
99 | a->q_flags |= ctladdr->q_flags & ~QPRIMARY; | |
100 | a->q_flags |= qflags; | |
331b7c9f EA |
101 | |
102 | /* see if this should be marked as a primary address */ | |
92f12b98 | 103 | if (ctladdr == NULL || |
506fc377 | 104 | (firstone && *p == '\0' && bitset(QPRIMARY, ctladdr->q_flags))) |
92f12b98 | 105 | a->q_flags |= QPRIMARY; |
331b7c9f | 106 | |
7338e3d4 | 107 | if (ctladdr != NULL && sameaddr(ctladdr, a)) |
5d41b806 | 108 | ctladdr->q_flags |= QSELFREF; |
78bbbc48 | 109 | al = a; |
92f12b98 | 110 | firstone = FALSE; |
d6b27179 EA |
111 | } |
112 | ||
113 | /* arrange to send to everyone on the local send list */ | |
abae7b2d EA |
114 | prev = sibl = NULL; |
115 | if (ctladdr != NULL) | |
116 | prev = ctladdr->q_child; | |
d6b27179 EA |
117 | while (al != NULL) |
118 | { | |
119 | register ADDRESS *a = al; | |
abae7b2d | 120 | extern ADDRESS *recipient(); |
d6b27179 EA |
121 | |
122 | al = a->q_next; | |
abae7b2d EA |
123 | sibl = recipient(a); |
124 | if (sibl != NULL) | |
125 | { | |
126 | extern ADDRESS *addrref(); | |
127 | ||
128 | /* inherit full name */ | |
129 | if (sibl->q_fullname == NULL && ctladdr != NULL) | |
130 | sibl->q_fullname = ctladdr->q_fullname; | |
131 | ||
132 | /* link tree together (but only if the node is new) */ | |
133 | if (sibl == a) | |
134 | { | |
135 | sibl->q_sibling = prev; | |
136 | prev = sibl; | |
137 | } | |
138 | } | |
6eefe4f6 | 139 | } |
d6b27179 | 140 | |
d1147db7 | 141 | e->e_to = oldto; |
1c7897ef | 142 | return (naddrs); |
abae7b2d EA |
143 | if (ctladdr != NULL) |
144 | ctladdr->q_child = prev; | |
145 | return (prev); | |
146 | } | |
147 | \f/* | |
148 | ** ADDRREF -- return pointer to address that references another address. | |
149 | ** | |
150 | ** Parameters: | |
151 | ** a -- address to check. | |
152 | ** r -- reference to find. | |
153 | ** | |
154 | ** Returns: | |
155 | ** address of node in tree rooted at 'a' that references | |
156 | ** 'r'. | |
157 | ** NULL if no such node exists. | |
158 | ** | |
159 | ** Side Effects: | |
160 | ** none. | |
161 | */ | |
162 | ||
163 | ADDRESS * | |
164 | addrref(a, r) | |
165 | register ADDRESS *a; | |
166 | register ADDRESS *r; | |
167 | { | |
168 | register ADDRESS *q; | |
169 | ||
170 | while (a != NULL) | |
171 | { | |
172 | if (a->q_child == r || a->q_sibling == r) | |
173 | return (a); | |
174 | q = addrref(a->q_child, r); | |
175 | if (q != NULL) | |
176 | return (q); | |
177 | a = a->q_sibling; | |
178 | } | |
179 | return (NULL); | |
6eefe4f6 EA |
180 | } |
181 | \f/* | |
182 | ** RECIPIENT -- Designate a message recipient | |
183 | ** | |
184 | ** Saves the named person for future mailing. | |
185 | ** | |
186 | ** Parameters: | |
187 | ** a -- the (preparsed) address header for the recipient. | |
d4f42161 EA |
188 | ** sendq -- a pointer to the head of a queue to put the |
189 | ** recipient in. Duplicate supression is done | |
190 | ** in this queue. | |
78bbbc48 | 191 | ** e -- the current envelope. |
6eefe4f6 EA |
192 | ** |
193 | ** Returns: | |
abae7b2d | 194 | ** pointer to address actually inserted in send list. |
6eefe4f6 EA |
195 | ** |
196 | ** Side Effects: | |
197 | ** none. | |
198 | */ | |
199 | ||
d344c0b7 | 200 | ADDRESS * |
abae7b2d | 201 | ADDRESS * |
a4076aed | 202 | recipient(a, sendq, e) |
6eefe4f6 | 203 | register ADDRESS *a; |
d4f42161 | 204 | register ADDRESS **sendq; |
a4076aed | 205 | register ENVELOPE *e; |
6eefe4f6 EA |
206 | { |
207 | register ADDRESS *q; | |
74c5fe7c | 208 | ADDRESS **pq; |
6eefe4f6 | 209 | register struct mailer *m; |
98f46225 EA |
210 | register char *p; |
211 | bool quoted = FALSE; /* set if the addr has a quote bit */ | |
7f0fd60b | 212 | int findusercount = 0; |
98f46225 | 213 | char buf[MAXNAME]; /* unquoted image of the user name */ |
118e6693 | 214 | extern int safefile(); |
6eefe4f6 | 215 | |
a4076aed | 216 | e->e_to = a->q_paddr; |
179c1218 | 217 | m = a->q_mailer; |
6eefe4f6 | 218 | errno = 0; |
9678c96d | 219 | if (tTd(26, 1)) |
331b7c9f EA |
220 | { |
221 | printf("\nrecipient: "); | |
222 | printaddr(a, FALSE); | |
223 | } | |
6eefe4f6 EA |
224 | |
225 | /* break aliasing loops */ | |
226 | if (AliasLevel > MAXRCRSN) | |
227 | { | |
b6edea3d | 228 | usrerr("554 aliasing/forwarding loop broken"); |
abae7b2d | 229 | return (NULL); |
6eefe4f6 EA |
230 | } |
231 | ||
232 | /* | |
ed45aae1 | 233 | ** Finish setting up address structure. |
6eefe4f6 EA |
234 | */ |
235 | ||
0fe3917f | 236 | /* set the queue timeout */ |
c2bdb1dd | 237 | a->q_timeout = TimeOuts.to_q_return; |
ed45aae1 | 238 | |
0fe3917f | 239 | /* get unquoted user for file, program or user.name check */ |
98f46225 EA |
240 | (void) strcpy(buf, a->q_user); |
241 | for (p = buf; *p != '\0' && !quoted; p++) | |
242 | { | |
3eb4fac4 | 243 | if (*p == '\\') |
98f46225 EA |
244 | quoted = TRUE; |
245 | } | |
85c61679 | 246 | stripquotes(buf); |
98f46225 | 247 | |
98e5062b | 248 | /* check for direct mailing to restricted mailers */ |
c2bdb1dd EA |
249 | if (a->q_alias == NULL && m == ProgMailer && |
250 | !bitset(EF_QUEUERUN, e->e_flags)) | |
6eefe4f6 | 251 | { |
bc854e30 | 252 | a->q_flags |= QBADADDR; |
d1147db7 | 253 | usrerr("550 Cannot mail directly to programs"); |
6eefe4f6 EA |
254 | } |
255 | ||
256 | /* | |
b9ca6d11 EA |
257 | ** Look up this person in the recipient list. |
258 | ** If they are there already, return, otherwise continue. | |
259 | ** If the list is empty, just add it. Notice the cute | |
260 | ** hack to make from addresses suppress things correctly: | |
261 | ** the QDONTSEND bit will be set in the send list. | |
262 | ** [Please note: the emphasis is on "hack."] | |
6eefe4f6 EA |
263 | */ |
264 | ||
d4f42161 | 265 | for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next) |
6eefe4f6 | 266 | { |
35943e55 | 267 | if (sameaddr(q, a)) |
6eefe4f6 | 268 | { |
1d303d98 EA |
269 | /* if this is a reinsertion, just go ahead */ |
270 | if (bitset(QVERIFIED, q->q_flags)) | |
271 | break; | |
272 | ||
9678c96d | 273 | if (tTd(26, 1)) |
331b7c9f EA |
274 | { |
275 | printf("%s in sendq: ", a->q_paddr); | |
276 | printaddr(q, FALSE); | |
277 | } | |
92f12b98 | 278 | if (!bitset(QPRIMARY, q->q_flags)) |
f7eca811 EA |
279 | { |
280 | if (!bitset(QDONTSEND, a->q_flags)) | |
b6edea3d | 281 | message("duplicate suppressed"); |
92f12b98 | 282 | q->q_flags |= a->q_flags; |
f7eca811 | 283 | } |
abae7b2d EA |
284 | if (!bitset(QPSEUDO, a->q_flags)) |
285 | q->q_flags &= ~QPSEUDO; | |
286 | return (q); | |
6eefe4f6 | 287 | } |
6eefe4f6 | 288 | } |
74c5fe7c EA |
289 | |
290 | /* add address on list */ | |
1d303d98 EA |
291 | if (*pq != a) |
292 | { | |
293 | *pq = a; | |
294 | a->q_next = NULL; | |
295 | } | |
296 | ||
297 | a->q_flags &= ~QVERIFIED; | |
6eefe4f6 EA |
298 | |
299 | /* | |
98e5062b | 300 | ** Alias the name and handle special mailer types. |
6eefe4f6 EA |
301 | */ |
302 | ||
7f0fd60b | 303 | trylocaluser: |
42450b5a EA |
304 | if (tTd(29, 7)) |
305 | printf("at trylocaluser %s\n", a->q_user); | |
306 | ||
bc854e30 | 307 | if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags)) |
d1147db7 | 308 | goto testselfdestruct; |
98e5062b EA |
309 | |
310 | if (m == InclMailer) | |
6eefe4f6 | 311 | { |
98e5062b | 312 | a->q_flags |= QDONTSEND; |
c2bdb1dd | 313 | if (a->q_alias == NULL && !bitset(EF_QUEUERUN, e->e_flags)) |
6eefe4f6 | 314 | { |
bc854e30 | 315 | a->q_flags |= QBADADDR; |
b6edea3d | 316 | usrerr("550 Cannot mail directly to :include:s"); |
6eefe4f6 EA |
317 | } |
318 | else | |
8a12fae4 | 319 | { |
d50bcc17 | 320 | int ret; |
118e6693 | 321 | |
b6edea3d | 322 | message("including file %s", a->q_user); |
d50bcc17 EA |
323 | ret = include(a->q_user, FALSE, a, sendq, e); |
324 | if (transienterror(ret)) | |
325 | { | |
326 | #ifdef LOG | |
327 | if (LogLevel > 2) | |
73f6752a | 328 | syslog(LOG_ERR, "%s: include %s: transient error: %e", |
3f0792d1 | 329 | e->e_id, a->q_user, errstring(ret)); |
d50bcc17 | 330 | #endif |
c069e0df | 331 | a->q_flags |= QQUEUEUP; |
d50bcc17 EA |
332 | usrerr("451 Cannot open %s: %s", |
333 | a->q_user, errstring(ret)); | |
334 | } | |
335 | else if (ret != 0) | |
336 | { | |
32c83608 | 337 | a->q_flags |= QBADADDR; |
d50bcc17 EA |
338 | usrerr("550 Cannot open %s: %s", |
339 | a->q_user, errstring(ret)); | |
d50bcc17 | 340 | } |
8a12fae4 | 341 | } |
98e5062b | 342 | } |
6e99f903 | 343 | else if (m == FileMailer) |
6eefe4f6 | 344 | { |
a0554f81 EA |
345 | struct stat stb; |
346 | extern bool writable(); | |
6eefe4f6 | 347 | |
f3d8f6d6 | 348 | p = strrchr(buf, '/'); |
d13779b1 | 349 | /* check if writable or creatable */ |
c2bdb1dd | 350 | if (a->q_alias == NULL && !bitset(EF_QUEUERUN, e->e_flags)) |
6eefe4f6 | 351 | { |
bc854e30 | 352 | a->q_flags |= QBADADDR; |
b6edea3d | 353 | usrerr("550 Cannot mail directly to files"); |
d13779b1 EA |
354 | } |
355 | else if ((stat(buf, &stb) >= 0) ? (!writable(&stb)) : | |
fd57f063 | 356 | (*p = '\0', safefile(buf, RealUid, TRUE, S_IWRITE|S_IEXEC) != 0)) |
d13779b1 | 357 | { |
bc854e30 | 358 | a->q_flags |= QBADADDR; |
c0ae0b24 | 359 | giveresponse(EX_CANTCREAT, m, NULL, e); |
d13779b1 | 360 | } |
d13779b1 EA |
361 | } |
362 | ||
98e5062b | 363 | if (m != LocalMailer) |
6e99f903 EA |
364 | { |
365 | if (!bitset(QDONTSEND, a->q_flags)) | |
366 | e->e_nrcpts++; | |
d1147db7 | 367 | goto testselfdestruct; |
6e99f903 | 368 | } |
98e5062b EA |
369 | |
370 | /* try aliasing */ | |
371 | alias(a, sendq, e); | |
372 | ||
373 | # ifdef USERDB | |
374 | /* if not aliased, look it up in the user database */ | |
ebd4f543 | 375 | if (!bitset(QDONTSEND|QNOTREMOTE|QVERIFIED, a->q_flags)) |
98e5062b EA |
376 | { |
377 | extern int udbexpand(); | |
73f6752a | 378 | extern int errno; |
98e5062b EA |
379 | |
380 | if (udbexpand(a, sendq, e) == EX_TEMPFAIL) | |
381 | { | |
c069e0df | 382 | a->q_flags |= QQUEUEUP; |
98e5062b EA |
383 | if (e->e_message == NULL) |
384 | e->e_message = newstr("Deferred: user database error"); | |
385 | # ifdef LOG | |
68f7099c | 386 | if (LogLevel > 8) |
3f0792d1 EA |
387 | syslog(LOG_INFO, "%s: deferred: udbexpand: %s", |
388 | e->e_id, errstring(errno)); | |
98e5062b | 389 | # endif |
73f6752a EA |
390 | message("queued (user database error): %s", |
391 | errstring(errno)); | |
6e99f903 | 392 | e->e_nrcpts++; |
d1147db7 | 393 | goto testselfdestruct; |
98e5062b EA |
394 | } |
395 | } | |
396 | # endif | |
397 | ||
398 | /* if it was an alias or a UDB expansion, just return now */ | |
118e6693 | 399 | if (bitset(QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags)) |
d1147db7 | 400 | goto testselfdestruct; |
98e5062b | 401 | |
d13779b1 EA |
402 | /* |
403 | ** If we have a level two config file, then pass the name through | |
404 | ** Ruleset 5 before sending it off. Ruleset 5 has the right | |
405 | ** to send rewrite it to another mailer. This gives us a hook | |
406 | ** after local aliasing has been done. | |
407 | */ | |
408 | ||
409 | if (tTd(29, 5)) | |
410 | { | |
411 | printf("recipient: testing local? cl=%d, rr5=%x\n\t", | |
412 | ConfigLevel, RewriteRules[5]); | |
413 | printaddr(a, FALSE); | |
414 | } | |
415 | if (!bitset(QNOTREMOTE, a->q_flags) && ConfigLevel >= 2 && | |
416 | RewriteRules[5] != NULL) | |
417 | { | |
a4076aed | 418 | maplocaluser(a, sendq, e); |
d13779b1 EA |
419 | } |
420 | ||
421 | /* | |
422 | ** If it didn't get rewritten to another mailer, go ahead | |
423 | ** and deliver it. | |
424 | */ | |
425 | ||
118e6693 | 426 | if (!bitset(QDONTSEND|QQUEUEUP, a->q_flags)) |
d13779b1 | 427 | { |
42450b5a | 428 | auto bool fuzzy; |
d13779b1 EA |
429 | register struct passwd *pw; |
430 | extern struct passwd *finduser(); | |
431 | ||
432 | /* warning -- finduser may trash buf */ | |
42450b5a | 433 | pw = finduser(buf, &fuzzy); |
d13779b1 EA |
434 | if (pw == NULL) |
435 | { | |
bc854e30 | 436 | a->q_flags |= QBADADDR; |
c0ae0b24 | 437 | giveresponse(EX_NOUSER, m, NULL, e); |
6eefe4f6 EA |
438 | } |
439 | else | |
440 | { | |
d13779b1 | 441 | char nbuf[MAXNAME]; |
ff3e3c1c | 442 | |
42450b5a | 443 | if (fuzzy) |
6eefe4f6 | 444 | { |
7f0fd60b | 445 | /* name was a fuzzy match */ |
d13779b1 | 446 | a->q_user = newstr(pw->pw_name); |
7f0fd60b EA |
447 | if (findusercount++ > 3) |
448 | { | |
bc854e30 | 449 | a->q_flags |= QBADADDR; |
b6edea3d | 450 | usrerr("554 aliasing/forwarding loop for %s broken", |
7f0fd60b EA |
451 | pw->pw_name); |
452 | return (a); | |
453 | } | |
454 | ||
455 | /* see if it aliases */ | |
d13779b1 | 456 | (void) strcpy(buf, pw->pw_name); |
7f0fd60b | 457 | goto trylocaluser; |
6eefe4f6 | 458 | } |
d13779b1 EA |
459 | a->q_home = newstr(pw->pw_dir); |
460 | a->q_uid = pw->pw_uid; | |
461 | a->q_gid = pw->pw_gid; | |
61cbea4c | 462 | a->q_ruser = newstr(pw->pw_name); |
d13779b1 EA |
463 | a->q_flags |= QGOODUID; |
464 | buildfname(pw->pw_gecos, pw->pw_name, nbuf); | |
465 | if (nbuf[0] != '\0') | |
466 | a->q_fullname = newstr(nbuf); | |
467 | if (!quoted) | |
a4076aed | 468 | forward(a, sendq, e); |
6eefe4f6 EA |
469 | } |
470 | } | |
6e99f903 EA |
471 | if (!bitset(QDONTSEND, a->q_flags)) |
472 | e->e_nrcpts++; | |
d1147db7 EA |
473 | |
474 | testselfdestruct: | |
cd361139 | 475 | if (tTd(26, 8)) |
d1147db7 | 476 | { |
cd361139 EA |
477 | printf("testselfdestruct: "); |
478 | printaddr(a, TRUE); | |
479 | } | |
480 | if (a->q_alias == NULL && a != &e->e_from && | |
481 | bitset(QDONTSEND, a->q_flags)) | |
482 | { | |
483 | q = *sendq; | |
0fc3fb21 | 484 | while (q != NULL && bitset(QDONTSEND, q->q_flags)) |
d1147db7 | 485 | q = q->q_next; |
cd361139 | 486 | if (q == NULL) |
d1147db7 EA |
487 | { |
488 | a->q_flags |= QBADADDR; | |
489 | usrerr("554 aliasing/forwarding loop broken"); | |
490 | } | |
491 | } | |
d344c0b7 | 492 | return (a); |
abae7b2d EA |
493 | |
494 | return (a); | |
6eefe4f6 EA |
495 | } |
496 | \f/* | |
ff3e3c1c EA |
497 | ** FINDUSER -- find the password entry for a user. |
498 | ** | |
499 | ** This looks a lot like getpwnam, except that it may want to | |
500 | ** do some fancier pattern matching in /etc/passwd. | |
501 | ** | |
7338e3d4 EA |
502 | ** This routine contains most of the time of many sendmail runs. |
503 | ** It deserves to be optimized. | |
504 | ** | |
ff3e3c1c EA |
505 | ** Parameters: |
506 | ** name -- the name to match against. | |
42450b5a EA |
507 | ** fuzzyp -- an outarg that is set to TRUE if this entry |
508 | ** was found using the fuzzy matching algorithm; | |
509 | ** set to FALSE otherwise. | |
ff3e3c1c EA |
510 | ** |
511 | ** Returns: | |
512 | ** A pointer to a pw struct. | |
513 | ** NULL if name is unknown or ambiguous. | |
514 | ** | |
515 | ** Side Effects: | |
29c33ff6 | 516 | ** may modify name. |
ff3e3c1c EA |
517 | */ |
518 | ||
519 | struct passwd * | |
42450b5a | 520 | finduser(name, fuzzyp) |
ff3e3c1c | 521 | char *name; |
42450b5a | 522 | bool *fuzzyp; |
ff3e3c1c | 523 | { |
2b5e3c25 | 524 | register struct passwd *pw; |
29c33ff6 | 525 | register char *p; |
f74d8dce EA |
526 | extern struct passwd *getpwent(); |
527 | extern struct passwd *getpwnam(); | |
29c33ff6 | 528 | |
42450b5a EA |
529 | if (tTd(29, 4)) |
530 | printf("finduser(%s): ", name); | |
531 | ||
42450b5a | 532 | *fuzzyp = FALSE; |
ff3e3c1c | 533 | |
639d8b98 | 534 | /* look up this login name using fast path */ |
77b16ff1 | 535 | if ((pw = getpwnam(name)) != NULL) |
42450b5a EA |
536 | { |
537 | if (tTd(29, 4)) | |
538 | printf("found (non-fuzzy)\n"); | |
77b16ff1 | 539 | return (pw); |
42450b5a | 540 | } |
77b16ff1 | 541 | |
7f0fd60b EA |
542 | #ifdef MATCHGECOS |
543 | /* see if fuzzy matching allowed */ | |
544 | if (!MatchGecos) | |
42450b5a EA |
545 | { |
546 | if (tTd(29, 4)) | |
547 | printf("not found (fuzzy disabled)\n"); | |
7f0fd60b | 548 | return NULL; |
42450b5a | 549 | } |
7f0fd60b | 550 | |
77b16ff1 | 551 | /* search for a matching full name instead */ |
639d8b98 EA |
552 | for (p = name; *p != '\0'; p++) |
553 | { | |
554 | if (*p == (SpaceSub & 0177) || *p == '_') | |
555 | *p = ' '; | |
556 | } | |
03388044 | 557 | (void) setpwent(); |
2b5e3c25 EA |
558 | while ((pw = getpwent()) != NULL) |
559 | { | |
abae7b2d | 560 | char buf[MAXNAME]; |
2b5e3c25 | 561 | |
abae7b2d | 562 | fullname(pw, buf); |
f3d8f6d6 | 563 | if (strchr(buf, ' ') != NULL && !strcasecmp(buf, name)) |
36292825 | 564 | { |
42450b5a EA |
565 | if (tTd(29, 4)) |
566 | printf("fuzzy matches %s\n", pw->pw_name); | |
abae7b2d EA |
567 | message(Arpa_Info, "sending to %s <%s>", |
568 | buf, pw->pw_name); | |
2b5e3c25 | 569 | return (pw); |
36292825 | 570 | } |
2b5e3c25 | 571 | } |
42450b5a EA |
572 | if (tTd(29, 4)) |
573 | printf("no fuzzy match found\n"); | |
70ce33b6 EA |
574 | #else |
575 | if (tTd(29, 4)) | |
576 | printf("not found (fuzzy disabled)\n"); | |
577 | #endif | |
2b5e3c25 | 578 | return (NULL); |
ff3e3c1c EA |
579 | } |
580 | \f/* | |
a0554f81 EA |
581 | ** WRITABLE -- predicate returning if the file is writable. |
582 | ** | |
583 | ** This routine must duplicate the algorithm in sys/fio.c. | |
584 | ** Unfortunately, we cannot use the access call since we | |
585 | ** won't necessarily be the real uid when we try to | |
586 | ** actually open the file. | |
587 | ** | |
588 | ** Notice that ANY file with ANY execute bit is automatically | |
589 | ** not writable. This is also enforced by mailfile. | |
590 | ** | |
591 | ** Parameters: | |
592 | ** s -- pointer to a stat struct for the file. | |
593 | ** | |
594 | ** Returns: | |
595 | ** TRUE -- if we will be able to write this file. | |
596 | ** FALSE -- if we cannot write this file. | |
597 | ** | |
598 | ** Side Effects: | |
599 | ** none. | |
600 | */ | |
601 | ||
602 | bool | |
603 | writable(s) | |
604 | register struct stat *s; | |
605 | { | |
44967af8 EA |
606 | uid_t euid; |
607 | gid_t egid; | |
a0554f81 EA |
608 | int bits; |
609 | ||
610 | if (bitset(0111, s->st_mode)) | |
611 | return (FALSE); | |
fd57f063 EA |
612 | euid = RealUid; |
613 | egid = RealGid; | |
a0554f81 EA |
614 | if (geteuid() == 0) |
615 | { | |
616 | if (bitset(S_ISUID, s->st_mode)) | |
617 | euid = s->st_uid; | |
618 | if (bitset(S_ISGID, s->st_mode)) | |
619 | egid = s->st_gid; | |
620 | } | |
621 | ||
622 | if (euid == 0) | |
623 | return (TRUE); | |
624 | bits = S_IWRITE; | |
625 | if (euid != s->st_uid) | |
626 | { | |
627 | bits >>= 3; | |
628 | if (egid != s->st_gid) | |
629 | bits >>= 3; | |
630 | } | |
631 | return ((s->st_mode & bits) != 0); | |
632 | } | |
633 | \f/* | |
6eefe4f6 EA |
634 | ** INCLUDE -- handle :include: specification. |
635 | ** | |
636 | ** Parameters: | |
637 | ** fname -- filename to include. | |
a90a7c55 EA |
638 | ** forwarding -- if TRUE, we are reading a .forward file. |
639 | ** if FALSE, it's a :include: file. | |
1bf7c76b EA |
640 | ** ctladdr -- address template to use to fill in these |
641 | ** addresses -- effective user/group id are | |
642 | ** the important things. | |
d4f42161 EA |
643 | ** sendq -- a pointer to the head of the send queue |
644 | ** to put these addresses in. | |
6eefe4f6 EA |
645 | ** |
646 | ** Returns: | |
e0136832 | 647 | ** open error status |
6eefe4f6 EA |
648 | ** |
649 | ** Side Effects: | |
650 | ** reads the :include: file and sends to everyone | |
651 | ** listed in that file. | |
652 | */ | |
653 | ||
a90a7c55 | 654 | static jmp_buf CtxIncludeTimeout; |
e6cb9fc4 | 655 | static int includetimeout(); |
a90a7c55 | 656 | |
e0136832 | 657 | int |
a4076aed | 658 | include(fname, forwarding, ctladdr, sendq, e) |
6eefe4f6 | 659 | char *fname; |
a90a7c55 | 660 | bool forwarding; |
1bf7c76b | 661 | ADDRESS *ctladdr; |
d4f42161 | 662 | ADDRESS **sendq; |
a4076aed | 663 | ENVELOPE *e; |
6eefe4f6 | 664 | { |
6eefe4f6 | 665 | register FILE *fp; |
a4076aed | 666 | char *oldto = e->e_to; |
7338e3d4 EA |
667 | char *oldfilename = FileName; |
668 | int oldlinenumber = LineNumber; | |
a90a7c55 | 669 | register EVENT *ev = NULL; |
1c7897ef | 670 | int nincludes; |
118e6693 | 671 | int ret; |
5d32a96c EA |
672 | ADDRESS *ca; |
673 | uid_t uid; | |
a90a7c55 | 674 | char buf[MAXLINE]; |
a90a7c55 | 675 | |
b21ec539 EA |
676 | if (tTd(27, 2)) |
677 | printf("include(%s)\n", fname); | |
ba6514e2 EA |
678 | if (tTd(27, 4)) |
679 | printf(" ruid=%d euid=%d\n", getuid(), geteuid()); | |
5d32a96c EA |
680 | if (tTd(27, 14)) |
681 | { | |
682 | printf("ctladdr "); | |
683 | printaddr(ctladdr, FALSE); | |
684 | } | |
b21ec539 | 685 | |
a90a7c55 EA |
686 | /* |
687 | ** If home directory is remote mounted but server is down, | |
688 | ** this can hang or give errors; use a timeout to avoid this | |
689 | */ | |
690 | ||
5d32a96c EA |
691 | ca = getctladdr(ctladdr); |
692 | if (ca == NULL) | |
693 | uid = 0; | |
694 | else | |
695 | uid = ca->q_uid; | |
696 | ||
a90a7c55 EA |
697 | if (setjmp(CtxIncludeTimeout) != 0) |
698 | { | |
c069e0df | 699 | ctladdr->q_flags |= QQUEUEUP; |
a90a7c55 EA |
700 | errno = 0; |
701 | usrerr("451 open timeout on %s", fname); | |
92f2b65e EA |
702 | |
703 | /* return pseudo-error code */ | |
704 | return EOPENTIMEOUT; | |
a90a7c55 EA |
705 | } |
706 | ev = setevent((time_t) 60, includetimeout, 0); | |
707 | ||
5d32a96c | 708 | /* the input file must be marked safe */ |
8e5c6745 | 709 | if ((ret = safefile(fname, uid, forwarding, S_IREAD)) != 0) |
a90a7c55 EA |
710 | { |
711 | /* don't use this .forward file */ | |
712 | clrevent(ev); | |
b21ec539 | 713 | if (tTd(27, 4)) |
118e6693 | 714 | printf("include: not safe (uid=%d): %s\n", |
5d32a96c | 715 | uid, errstring(ret)); |
118e6693 | 716 | return ret; |
a90a7c55 | 717 | } |
6eefe4f6 | 718 | |
6eefe4f6 EA |
719 | fp = fopen(fname, "r"); |
720 | if (fp == NULL) | |
721 | { | |
e0136832 EA |
722 | int ret = errno; |
723 | ||
5d41b806 | 724 | clrevent(ev); |
ba6514e2 EA |
725 | if (tTd(27, 4)) |
726 | printf("include: open: %s\n", errstring(ret)); | |
e0136832 | 727 | return ret; |
6eefe4f6 | 728 | } |
a90a7c55 | 729 | |
5d32a96c | 730 | if (ca == NULL) |
962c3fb2 EA |
731 | { |
732 | struct stat st; | |
733 | ||
734 | if (fstat(fileno(fp), &st) < 0) | |
5d41b806 EA |
735 | { |
736 | int ret = errno; | |
737 | ||
738 | clrevent(ev); | |
962c3fb2 | 739 | syserr("Cannot fstat %s!", fname); |
5d41b806 EA |
740 | return ret; |
741 | } | |
962c3fb2 EA |
742 | ctladdr->q_uid = st.st_uid; |
743 | ctladdr->q_gid = st.st_gid; | |
744 | ctladdr->q_flags |= QGOODUID; | |
745 | } | |
6eefe4f6 | 746 | |
a90a7c55 EA |
747 | clrevent(ev); |
748 | ||
8f48def8 EA |
749 | if (bitset(EF_VRFYONLY, e->e_flags)) |
750 | { | |
751 | /* don't do any more now */ | |
5f6654cf | 752 | ctladdr->q_flags |= QVERIFIED; |
a7dc53bd | 753 | e->e_nrcpts++; |
bc854e30 | 754 | xfclose(fp, "include", fname); |
8f48def8 EA |
755 | return 0; |
756 | } | |
757 | ||
6eefe4f6 | 758 | /* read the file -- each line is a comma-separated list. */ |
7338e3d4 EA |
759 | FileName = fname; |
760 | LineNumber = 0; | |
1c7897ef EA |
761 | ctladdr->q_flags &= ~QSELFREF; |
762 | nincludes = 0; | |
6eefe4f6 EA |
763 | while (fgets(buf, sizeof buf, fp) != NULL) |
764 | { | |
f3d8f6d6 | 765 | register char *p = strchr(buf, '\n'); |
6eefe4f6 | 766 | |
dd0758e5 | 767 | LineNumber++; |
6eefe4f6 EA |
768 | if (p != NULL) |
769 | *p = '\0'; | |
b21ec539 | 770 | if (buf[0] == '#' || buf[0] == '\0') |
af04f6e4 | 771 | continue; |
51d9cc47 | 772 | e->e_to = NULL; |
b6edea3d | 773 | message("%s to %s", |
a90a7c55 | 774 | forwarding ? "forwarding" : "sending", buf); |
aa102c71 | 775 | #ifdef LOG |
68f7099c | 776 | if (forwarding && LogLevel > 9) |
aa102c71 EA |
777 | syslog(LOG_INFO, "%s: forward %s => %s", |
778 | e->e_id, oldto, buf); | |
779 | #endif | |
780 | ||
23008226 | 781 | AliasLevel++; |
abae7b2d | 782 | sendto(buf, 1, ctladdr, 0); |
23008226 | 783 | AliasLevel--; |
6eefe4f6 | 784 | } |
ba6514e2 EA |
785 | |
786 | if (ferror(fp) && tTd(27, 3)) | |
787 | printf("include: read error: %s\n", errstring(errno)); | |
1c7897ef | 788 | if (nincludes > 0 && !bitset(QSELFREF, ctladdr->q_flags)) |
f7eca811 EA |
789 | { |
790 | if (tTd(27, 5)) | |
791 | { | |
792 | printf("include: QDONTSEND "); | |
793 | printaddr(ctladdr, FALSE); | |
794 | } | |
795 | ctladdr->q_flags |= QDONTSEND; | |
796 | } | |
6eefe4f6 | 797 | |
bc854e30 | 798 | (void) xfclose(fp, "include", fname); |
7338e3d4 EA |
799 | FileName = oldfilename; |
800 | LineNumber = oldlinenumber; | |
d1147db7 | 801 | e->e_to = oldto; |
e0136832 | 802 | return 0; |
6eefe4f6 | 803 | } |
a90a7c55 EA |
804 | |
805 | static | |
806 | includetimeout() | |
807 | { | |
808 | longjmp(CtxIncludeTimeout, 1); | |
809 | } | |
d6b27179 EA |
810 | \f/* |
811 | ** SENDTOARGV -- send to an argument vector. | |
812 | ** | |
813 | ** Parameters: | |
814 | ** argv -- argument vector to send to. | |
118e6693 | 815 | ** e -- the current envelope. |
d6b27179 EA |
816 | ** |
817 | ** Returns: | |
818 | ** none. | |
819 | ** | |
820 | ** Side Effects: | |
821 | ** puts all addresses on the argument vector onto the | |
822 | ** send queue. | |
823 | */ | |
824 | ||
a4076aed | 825 | sendtoargv(argv, e) |
d6b27179 | 826 | register char **argv; |
a4076aed | 827 | register ENVELOPE *e; |
d6b27179 EA |
828 | { |
829 | register char *p; | |
d6b27179 EA |
830 | |
831 | while ((p = *argv++) != NULL) | |
832 | { | |
abae7b2d | 833 | sendto(p, 0, (ADDRESS *) NULL, 0); |
d6b27179 EA |
834 | } |
835 | } | |
1bf7c76b EA |
836 | \f/* |
837 | ** GETCTLADDR -- get controlling address from an address header. | |
838 | ** | |
839 | ** If none, get one corresponding to the effective userid. | |
840 | ** | |
841 | ** Parameters: | |
842 | ** a -- the address to find the controller of. | |
843 | ** | |
844 | ** Returns: | |
845 | ** the controlling address. | |
846 | ** | |
847 | ** Side Effects: | |
848 | ** none. | |
849 | */ | |
850 | ||
851 | ADDRESS * | |
852 | getctladdr(a) | |
853 | register ADDRESS *a; | |
854 | { | |
2c1457f0 | 855 | while (a != NULL && !bitset(QGOODUID, a->q_flags)) |
1bf7c76b | 856 | a = a->q_alias; |
1bf7c76b EA |
857 | return (a); |
858 | } |