Bug Summary

File:src/libexec/mail.local/mail.local.c
Warning:line 85, column 2
Value stored to 'argc' is never read

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 mail.local.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/libexec/mail.local/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/libexec/mail.local/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/libexec/mail.local/mail.local.c
1/* $OpenBSD: mail.local.c,v 1.39 2020/02/09 14:59:20 millert Exp $ */
2
3/*-
4 * Copyright (c) 1996-1998 Theo de Raadt <deraadt@theos.com>
5 * Copyright (c) 1996-1998 David Mazieres <dm@lcs.mit.edu>
6 * Copyright (c) 1990 The Regents of the University of California.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <sys/socket.h>
37#include <sys/wait.h>
38#include <netinet/in.h>
39#include <sysexits.h>
40#include <syslog.h>
41#include <fcntl.h>
42#include <netdb.h>
43#include <pwd.h>
44#include <time.h>
45#include <unistd.h>
46#include <limits.h>
47#include <errno(*__errno()).h>
48#include <stdio.h>
49#include <stdlib.h>
50#include <string.h>
51#include <signal.h>
52#include "pathnames.h"
53#include "mail.local.h"
54
55int
56main(int argc, char *argv[])
57{
58 struct passwd *pw;
59 int ch, fd, eval, lockfile=1;
60 uid_t uid;
61 char *from;
62
63 openlog("mail.local", LOG_PERROR0x20, LOG_MAIL(2<<3));
64
65 from = NULL((void *)0);
66 while ((ch = getopt(argc, argv, "lLdf:r:")) != -1)
67 switch (ch) {
68 case 'd': /* backward compatible */
69 break;
70 case 'f':
71 case 'r': /* backward compatible */
72 if (from)
73 merr(EX_USAGE64, "multiple -f options");
74 from = optarg;
75 break;
76 case 'l':
77 lockfile=1;
78 break;
79 case 'L':
80 lockfile=0;
81 break;
82 default:
83 usage();
84 }
85 argc -= optind;
Value stored to 'argc' is never read
86 argv += optind;
87
88 if (!*argv)
89 usage();
90
91 /*
92 * If from not specified, use the name from getlogin() if the
93 * uid matches, otherwise, use the name from the password file
94 * corresponding to the uid.
95 */
96 uid = getuid();
97 if (!from && (!(from = getlogin()) ||
98 !(pw = getpwnam(from)) || pw->pw_uid != uid))
99 from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
100
101 fd = storemail(from);
102 for (eval = 0; *argv; ++argv) {
103 if ((ch = deliver(fd, *argv, lockfile)) != 0)
104 eval = ch;
105 }
106 exit(eval);
107}
108
109int
110storemail(char *from)
111{
112 FILE *fp = NULL((void *)0);
113 time_t tval;
114 int fd, eline;
115 size_t len;
116 char *line, *tbuf;
117
118 if ((tbuf = strdup(_PATH_LOCTMP"/tmp/local.XXXXXXXXXX")) == NULL((void *)0))
119 merr(EX_OSERR71, "unable to allocate memory");
120 if ((fd = mkstemp(tbuf)) == -1 || !(fp = fdopen(fd, "w+")))
121 merr(EX_OSERR71, "unable to open temporary file");
122 (void)unlink(tbuf);
123 free(tbuf);
124
125 (void)time(&tval);
126 (void)fprintf(fp, "From %s %s", from, ctime(&tval));
127
128 for (eline = 1, tbuf = NULL((void *)0); (line = fgetln(stdin(&__sF[0]), &len));) {
129 /* We have to NUL-terminate the line since fgetln does not */
130 if (line[len - 1] == '\n')
131 line[len - 1] = '\0';
132 else {
133 /* No trailing newline, so alloc space and copy */
134 if ((tbuf = malloc(len + 1)) == NULL((void *)0))
135 merr(EX_OSERR71, "unable to allocate memory");
136 memcpy(tbuf, line, len);
137 tbuf[len] = '\0';
138 line = tbuf;
139 }
140 if (line[0] == '\0')
141 eline = 1;
142 else {
143 if (eline && line[0] == 'F' && len > 5 &&
144 !memcmp(line, "From ", 5))
145 (void)putc('>', fp)(!__isthreaded ? __sputc('>', fp) : (putc)('>', fp));
146 eline = 0;
147 }
148 (void)fprintf(fp, "%s\n", line);
149 if (ferror(fp)(!__isthreaded ? (((fp)->_flags & 0x0040) != 0) : (ferror
)(fp))
)
150 break;
151 }
152 free(tbuf);
153
154 /* Output a newline; note, empty messages are allowed. */
155 (void)putc('\n', fp)(!__isthreaded ? __sputc('\n', fp) : (putc)('\n', fp));
156 (void)fflush(fp);
157 if (ferror(fp)(!__isthreaded ? (((fp)->_flags & 0x0040) != 0) : (ferror
)(fp))
)
158 merr(EX_OSERR71, "temporary file write error");
159 return(fd);
160}
161
162int
163deliver(int fd, char *name, int lockfile)
164{
165 struct stat sb, fsb;
166 struct passwd *pw;
167 int mbfd=-1, lfd=-1, rval=EX_OSERR71;
168 char biffmsg[100], buf[8*1024], path[PATH_MAX1024];
169 off_t curoff;
170 size_t off;
171 ssize_t nr, nw;
172
173 /*
174 * Disallow delivery to unknown names -- special mailboxes can be
175 * handled in the sendmail aliases file.
176 */
177 if (!(pw = getpwnam(name))) {
178 mwarn("unknown name: %s", name);
179 return(EX_NOUSER67);
180 }
181
182 (void)snprintf(path, sizeof path, "%s/%s", _PATH_MAILDIR"/var/mail", name);
183
184 if (lockfile) {
185 lfd = lockspool(name, pw);
186 if (lfd == -1)
187 return(EX_OSERR71);
188 }
189
190 /* after this point, always exit via bad to remove lockfile */
191retry:
192 if (lstat(path, &sb)) {
193 if (errno(*__errno()) != ENOENT2) {
194 mwarn("%s: %s", path, strerror(errno(*__errno())));
195 goto bad;
196 }
197 if ((mbfd = open(path, O_APPEND0x0008|O_CREAT0x0200|O_EXCL0x0800|O_WRONLY0x0001|O_EXLOCK0x0020,
198 S_IRUSR0000400|S_IWUSR0000200)) == -1) {
199 if (errno(*__errno()) == EEXIST17) {
200 /* file appeared since lstat */
201 goto retry;
202 } else {
203 mwarn("%s: %s", path, strerror(errno(*__errno())));
204 rval = EX_CANTCREAT73;
205 goto bad;
206 }
207 }
208 /*
209 * Set the owner and group. Historically, binmail repeated
210 * this at each mail delivery. We no longer do this, assuming
211 * that if the ownership or permissions were changed there
212 * was a reason for doing so.
213 */
214 if (fchown(mbfd, pw->pw_uid, pw->pw_gid) == -1) {
215 mwarn("chown %u:%u: %s", pw->pw_uid, pw->pw_gid, name);
216 goto bad;
217 }
218 } else {
219 if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)((sb.st_mode & 0170000) == 0100000)) {
220 mwarn("%s: linked or special file", path);
221 goto bad;
222 }
223 if ((mbfd = open(path, O_APPEND0x0008|O_WRONLY0x0001|O_EXLOCK0x0020,
224 S_IRUSR0000400|S_IWUSR0000200)) == -1) {
225 mwarn("%s: %s", path, strerror(errno(*__errno())));
226 goto bad;
227 }
228 if (fstat(mbfd, &fsb) == -1) {
229 /* relating error to path may be bad style */
230 mwarn("%s: %s", path, strerror(errno(*__errno())));
231 goto bad;
232 }
233 if (sb.st_dev != fsb.st_dev || sb.st_ino != fsb.st_ino) {
234 mwarn("%s: changed after open", path);
235 goto bad;
236 }
237 /* paranoia? */
238 if (fsb.st_nlink != 1 || !S_ISREG(fsb.st_mode)((fsb.st_mode & 0170000) == 0100000)) {
239 mwarn("%s: linked or special file", path);
240 rval = EX_CANTCREAT73;
241 goto bad;
242 }
243 }
244
245 curoff = lseek(mbfd, 0, SEEK_END2);
246 (void)snprintf(biffmsg, sizeof biffmsg, "%s@%lld\n", name, curoff);
247 if (lseek(fd, 0, SEEK_SET0) == (off_t)-1) {
248 mwarn("temporary file: %s", strerror(errno(*__errno())));
249 goto bad;
250 }
251
252 while ((nr = read(fd, buf, sizeof(buf))) > 0)
253 for (off = 0; off < nr; off += nw)
254 if ((nw = write(mbfd, buf + off, nr - off)) == -1) {
255 mwarn("%s: %s", path, strerror(errno(*__errno())));
256 (void)ftruncate(mbfd, curoff);
257 goto bad;
258 }
259
260 if (nr == 0) {
261 rval = 0;
262 } else {
263 (void)ftruncate(mbfd, curoff);
264 mwarn("temporary file: %s", strerror(errno(*__errno())));
265 }
266
267bad:
268 if (lfd != -1)
269 unlockspool();
270
271 if (mbfd != -1) {
272 (void)fsync(mbfd); /* Don't wait for update. */
273 (void)close(mbfd); /* Implicit unlock. */
274 }
275
276 if (!rval)
277 notifybiff(biffmsg);
278 return(rval);
279}
280
281void
282notifybiff(char *msg)
283{
284 static struct addrinfo *res0;
285 struct addrinfo hints, *res;
286 static int f = -1;
287 size_t len;
288 int error;
289
290 if (res0 == NULL((void *)0)) {
291 memset(&hints, 0, sizeof(hints));
292 hints.ai_family = PF_UNSPEC0;
293 hints.ai_socktype = SOCK_DGRAM2;
294
295 error = getaddrinfo("localhost", "biff", &hints, &res0);
296 if (error) {
297 /* Be silent if biff service not available. */
298 if (error != EAI_SERVICE-8) {
299 mwarn("localhost: %s", gai_strerror(error));
300 }
301 return;
302 }
303 }
304
305 if (f == -1) {
306 for (res = res0; res != NULL((void *)0); res = res->ai_next) {
307 f = socket(res->ai_family, res->ai_socktype,
308 res->ai_protocol);
309 if (f != -1)
310 break;
311 }
312 }
313 if (f == -1) {
314 mwarn("socket: %s", strerror(errno(*__errno())));
315 return;
316 }
317
318 len = strlen(msg) + 1; /* XXX */
319 if (sendto(f, msg, len, 0, res->ai_addr, res->ai_addrlen) != len)
320 mwarn("sendto biff: %s", strerror(errno(*__errno())));
321}
322
323static int lockfd = -1;
324static pid_t lockpid = -1;
325
326int
327lockspool(const char *name, struct passwd *pw)
328{
329 int pfd[2];
330 char ch;
331
332 if (geteuid() == 0)
333 return getlock(name, pw);
334
335 /* If not privileged, open pipe to lockspool(1) instead */
336 if (pipe2(pfd, O_CLOEXEC0x10000) == -1) {
337 merr(EX_OSERR71, "pipe: %s", strerror(errno(*__errno())));
338 return -1;
339 }
340
341 signal(SIGPIPE13, SIG_IGN(void (*)(int))1);
342 switch ((lockpid = fork())) {
343 case -1:
344 merr(EX_OSERR71, "fork: %s", strerror(errno(*__errno())));
345 return -1;
346 case 0:
347 /* child */
348 close(pfd[0]);
349 dup2(pfd[1], STDOUT_FILENO1);
350 execl(_PATH_LOCKSPOOL"/usr/libexec/lockspool", "lockspool", (char *)NULL((void *)0));
351 merr(EX_OSERR71, "execl: lockspool: %s", strerror(errno(*__errno())));
352 /* NOTREACHED */
353 break;
354 default:
355 /* parent */
356 close(pfd[1]);
357 lockfd = pfd[0];
358 break;
359 }
360
361 if (read(lockfd, &ch, 1) != 1 || ch != '1') {
362 unlockspool();
363 merr(EX_OSERR71, "lockspool: unable to get lock");
364 }
365
366 return lockfd;
367}
368
369void
370unlockspool(void)
371{
372 if (lockpid != -1) {
373 waitpid(lockpid, NULL((void *)0), 0);
374 lockpid = -1;
375 } else {
376 rellock();
377 }
378 close(lockfd);
379 lockfd = -1;
380}
381
382void
383usage(void)
384{
385 merr(EX_USAGE64, "usage: mail.local [-Ll] [-f from] user ...");
386}