File: | src/usr.bin/vacation/vacation.c |
Warning: | line 465, column 6 Call to function 'vfork' is insecure as it can lead to denial of service situations in the parent process. Replace calls to vfork with calls to the safer 'posix_spawn' function |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: vacation.c,v 1.38 2019/06/28 13:35:05 deraadt Exp $ */ |
2 | /* $NetBSD: vacation.c,v 1.7 1995/04/29 05:58:27 cgd Exp $ */ |
3 | |
4 | /* |
5 | * Copyright (c) 1983, 1987, 1993 |
6 | * The Regents of the University of California. All rights reserved. |
7 | * |
8 | * Redistribution and use in source and binary forms, with or without |
9 | * modification, are permitted provided that the following conditions |
10 | * are met: |
11 | * 1. Redistributions of source code must retain the above copyright |
12 | * notice, this list of conditions and the following disclaimer. |
13 | * 2. Redistributions in binary form must reproduce the above copyright |
14 | * notice, this list of conditions and the following disclaimer in the |
15 | * documentation and/or other materials provided with the distribution. |
16 | * 3. Neither the name of the University nor the names of its contributors |
17 | * may be used to endorse or promote products derived from this software |
18 | * without specific prior written permission. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
30 | * SUCH DAMAGE. |
31 | */ |
32 | |
33 | /* |
34 | ** Vacation |
35 | ** Copyright (c) 1983 Eric P. Allman |
36 | ** Berkeley, California |
37 | */ |
38 | |
39 | #include <sys/stat.h> |
40 | #include <fcntl.h> |
41 | #include <pwd.h> |
42 | #include <db.h> |
43 | #include <time.h> |
44 | #include <syslog.h> |
45 | #include <errno(*__errno()).h> |
46 | #include <unistd.h> |
47 | #include <stdio.h> |
48 | #include <ctype.h> |
49 | #include <stdlib.h> |
50 | #include <string.h> |
51 | #include <paths.h> |
52 | |
53 | /* |
54 | * VACATION -- return a message to the sender when on vacation. |
55 | * |
56 | * This program is invoked as a message receiver. It returns a |
57 | * message specified by the user to whomever sent the mail, taking |
58 | * care not to return a message too often to prevent "I am on |
59 | * vacation" loops. |
60 | */ |
61 | |
62 | #define MAXLINE1024 1024 /* max line from mail header */ |
63 | #define VDB".vacation.db" ".vacation.db" /* dbm's database */ |
64 | #define VMSG".vacation.msg" ".vacation.msg" /* vacation message */ |
65 | |
66 | typedef struct alias { |
67 | struct alias *next; |
68 | char *name; |
69 | } ALIAS; |
70 | ALIAS *names; |
71 | |
72 | DB *db; |
73 | char from[MAXLINE1024]; |
74 | char subj[MAXLINE1024]; |
75 | |
76 | int junkmail(void); |
77 | int nsearch(char *, char *); |
78 | void readheaders(void); |
79 | int recent(void); |
80 | void sendmessage(char *); |
81 | void setinterval(time_t); |
82 | void setreply(void); |
83 | void usage(void); |
84 | |
85 | #define SECSPERDAY(24 * 60 * 60) (24 * 60 * 60) |
86 | |
87 | int |
88 | main(int argc, char *argv[]) |
89 | { |
90 | int ch, iflag, flags; |
91 | struct passwd *pw; |
92 | time_t interval; |
93 | struct stat sb; |
94 | ALIAS *cur; |
95 | |
96 | opterr = iflag = 0; |
97 | interval = -1; |
98 | while ((ch = getopt(argc, argv, "a:Iir:")) != -1) |
99 | switch ((char)ch) { |
100 | case 'a': /* alias */ |
101 | if (!(cur = malloc(sizeof(ALIAS)))) |
102 | break; |
103 | cur->name = optarg; |
104 | cur->next = names; |
105 | names = cur; |
106 | break; |
107 | case 'I': /* backward compatible */ |
108 | case 'i': /* init the database */ |
109 | iflag = 1; |
110 | break; |
111 | case 'r': |
112 | if (isdigit((unsigned char)*optarg)) { |
113 | interval = atol(optarg) * SECSPERDAY(24 * 60 * 60); |
114 | if (interval < 0) |
115 | usage(); |
116 | } else |
117 | interval = 0; /* one time only */ |
118 | break; |
119 | default: |
120 | usage(); |
121 | } |
122 | argc -= optind; |
123 | argv += optind; |
124 | |
125 | if (argc != 1) { |
126 | if (!iflag) |
127 | usage(); |
128 | if (!(pw = getpwuid(getuid()))) { |
129 | syslog(LOG_ERR3, |
130 | "no such user uid %u.", getuid()); |
131 | exit(1); |
132 | } |
133 | } else if (!(pw = getpwnam(*argv))) { |
134 | syslog(LOG_ERR3, "no such user %s.", *argv); |
135 | exit(1); |
136 | } |
137 | if (chdir(pw->pw_dir)) { |
138 | syslog(LOG_NOTICE5, |
139 | "no such directory %s.", pw->pw_dir); |
140 | exit(1); |
141 | } |
142 | |
143 | /* |
144 | * dbopen(3) can not deal with a zero-length file w/o O_TRUNC. |
145 | */ |
146 | if (iflag == 1 || (stat(VDB".vacation.db", &sb) == 0 && sb.st_size == (off_t)0)) |
147 | flags = O_CREAT0x0200|O_RDWR0x0002|O_TRUNC0x0400; |
148 | else |
149 | flags = O_CREAT0x0200|O_RDWR0x0002; |
150 | |
151 | db = dbopen(VDB".vacation.db", flags, S_IRUSR0000400|S_IWUSR0000200, DB_HASH, NULL((void *)0)); |
152 | if (!db) { |
153 | syslog(LOG_NOTICE5, "%s: %m", VDB".vacation.db"); |
154 | exit(1); |
155 | } |
156 | |
157 | if (interval != -1) |
158 | setinterval(interval); |
159 | |
160 | if (iflag) { |
161 | (void)(db->close)(db); |
162 | exit(0); |
163 | } |
164 | |
165 | if (!(cur = malloc(sizeof(ALIAS)))) |
166 | exit(1); |
167 | cur->name = pw->pw_name; |
168 | cur->next = names; |
169 | names = cur; |
170 | |
171 | readheaders(); |
172 | if (!recent()) { |
173 | setreply(); |
174 | (void)(db->close)(db); |
175 | sendmessage(pw->pw_name); |
176 | } else |
177 | (void)(db->close)(db); |
178 | exit(0); |
179 | /* NOTREACHED */ |
180 | } |
181 | |
182 | /* |
183 | * readheaders -- |
184 | * read mail headers |
185 | */ |
186 | void |
187 | readheaders(void) |
188 | { |
189 | char buf[MAXLINE1024], *p; |
190 | int tome, cont; |
191 | ALIAS *cur; |
192 | |
193 | cont = tome = 0; |
194 | while (fgets(buf, sizeof(buf), stdin(&__sF[0])) && *buf != '\n') |
195 | switch (*buf) { |
196 | case 'A': /* "Auto-Submitted:" */ |
197 | case 'a': |
198 | cont = 0; |
199 | if (strncasecmp(buf, "Auto-Submitted:", 15)) |
200 | break; |
201 | for (p = buf + 15; isspace((unsigned char)*p); ++p) |
202 | ; |
203 | /* |
204 | * RFC 3834 section 2: |
205 | * Automatic responses SHOULD NOT be issued in response |
206 | * to any message which contains an Auto-Submitted |
207 | * header where the field has any value other than "no". |
208 | */ |
209 | if ((p[0] == 'n' || p[0] == 'N') && |
210 | (p[1] == 'o' || p[1] == 'O')) { |
211 | for (p += 2; isspace((unsigned char)*p); ++p) |
212 | ; |
213 | if (*p == '\0') |
214 | break; /* Auto-Submitted: no */ |
215 | } |
216 | exit(0); |
217 | case 'F': /* "From " */ |
218 | case 'f': |
219 | cont = 0; |
220 | if (!strncasecmp(buf, "From ", 5)) { |
221 | for (p = buf + 5; *p && *p != ' '; ++p) |
222 | ; |
223 | *p = '\0'; |
224 | (void)strlcpy(from, buf + 5, sizeof(from)); |
225 | from[strcspn(from, "\n")] = '\0'; |
226 | if (junkmail()) |
227 | exit(0); |
228 | } |
229 | break; |
230 | case 'L': /* "List-Id:" */ |
231 | case 'l': |
232 | cont = 0; |
233 | /* |
234 | * If present (with any value), message is coming from a |
235 | * mailing list, cf. RFC2919. |
236 | */ |
237 | if (strncasecmp(buf, "List-Id:", 8) == 0) |
238 | exit(0); |
239 | break; |
240 | case 'R': /* "Return-Path:" */ |
241 | case 'r': |
242 | cont = 0; |
243 | if (strncasecmp(buf, "Return-Path:", |
244 | sizeof("Return-Path:")-1) || |
245 | (buf[12] != ' ' && buf[12] != '\t')) |
246 | break; |
247 | for (p = buf + 12; isspace((unsigned char)*p); ++p) |
248 | ; |
249 | if (strlcpy(from, p, sizeof(from)) >= sizeof(from)) { |
250 | syslog(LOG_NOTICE5, |
251 | "Return-Path %s exceeds limits", p); |
252 | exit(1); |
253 | } |
254 | from[strcspn(from, "\n")] = '\0'; |
255 | if (junkmail()) |
256 | exit(0); |
257 | break; |
258 | case 'P': /* "Precedence:" */ |
259 | case 'p': |
260 | cont = 0; |
261 | if (strncasecmp(buf, "Precedence:", 11)) |
262 | break; |
263 | for (p = buf + 11; isspace((unsigned char)*p); ++p) |
264 | ; |
265 | if (!strncasecmp(p, "junk", 4) || |
266 | !strncasecmp(p, "bulk", 4) || |
267 | !strncasecmp(p, "list", 4)) |
268 | exit(0); |
269 | break; |
270 | case 'S': /* Subject: */ |
271 | case 's': |
272 | cont = 0; |
273 | if (strncasecmp(buf, "Subject:", |
274 | sizeof("Subject:")-1) || |
275 | (buf[8] != ' ' && buf[8] != '\t')) |
276 | break; |
277 | for (p = buf + 8; isspace((unsigned char)*p); ++p) |
278 | ; |
279 | if (strlcpy(subj, p, sizeof(subj)) >= sizeof(subj)) { |
280 | syslog(LOG_NOTICE5, |
281 | "Subject %s exceeds limits", p); |
282 | exit(1); |
283 | } |
284 | subj[strcspn(subj, "\n")] = '\0'; |
285 | break; |
286 | case 'C': /* "Cc:" */ |
287 | case 'c': |
288 | if (strncasecmp(buf, "Cc:", 3)) |
289 | break; |
290 | cont = 1; |
291 | goto findme; |
292 | case 'T': /* "To:" */ |
293 | case 't': |
294 | if (strncasecmp(buf, "To:", 3)) |
295 | break; |
296 | cont = 1; |
297 | goto findme; |
298 | default: |
299 | if (!isspace((unsigned char)*buf) || !cont || tome) { |
300 | cont = 0; |
301 | break; |
302 | } |
303 | findme: for (cur = names; !tome && cur; cur = cur->next) |
304 | tome += nsearch(cur->name, buf); |
305 | } |
306 | if (!tome) |
307 | exit(0); |
308 | if (!*from) { |
309 | syslog(LOG_NOTICE5, |
310 | "no initial \"From\" or \"Return-Path\"line."); |
311 | exit(1); |
312 | } |
313 | } |
314 | |
315 | /* |
316 | * nsearch -- |
317 | * do a nice, slow, search of a string for a substring. |
318 | */ |
319 | int |
320 | nsearch(char *name, char *str) |
321 | { |
322 | int len; |
323 | |
324 | for (len = strlen(name); *str; ++str) |
325 | if (!strncasecmp(name, str, len)) |
326 | return(1); |
327 | return(0); |
328 | } |
329 | |
330 | /* |
331 | * junkmail -- |
332 | * read the header and return if automagic/junk/bulk/list mail |
333 | */ |
334 | int |
335 | junkmail(void) |
336 | { |
337 | static struct ignore { |
338 | char *name; |
339 | int len; |
340 | } ignore[] = { |
341 | { "-request", 8 }, |
342 | { "postmaster", 10 }, |
343 | { "uucp", 4 }, |
344 | { "mailer-daemon", 13 }, |
345 | { "mailer", 6 }, |
346 | { "-relay", 6 }, |
347 | { NULL((void *)0), 0 } |
348 | }; |
349 | struct ignore *cur; |
350 | int len; |
351 | char *p; |
352 | |
353 | /* |
354 | * This is mildly amusing, and I'm not positive it's right; trying |
355 | * to find the "real" name of the sender, assuming that addresses |
356 | * will be some variant of: |
357 | * |
358 | * From site!site!SENDER%site.domain%site.domain@site.domain |
359 | */ |
360 | if (!(p = strchr(from, '%'))) { |
361 | if (!(p = strchr(from, '@'))) { |
362 | if ((p = strrchr(from, '!'))) |
363 | ++p; |
364 | else |
365 | p = from; |
366 | for (; *p; ++p) |
367 | ; |
368 | } |
369 | } |
370 | len = p - from; |
371 | for (cur = ignore; cur->name; ++cur) |
372 | if (len >= cur->len && |
373 | !strncasecmp(cur->name, p - cur->len, cur->len)) |
374 | return(1); |
375 | return(0); |
376 | } |
377 | |
378 | #define VIT"__VACATION__INTERVAL__TIMER__" "__VACATION__INTERVAL__TIMER__" |
379 | |
380 | /* |
381 | * recent -- |
382 | * find out if user has gotten a vacation message recently. |
383 | * use bcopy for machines with alignment restrictions |
384 | */ |
385 | int |
386 | recent(void) |
387 | { |
388 | time_t then, next; |
389 | DBT key, data; |
390 | |
391 | /* get interval time */ |
392 | key.data = VIT"__VACATION__INTERVAL__TIMER__"; |
393 | key.size = sizeof(VIT"__VACATION__INTERVAL__TIMER__"); |
394 | if ((db->get)(db, &key, &data, 0)) |
395 | next = SECSPERDAY(24 * 60 * 60) * 7; |
396 | else |
397 | bcopy(data.data, &next, sizeof(next)); |
398 | |
399 | /* get record for this address */ |
400 | key.data = from; |
401 | key.size = strlen(from); |
402 | if (!(db->get)(db, &key, &data, 0)) { |
403 | bcopy(data.data, &then, sizeof(then)); |
404 | if (next == 0 || |
405 | then + next > time(NULL((void *)0))) |
406 | return(1); |
407 | } |
408 | return(0); |
409 | } |
410 | |
411 | /* |
412 | * setinterval -- |
413 | * store the reply interval |
414 | */ |
415 | void |
416 | setinterval(time_t interval) |
417 | { |
418 | DBT key, data; |
419 | |
420 | key.data = VIT"__VACATION__INTERVAL__TIMER__"; |
421 | key.size = sizeof(VIT"__VACATION__INTERVAL__TIMER__"); |
422 | data.data = &interval; |
423 | data.size = sizeof(interval); |
424 | (void)(db->put)(db, &key, &data, 0); |
425 | } |
426 | |
427 | /* |
428 | * setreply -- |
429 | * store that this user knows about the vacation. |
430 | */ |
431 | void |
432 | setreply(void) |
433 | { |
434 | DBT key, data; |
435 | time_t now; |
436 | |
437 | key.data = from; |
438 | key.size = strlen(from); |
439 | (void)time(&now); |
440 | data.data = &now; |
441 | data.size = sizeof(now); |
442 | (void)(db->put)(db, &key, &data, 0); |
443 | } |
444 | |
445 | /* |
446 | * sendmessage -- |
447 | * exec sendmail to send the vacation file to sender |
448 | */ |
449 | void |
450 | sendmessage(char *myname) |
451 | { |
452 | char buf[MAXLINE1024]; |
453 | FILE *mfp, *sfp; |
454 | int pvect[2], i; |
455 | |
456 | mfp = fopen(VMSG".vacation.msg", "r"); |
457 | if (mfp == NULL((void *)0)) { |
458 | syslog(LOG_NOTICE5, "no ~%s/%s file.", myname, VMSG".vacation.msg"); |
459 | exit(1); |
460 | } |
461 | if (pipe(pvect) == -1) { |
462 | syslog(LOG_ERR3, "pipe: %m"); |
463 | exit(1); |
464 | } |
465 | i = vfork(); |
Call to function 'vfork' is insecure as it can lead to denial of service situations in the parent process. Replace calls to vfork with calls to the safer 'posix_spawn' function | |
466 | if (i == -1) { |
467 | syslog(LOG_ERR3, "fork: %m"); |
468 | exit(1); |
469 | } |
470 | if (i == 0) { |
471 | dup2(pvect[0], 0); |
472 | close(pvect[0]); |
473 | close(pvect[1]); |
474 | close(fileno(mfp)(!__isthreaded ? ((mfp)->_file) : (fileno)(mfp))); |
475 | execl(_PATH_SENDMAIL"/usr/sbin/sendmail", "sendmail", "-f", myname, "--", |
476 | from, (char *)NULL((void *)0)); |
477 | syslog(LOG_ERR3, "can't exec %s: %m", _PATH_SENDMAIL"/usr/sbin/sendmail"); |
478 | _exit(1); |
479 | } |
480 | close(pvect[0]); |
481 | sfp = fdopen(pvect[1], "w"); |
482 | if (sfp == NULL((void *)0)) { |
483 | /* XXX could not fdopen; likely out of memory */ |
484 | fclose(mfp); |
485 | close(pvect[1]); |
486 | return; |
487 | } |
488 | fprintf(sfp, "To: %s\n", from); |
489 | fputs("Auto-Submitted: auto-replied\n", sfp); |
490 | while (fgets(buf, sizeof buf, mfp)) { |
491 | char *s = strstr(buf, "$SUBJECT"); |
492 | |
493 | if (s) { |
494 | *s = 0; |
495 | fputs(buf, sfp); |
496 | fputs(subj, sfp); |
497 | fputs(s+8, sfp); |
498 | } else { |
499 | fputs(buf, sfp); |
500 | } |
501 | } |
502 | fclose(mfp); |
503 | fclose(sfp); |
504 | } |
505 | |
506 | void |
507 | usage(void) |
508 | { |
509 | syslog(LOG_NOTICE5, "uid %u: usage: vacation [-i] [-a alias] login", |
510 | getuid()); |
511 | exit(1); |
512 | } |