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 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
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.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 | |
55 | |
56 | |
57 | |
58 | |
59 | |
60 | |
61 | |
62 | #define MAXLINE 1024 /* max line from mail header */ |
63 | #define VDB ".vacation.db" /* dbm's database */ |
64 | #define VMSG ".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[MAXLINE]; |
74 | char subj[MAXLINE]; |
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) |
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) |
| 1 | Assuming the condition is false | |
|
| 2 | | Loop condition is false. Execution continues on line 122 | |
|
99 | switch ((char)ch) { |
100 | case 'a': |
101 | if (!(cur = malloc(sizeof(ALIAS)))) |
102 | break; |
103 | cur->name = optarg; |
104 | cur->next = names; |
105 | names = cur; |
106 | break; |
107 | case 'I': |
108 | case 'i': |
109 | iflag = 1; |
110 | break; |
111 | case 'r': |
112 | if (isdigit((unsigned char)*optarg)) { |
113 | interval = atol(optarg) * SECSPERDAY; |
114 | if (interval < 0) |
115 | usage(); |
116 | } else |
117 | interval = 0; |
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 | |
|
| |
126 | if (!iflag) |
127 | usage(); |
128 | if (!(pw = getpwuid(getuid()))) { |
129 | syslog(LOG_ERR, |
130 | "no such user uid %u.", getuid()); |
131 | exit(1); |
132 | } |
133 | } else if (!(pw = getpwnam(*argv))) { |
| 5 | | Assuming 'pw' is non-null | |
|
| |
134 | syslog(LOG_ERR, "no such user %s.", *argv); |
135 | exit(1); |
136 | } |
137 | if (chdir(pw->pw_dir)) { |
| 7 | | Assuming the condition is false | |
|
| |
138 | syslog(LOG_NOTICE, |
139 | "no such directory %s.", pw->pw_dir); |
140 | exit(1); |
141 | } |
142 | |
143 | |
144 | |
145 | |
146 | if (iflag == 1 || (stat(VDB, &sb) == 0 && sb.st_size == (off_t)0)) |
| 9 | | Assuming the condition is false | |
|
147 | flags = O_CREAT|O_RDWR|O_TRUNC; |
148 | else |
149 | flags = O_CREAT|O_RDWR; |
150 | |
151 | db = dbopen(VDB, flags, S_IRUSR|S_IWUSR, DB_HASH, NULL); |
152 | if (!db) { |
| 10 | | Assuming 'db' is non-null | |
|
| |
153 | syslog(LOG_NOTICE, "%s: %m", VDB); |
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)))) |
| 14 | | Assuming 'cur' is non-null | |
|
| |
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 | |
180 | } |
181 | |
182 | |
183 | |
184 | |
185 | |
186 | void |
187 | readheaders(void) |
188 | { |
189 | char buf[MAXLINE], *p; |
190 | int tome, cont; |
191 | ALIAS *cur; |
192 | |
193 | cont = tome = 0; |
194 | while (fgets(buf, sizeof(buf), stdin) && *buf != '\n') |
195 | switch (*buf) { |
196 | case 'A': |
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 | |
205 | |
206 | |
207 | |
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; |
215 | } |
216 | exit(0); |
217 | case 'F': |
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': |
231 | case 'l': |
232 | cont = 0; |
233 | |
234 | |
235 | |
236 | |
237 | if (strncasecmp(buf, "List-Id:", 8) == 0) |
238 | exit(0); |
239 | break; |
240 | case 'R': |
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_NOTICE, |
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': |
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': |
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_NOTICE, |
281 | "Subject %s exceeds limits", p); |
282 | exit(1); |
283 | } |
284 | subj[strcspn(subj, "\n")] = '\0'; |
285 | break; |
286 | case 'C': |
287 | case 'c': |
288 | if (strncasecmp(buf, "Cc:", 3)) |
289 | break; |
290 | cont = 1; |
291 | goto findme; |
292 | case 'T': |
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_NOTICE, |
310 | "no initial \"From\" or \"Return-Path\"line."); |
311 | exit(1); |
312 | } |
313 | } |
314 | |
315 | |
316 | |
317 | |
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 | |
332 | |
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, 0 } |
348 | }; |
349 | struct ignore *cur; |
350 | int len; |
351 | char *p; |
352 | |
353 | |
354 | |
355 | |
356 | |
357 | |
358 | |
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__" |
379 | |
380 | |
381 | |
382 | |
383 | |
384 | |
385 | int |
386 | recent(void) |
387 | { |
388 | time_t then, next; |
389 | DBT key, data; |
390 | |
391 | |
392 | key.data = VIT; |
393 | key.size = sizeof(VIT); |
394 | if ((db->get)(db, &key, &data, 0)) |
395 | next = SECSPERDAY * 7; |
396 | else |
397 | bcopy(data.data, &next, sizeof(next)); |
398 | |
399 | |
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)) |
406 | return(1); |
407 | } |
408 | return(0); |
409 | } |
410 | |
411 | |
412 | |
413 | |
414 | |
415 | void |
416 | setinterval(time_t interval) |
417 | { |
418 | DBT key, data; |
419 | |
420 | key.data = VIT; |
421 | key.size = sizeof(VIT); |
422 | data.data = &interval; |
423 | data.size = sizeof(interval); |
424 | (void)(db->put)(db, &key, &data, 0); |
425 | } |
426 | |
427 | |
428 | |
429 | |
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 | |
447 | |
448 | |
449 | void |
450 | sendmessage(char *myname) |
451 | { |
452 | char buf[MAXLINE]; |
453 | FILE *mfp, *sfp; |
454 | int pvect[2], i; |
455 | |
456 | mfp = fopen(VMSG, "r"); |
457 | if (mfp == NULL) { |
| 18 | | Assuming 'mfp' is not equal to NULL | |
|
| |
458 | syslog(LOG_NOTICE, "no ~%s/%s file.", myname, VMSG); |
459 | exit(1); |
460 | } |
461 | if (pipe(pvect) == -1) { |
| 20 | | Assuming the condition is false | |
|
| |
462 | syslog(LOG_ERR, "pipe: %m"); |
463 | exit(1); |
464 | } |
465 | i = vfork(); |
466 | if (i == -1) { |
| |
467 | syslog(LOG_ERR, "fork: %m"); |
468 | exit(1); |
469 | } |
470 | if (i == 0) { |
| |
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)); |
475 | execl(_PATH_SENDMAIL, "sendmail", "-f", myname, "--", |
476 | from, (char *)NULL); |
477 | syslog(LOG_ERR, "can't exec %s: %m", _PATH_SENDMAIL); |
478 | _exit(1); |
479 | } |
480 | close(pvect[0]); |
481 | sfp = fdopen(pvect[1], "w"); |
482 | if (sfp == NULL) { |
483 | |
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_NOTICE, "uid %u: usage: vacation [-i] [-a alias] login", |
510 | getuid()); |
511 | exit(1); |
512 | } |