Bug Summary

File:src/usr.bin/vacation/vacation.c
Warning:line 471, column 3
This function call is prohibited after a successful vfork

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name vacation.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/vacation/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/vacation/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/vacation/vacation.c
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
66typedef struct alias {
67 struct alias *next;
68 char *name;
69} ALIAS;
70ALIAS *names;
71
72DB *db;
73char from[MAXLINE1024];
74char subj[MAXLINE1024];
75
76int junkmail(void);
77int nsearch(char *, char *);
78void readheaders(void);
79int recent(void);
80void sendmessage(char *);
81void setinterval(time_t);
82void setreply(void);
83void usage(void);
84
85#define SECSPERDAY(24 * 60 * 60) (24 * 60 * 60)
86
87int
88main(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)
1
Assuming the condition is false
2
Loop condition is false. Execution continues on line 122
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) {
3
Assuming 'argc' is equal to 1
4
Taking false branch
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))) {
5
Assuming 'pw' is non-null
6
Taking false branch
134 syslog(LOG_ERR3, "no such user %s.", *argv);
135 exit(1);
136 }
137 if (chdir(pw->pw_dir)) {
7
Assuming the condition is false
8
Taking false branch
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
8.1
'iflag' is not equal to 1
== 1 || (stat(VDB".vacation.db", &sb) == 0 && sb.st_size == (off_t)0))
9
Assuming the condition is false
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) {
10
Assuming 'db' is non-null
11
Taking false branch
153 syslog(LOG_NOTICE5, "%s: %m", VDB".vacation.db");
154 exit(1);
155 }
156
157 if (interval != -1)
12
Taking false branch
158 setinterval(interval);
159
160 if (iflag
12.1
'iflag' is 0
) {
13
Taking false branch
161 (void)(db->close)(db);
162 exit(0);
163 }
164
165 if (!(cur = malloc(sizeof(ALIAS))))
14
Assuming 'cur' is non-null
15
Taking false branch
166 exit(1);
167 cur->name = pw->pw_name;
168 cur->next = names;
169 names = cur;
170
171 readheaders();
172 if (!recent()) {
16
Taking true branch
173 setreply();
174 (void)(db->close)(db);
175 sendmessage(pw->pw_name);
17
Calling 'sendmessage'
176 } else
177 (void)(db->close)(db);
178 exit(0);
179 /* NOTREACHED */
180}
181
182/*
183 * readheaders --
184 * read mail headers
185 */
186void
187readheaders(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 }
303findme: 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 */
319int
320nsearch(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 */
334int
335junkmail(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 */
385int
386recent(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 */
415void
416setinterval(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 */
431void
432setreply(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 */
449void
450sendmessage(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)) {
18
Assuming 'mfp' is not equal to NULL
19
Taking false branch
458 syslog(LOG_NOTICE5, "no ~%s/%s file.", myname, VMSG".vacation.msg");
459 exit(1);
460 }
461 if (pipe(pvect) == -1) {
20
Assuming the condition is false
21
Taking false branch
462 syslog(LOG_ERR3, "pipe: %m");
463 exit(1);
464 }
465 i = vfork();
466 if (i == -1) {
22
Taking false branch
467 syslog(LOG_ERR3, "fork: %m");
468 exit(1);
469 }
470 if (i
22.1
'i' is equal to 0
== 0) {
23
Taking true branch
471 dup2(pvect[0], 0);
24
This function call is prohibited after a successful vfork
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
506void
507usage(void)
508{
509 syslog(LOG_NOTICE5, "uid %u: usage: vacation [-i] [-a alias] login",
510 getuid());
511 exit(1);
512}