Bug Summary

File:src/usr.bin/calendar/io.c
Warning:line 398, column 17
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

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 io.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/calendar/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/calendar/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/calendar/io.c
1/* $OpenBSD: io.c,v 1.51 2021/12/07 14:00:33 robert Exp $ */
2
3/*
4 * Copyright (c) 1989, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/stat.h>
33#include <sys/time.h>
34#include <sys/types.h>
35#include <sys/uio.h>
36#include <sys/wait.h>
37
38#include <ctype.h>
39#include <err.h>
40#include <errno(*__errno()).h>
41#include <fcntl.h>
42#include <locale.h>
43#include <pwd.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48#include <limits.h>
49
50#include "pathnames.h"
51#include "calendar.h"
52
53
54struct iovec header[] = {
55 { "From: ", 6 },
56 { NULL((void *)0), 0 },
57 { " (Reminder Service)\nTo: ", 24 },
58 { NULL((void *)0), 0 },
59 { "\nSubject: ", 10 },
60 { NULL((void *)0), 0 },
61 { "'s Calendar\nPrecedence: bulk\n", 29 },
62 { "Auto-Submitted: auto-generated\n\n", 32 },
63};
64
65
66void
67cal(void)
68{
69 int ch, l, i, bodun = 0, bodun_maybe = 0, var, printing;
70 struct event *events, *cur_evt, *ev1, *tmp;
71 char buf[2048 + 1], *prefix = NULL((void *)0), *p;
72 struct match *m;
73 FILE *fp;
74
75 events = NULL((void *)0);
76 cur_evt = NULL((void *)0);
77 if ((fp = opencal()) == NULL((void *)0))
78 return;
79 for (printing = 0; fgets(buf, sizeof(buf), stdin(&__sF[0])) != NULL((void *)0);) {
80 if ((p = strchr(buf, '\n')) != NULL((void *)0))
81 *p = '\0';
82 else
83 while ((ch = getchar()(!__isthreaded ? (--((&__sF[0]))->_r < 0 ? __srget(
(&__sF[0])) : (int)(*((&__sF[0]))->_p++)) : (getc)
((&__sF[0])))
) != '\n' && ch != EOF(-1));
84 for (l = strlen(buf); l > 0 && isspace(buf[l - 1]); l--)
85 ;
86 buf[l] = '\0';
87 if (buf[0] == '\0')
88 continue;
89 if (strncmp(buf, "LANG=", 5) == 0) {
90 (void) setlocale(LC_ALL0, buf + 5);
91 setnnames();
92 if (!strcmp(buf + 5, "ru_RU.UTF-8") ||
93 !strcmp(buf + 5, "uk_UA.UTF-8") ||
94 !strcmp(buf + 5, "by_BY.UTF-8")) {
95 bodun_maybe++;
96 bodun = 0;
97 free(prefix);
98 prefix = NULL((void *)0);
99 } else
100 bodun_maybe = 0;
101 continue;
102 } else if (strncmp(buf, "CALENDAR=", 9) == 0) {
103 char *ep;
104
105 if (buf[9] == '\0')
106 calendar = 0;
107 else if (!strcasecmp(buf + 9, "julian")) {
108 calendar = JULIAN;
109 errno(*__errno()) = 0;
110 julian = strtoul(buf + 14, &ep, 10);
111 if (buf[0] == '\0' || *ep != '\0')
112 julian = 13;
113 if ((errno(*__errno()) == ERANGE34 && julian == ULONG_MAX(9223372036854775807L *2UL+1UL)) ||
114 julian > 14)
115 errx(1, "Julian calendar offset is too large");
116 } else if (!strcasecmp(buf + 9, "gregorian"))
117 calendar = GREGORIAN;
118 else if (!strcasecmp(buf + 9, "lunar"))
119 calendar = LUNAR;
120 } else if (bodun_maybe && strncmp(buf, "BODUN=", 6) == 0) {
121 bodun++;
122 free(prefix);
123 if ((prefix = strdup(buf + 6)) == NULL((void *)0))
124 err(1, NULL((void *)0));
125 continue;
126 }
127 /* User defined names for special events */
128 if ((p = strchr(buf, '='))) {
129 for (i = 0; i < NUMEV3; i++) {
130 if (strncasecmp(buf, spev[i].name,
131 spev[i].nlen) == 0 &&
132 (p - buf == spev[i].nlen) &&
133 buf[spev[i].nlen + 1]) {
134 p++;
135 free(spev[i].uname);
136 if ((spev[i].uname = strdup(p)) == NULL((void *)0))
137 err(1, NULL((void *)0));
138 spev[i].ulen = strlen(p);
139 i = NUMEV3 + 1;
140 }
141 }
142 if (i > NUMEV3)
143 continue;
144 }
145 if (buf[0] != '\t') {
146 printing = (m = isnow(buf, bodun)) ? 1 : 0;
147 if ((p = strchr(buf, '\t')) == NULL((void *)0)) {
148 printing = 0;
149 continue;
150 }
151 /* Need the following to catch hardwired "variable"
152 * dates */
153 if (p > buf && p[-1] == '*')
154 var = 1;
155 else
156 var = 0;
157 if (printing) {
158 struct match *foo;
159
160 ev1 = NULL((void *)0);
161 while (m) {
162 cur_evt = malloc(sizeof(struct event));
163 if (cur_evt == NULL((void *)0))
164 err(1, NULL((void *)0));
165
166 cur_evt->when = m->when;
167 snprintf(cur_evt->print_date,
168 sizeof(cur_evt->print_date), "%s%c",
169 m->print_date, (var + m->var) ? '*' : ' ');
170 if (ev1) {
171 cur_evt->desc = ev1->desc;
172 cur_evt->ldesc = NULL((void *)0);
173 } else {
174 if (m->bodun && prefix) {
175 if (asprintf(&cur_evt->ldesc,
176 "\t%s %s", prefix,
177 p + 1) == -1)
178 err(1, NULL((void *)0));
179 } else if ((cur_evt->ldesc =
180 strdup(p)) == NULL((void *)0))
181 err(1, NULL((void *)0));
182 cur_evt->desc = &(cur_evt->ldesc);
183 ev1 = cur_evt;
184 }
185 insert(&events, cur_evt);
186 foo = m;
187 m = m->next;
188 free(foo);
189 }
190 }
191 } else if (printing) {
192 if (asprintf(&p, "%s\n%s", ev1->ldesc,
193 buf) == -1)
194 err(1, NULL((void *)0));
195 free(ev1->ldesc);
196 ev1->ldesc = p;
197 }
198 }
199 tmp = events;
200 while (tmp) {
201 (void)fprintf(fp, "%s%s\n", tmp->print_date, *(tmp->desc));
202 tmp = tmp->next;
203 }
204 tmp = events;
205 while (tmp) {
206 events = tmp;
207 free(tmp->ldesc);
208 tmp = tmp->next;
209 free(events);
210 }
211 closecal(fp);
212}
213
214int
215getfield(char *p, char **endp, int *flags)
216{
217 int val, var, i;
218 char *start, savech;
219
220 for (; !isdigit((unsigned char)*p) && !isalpha((unsigned char)*p) &&
221 *p != '*' && *p != '\t'; ++p)
222 ;
223 if (*p == '*') { /* `*' is every month */
224 *flags |= F_ISMONTH0x01;
225 *endp = p+1;
226 return (-1); /* means 'every month' */
227 }
228 if (isdigit((unsigned char)*p)) {
229 val = strtol(p, &p, 10); /* if 0, it's failure */
230 for (; !isdigit((unsigned char)*p) &&
231 !isalpha((unsigned char)*p) && *p != '*'; ++p)
232 ;
233 *endp = p;
234 return (val);
235 }
236 for (start = p; isalpha((unsigned char)*++p);)
237 ;
238
239 /* Sunday-1 */
240 if (*p == '+' || *p == '-')
241 for(; isdigit((unsigned char)*++p); )
242 ;
243
244 savech = *p;
245 *p = '\0';
246
247 /* Month */
248 if ((val = getmonth(start)) != 0)
249 *flags |= F_ISMONTH0x01;
250
251 /* Day */
252 else if ((val = getday(start)) != 0) {
253 *flags |= F_ISDAY0x02;
254
255 /* variable weekday */
256 if ((var = getdayvar(start)) != 0) {
257 if (var <= 5 && var >= -4)
258 val += var * 10;
259#ifdef DEBUG
260 printf("var: %d\n", var);
261#endif
262 }
263 }
264
265 /* Try specials (Easter, Paskha, ...) */
266 else {
267 for (i = 0; i < NUMEV3; i++) {
268 if (strncasecmp(start, spev[i].name, spev[i].nlen) == 0) {
269 start += spev[i].nlen;
270 val = i + 1;
271 i = NUMEV3 + 1;
272 } else if (spev[i].uname != NULL((void *)0) &&
273 strncasecmp(start, spev[i].uname, spev[i].ulen) == 0) {
274 start += spev[i].ulen;
275 val = i + 1;
276 i = NUMEV3 + 1;
277 }
278 }
279 if (i > NUMEV3) {
280 const char *errstr;
281
282 switch (*start) {
283 case '-':
284 case '+':
285 var = strtonum(start + 1, 0, 365, &errstr);
286 if (errstr)
287 return (0); /* Someone is just being silly */
288 if (*start == '-')
289 var = -var;
290 val += (NUMEV3 + 1) * var;
291 /* We add one to the matching event and multiply by
292 * (NUMEV + 1) so as not to return 0 if there's a match.
293 * val will overflow if there is an obscenely large
294 * number of special events. */
295 break;
296 }
297 *flags |= F_SPECIAL0x08;
298 }
299 if (!(*flags & F_SPECIAL0x08)) {
300 /* undefined rest */
301 *p = savech;
302 return (0);
303 }
304 }
305 for (*p = savech; !isdigit((unsigned char)*p) &&
306 !isalpha((unsigned char)*p) && *p != '*' && *p != '\t'; ++p)
307 ;
308 *endp = p;
309 return (val);
310}
311
312
313FILE *
314opencal(void)
315{
316 int pdes[2], fdin;
317 struct stat st;
318
319 /* open up calendar file as stdin */
320 if ((fdin = open(calendarFile, O_RDONLY0x0000)) == -1 ||
321 fstat(fdin, &st) == -1 || !S_ISREG(st.st_mode)((st.st_mode & 0170000) == 0100000)) {
322 if (!doall) {
323 char *home = getenv("HOME");
324 if (home == NULL((void *)0) || *home == '\0')
325 errx(1, "cannot get home directory");
326 if (!(chdir(home) == 0 &&
327 chdir(calendarHome) == 0 &&
328 (fdin = open(calendarFile, O_RDONLY0x0000)) != -1))
329 errx(1, "no calendar file: \"%s\" or \"~/%s/%s\"",
330 calendarFile, calendarHome, calendarFile);
331 }
332 }
333
334 if (pipe(pdes) == -1) {
335 close(fdin);
336 return (NULL((void *)0));
337 }
338 switch (vfork()) {
339 case -1: /* error */
340 (void)close(pdes[0]);
341 (void)close(pdes[1]);
342 close(fdin);
343 return (NULL((void *)0));
344 case 0:
345 dup2(fdin, STDIN_FILENO0);
346 /* child -- set stdout to pipe input */
347 if (pdes[1] != STDOUT_FILENO1) {
348 (void)dup2(pdes[1], STDOUT_FILENO1);
349 (void)close(pdes[1]);
350 }
351 (void)close(pdes[0]);
352 /*
353 * Set stderr to /dev/null. Necessary so that cron does not
354 * wait for cpp to finish if it's running calendar -a.
355 */
356 if (doall) {
357 int fderr;
358 fderr = open(_PATH_DEVNULL"/dev/null", O_WRONLY0x0001);
359 if (fderr == -1)
360 _exit(0);
361 (void)dup2(fderr, STDERR_FILENO2);
362 (void)close(fderr);
363 }
364 execl(_PATH_CPP"/usr/libexec/cpp", "cpp", "-traditional", "-undef", "-U__GNUC__",
365 "-P", "-I.", "-w", _PATH_INCLUDE"-I/usr/share/calendar", (char *)NULL((void *)0));
366 warn(_PATH_CPP"/usr/libexec/cpp");
367 _exit(1);
368 }
369 /* parent -- set stdin to pipe output */
370 (void)dup2(pdes[0], STDIN_FILENO0);
371 (void)close(pdes[0]);
372 (void)close(pdes[1]);
373
374 /* not reading all calendar files, just set output to stdout */
375 if (!doall)
376 return (stdout(&__sF[1]));
377
378 /* set output to a temporary file, so if no output don't send mail */
379 return(tmpfile());
380}
381
382void
383closecal(FILE *fp)
384{
385 struct stat sbuf;
386 int nread, pdes[2], status;
387 char buf[1024];
388 pid_t pid = -1;
389
390 if (!doall)
391 return;
392
393 (void)rewind(fp);
394 if (fstat(fileno(fp)(!__isthreaded ? ((fp)->_file) : (fileno)(fp)), &sbuf) || !sbuf.st_size)
395 goto done;
396 if (pipe(pdes) == -1)
397 goto done;
398 switch ((pid = 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
399 case -1: /* error */
400 (void)close(pdes[0]);
401 (void)close(pdes[1]);
402 goto done;
403 case 0:
404 /* child -- set stdin to pipe output */
405 if (pdes[0] != STDIN_FILENO0) {
406 (void)dup2(pdes[0], STDIN_FILENO0);
407 (void)close(pdes[0]);
408 }
409 (void)close(pdes[1]);
410 execl(_PATH_SENDMAIL"/usr/sbin/sendmail", "sendmail", "-i", "-t", "-F",
411 "\"Reminder Service\"", (char *)NULL((void *)0));
412 warn(_PATH_SENDMAIL"/usr/sbin/sendmail");
413 _exit(1);
414 }
415 /* parent -- write to pipe input */
416 (void)close(pdes[0]);
417
418 header[1].iov_base = header[3].iov_base = pw->pw_name;
419 header[1].iov_len = header[3].iov_len = strlen(pw->pw_name);
420 writev(pdes[1], header, 8);
421 while ((nread = read(fileno(fp)(!__isthreaded ? ((fp)->_file) : (fileno)(fp)), buf, sizeof(buf))) > 0)
422 (void)write(pdes[1], buf, nread);
423 (void)close(pdes[1]);
424done: (void)fclose(fp);
425 if (pid != -1) {
426 while (waitpid(pid, &status, 0) == -1) {
427 if (errno(*__errno()) != EINTR4)
428 break;
429 }
430 }
431}
432
433
434void
435insert(struct event **head, struct event *cur_evt)
436{
437 struct event *tmp, *tmp2;
438
439 if (*head) {
440 /* Insert this one in order */
441 tmp = *head;
442 tmp2 = NULL((void *)0);
443 while (tmp->next &&
444 tmp->when <= cur_evt->when) {
445 tmp2 = tmp;
446 tmp = tmp->next;
447 }
448 if (tmp->when > cur_evt->when) {
449 cur_evt->next = tmp;
450 if (tmp2)
451 tmp2->next = cur_evt;
452 else
453 *head = cur_evt;
454 } else {
455 cur_evt->next = tmp->next;
456 tmp->next = cur_evt;
457 }
458 } else {
459 *head = cur_evt;
460 cur_evt->next = NULL((void *)0);
461 }
462}