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