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