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