Bug Summary

File:src/usr.bin/wall/wall.c
Warning:line 227, column 3
The return value from the call to 'setegid' is not checked. If an error occurs in 'setegid', the following code may execute with unexpected privileges

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 wall.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/wall/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/wall/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/wall/wall.c
1/* $OpenBSD: wall.c,v 1.35 2021/07/12 15:09:20 beck Exp $ */
2/* $NetBSD: wall.c,v 1.6 1994/11/17 07:17:58 jtc Exp $ */
3
4/*
5 * Copyright (c) 1988, 1990, 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 * This program is not related to David Wall, whose Stanford Ph.D. thesis
35 * is entitled "Mechanisms for Broadcast and Selective Broadcast".
36 */
37
38#include <sys/queue.h>
39#include <sys/stat.h>
40#include <sys/time.h>
41#include <sys/uio.h>
42
43#include <ctype.h>
44#include <err.h>
45#include <grp.h>
46#include <limits.h>
47#include <paths.h>
48#include <pwd.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
52#include <unistd.h>
53#include <utmp.h>
54
55struct wallgroup {
56 gid_t gid;
57 char *name;
58 char **mem;
59 struct wallgroup *next;
60} *grouplist;
61
62struct utmptty {
63 char *tty;
64 SLIST_ENTRY(utmptty)struct { struct utmptty *sle_next; } next;
65};
66
67void makemsg(char *);
68void addgroup(struct group *, char *);
69char *ttymsg(struct iovec *, int, char *, int);
70__dead__attribute__((__noreturn__)) void usage(void);
71static int isu8cont(unsigned char);
72
73int nobanner;
74int mbufsize;
75char *mbuf;
76
77/* ARGSUSED */
78int
79main(int argc, char **argv)
80{
81 FILE *fp;
82 int ch, ingroup;
83 struct iovec iov;
84 struct utmp utmp;
85 char *p, **mem;
86 char line[sizeof(utmp.ut_line) + 1];
87 char username[sizeof(utmp.ut_name) + 1];
88 struct passwd *pw;
89 struct group *grp;
90 struct wallgroup *g;
91 struct utmptty *un;
92 SLIST_HEAD(,utmptty)struct { struct utmptty *slh_first; } utmphead;
93 SLIST_INIT(&utmphead){ ((&utmphead)->slh_first) = ((void *)0); };
94
95 while ((ch = getopt(argc, argv, "ng:")) != -1)
96 switch (ch) {
97 case 'n':
98 /* undoc option for shutdown: suppress banner */
99 pw = getpwnam("nobody");
100 if (geteuid() == 0 || (pw && getuid() == pw->pw_uid))
101 nobanner = 1;
102 break;
103 case 'g':
104 if ((grp = getgrnam(optarg)) == NULL((void *)0))
105 errx(1, "unknown group `%s'", optarg);
106 addgroup(grp, optarg);
107 break;
108 default:
109 usage();
110 }
111 argc -= optind;
112 argv += optind;
113 if (argc > 1)
114 usage();
115
116 makemsg(*argv);
117
118 if (unveil(_PATH_UTMP"/var/run/utmp", "r") == -1)
119 err(1, "unveil %s", _PATH_UTMP"/var/run/utmp");
120 if (unveil(_PATH_DEV"/dev/", "w") == -1)
121 err(1, "unveil %s", _PATH_DEV"/dev/");
122 if (unveil(_PATH_DEVDB"/var/run/dev.db", "r") == -1)
123 err(1, "unveil %s", _PATH_DEVDB"/var/run/dev.db");
124 if (pledge("stdio rpath wpath getpw proc", NULL((void *)0)) == -1)
125 err(1, "pledge");
126
127 if (!(fp = fopen(_PATH_UTMP"/var/run/utmp", "r")))
128 err(1, "cannot read %s", _PATH_UTMP"/var/run/utmp");
129 iov.iov_base = mbuf;
130 iov.iov_len = mbufsize;
131
132 while (fread(&utmp, sizeof(utmp), 1, fp) == 1) {
133 if (!utmp.ut_name[0])
134 continue;
135 if (grouplist) {
136 ingroup = 0;
137 strncpy(username, utmp.ut_name, sizeof(utmp.ut_name));
138 username[sizeof(utmp.ut_name)] = '\0';
139 pw = getpwnam(username);
140 if (!pw)
141 continue;
142 for (g = grouplist; g && ingroup == 0; g = g->next) {
143 if (g->gid == pw->pw_gid)
144 ingroup = 1;
145 for (mem = g->mem; *mem && ingroup == 0; mem++)
146 if (strcmp(username, *mem) == 0)
147 ingroup = 1;
148 }
149 if (ingroup == 0)
150 continue;
151 }
152 strncpy(line, utmp.ut_line, sizeof(utmp.ut_line));
153 line[sizeof(utmp.ut_line)] = '\0';
154 un = malloc(sizeof(struct utmptty));
155 if (un == NULL((void *)0))
156 err(1, "malloc");
157 un->tty = strndup(line, sizeof(utmp.ut_line));
158 if (un->tty == NULL((void *)0))
159 err(1, "strndup");
160 SLIST_INSERT_HEAD(&utmphead, un, next)do { (un)->next.sle_next = (&utmphead)->slh_first; (
&utmphead)->slh_first = (un); } while (0)
;
161 }
162 fclose(fp);
163
164 if (pledge("stdio rpath wpath proc", NULL((void *)0)) == -1)
165 err(1, "pledge");
166
167 SLIST_FOREACH(un, &utmphead, next)for((un) = ((&utmphead)->slh_first); (un) != ((void *)
0); (un) = ((un)->next.sle_next))
{
168 if ((p = ttymsg(&iov, 1, un->tty, 60*5)) != NULL((void *)0))
169 warnx("%s", p);
170 }
171
172 exit(0);
173}
174
175void
176makemsg(char *fname)
177{
178 int cnt;
179 struct tm *lt;
180 struct passwd *pw;
181 struct stat sbuf;
182 time_t now;
183 FILE *fp;
184 int fd;
185 char *p, *whom, hostname[HOST_NAME_MAX255+1], lbuf[100], tmpname[PATH_MAX1024];
186 char *ttynam;
187 unsigned char ch;
188
189 snprintf(tmpname, sizeof(tmpname), "%s/wall.XXXXXXXXXX", _PATH_TMP"/tmp/");
190 if ((fd = mkstemp(tmpname)) >= 0) {
191 (void)unlink(tmpname);
192 fp = fdopen(fd, "r+");
193 }
194 if (fd == -1 || fp == NULL((void *)0))
195 err(1, "can't open temporary file");
196
197 if (!nobanner) {
198 if (!(whom = getlogin()))
199 whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
200 (void)gethostname(hostname, sizeof(hostname));
201 (void)time(&now);
202 lt = localtime(&now);
203 if ((ttynam = ttyname(STDERR_FILENO2)) == NULL((void *)0))
204 ttynam = "(not a tty)";
205
206 /*
207 * all this stuff is to blank out a square for the message;
208 * we wrap message lines at column 79, not 80, because some
209 * terminals wrap after 79, some do not, and we can't tell.
210 * Which means that we may leave a non-blank character
211 * in column 80, but that can't be helped.
212 */
213 (void)fprintf(fp, "\r%79s\r\n", " ");
214 (void)snprintf(lbuf, sizeof lbuf,
215 "Broadcast Message from %s@%s", whom, hostname);
216 (void)fprintf(fp, "%-79.79s\007\007\r\n", lbuf);
217 (void)snprintf(lbuf, sizeof lbuf,
218 " (%s) at %d:%02d ...", ttynam,
219 lt->tm_hour, lt->tm_min);
220 (void)fprintf(fp, "%-79.79s\r\n", lbuf);
221 }
222 (void)fprintf(fp, "%79s\r\n", " ");
223
224 if (fname) {
225 gid_t egid = getegid();
226
227 setegid(getgid());
The return value from the call to 'setegid' is not checked. If an error occurs in 'setegid', the following code may execute with unexpected privileges
228 if (freopen(fname, "r", stdin(&__sF[0])) == NULL((void *)0))
229 err(1, "can't read %s", fname);
230 setegid(egid);
231 }
232 while (fgets(lbuf, sizeof(lbuf), stdin(&__sF[0])))
233 for (cnt = 0, p = lbuf; (ch = *p) != '\0'; ++p, ++cnt) {
234 if (cnt == 79 || ch == '\n') {
235 for (; cnt < 79; ++cnt)
236 putc(' ', fp)(!__isthreaded ? __sputc(' ', fp) : (putc)(' ', fp));
237 putc('\r', fp)(!__isthreaded ? __sputc('\r', fp) : (putc)('\r', fp));
238 putc('\n', fp)(!__isthreaded ? __sputc('\n', fp) : (putc)('\n', fp));
239 cnt = -1;
240 } else if (!isu8cont(ch))
241 putc(isprint(ch) || isspace(ch) || ch == '\a' ?(!__isthreaded ? __sputc(isprint(ch) || isspace(ch) || ch == '\a'
? ch : '?', fp) : (putc)(isprint(ch) || isspace(ch) || ch ==
'\a' ? ch : '?', fp))
242 ch : '?', fp)(!__isthreaded ? __sputc(isprint(ch) || isspace(ch) || ch == '\a'
? ch : '?', fp) : (putc)(isprint(ch) || isspace(ch) || ch ==
'\a' ? ch : '?', fp))
;
243 }
244 (void)fprintf(fp, "%79s\r\n", " ");
245 rewind(fp);
246
247 if (fstat(fd, &sbuf))
248 err(1, "can't stat temporary file");
249 mbufsize = sbuf.st_size;
250 mbuf = malloc((u_int)mbufsize);
251 if (mbuf == NULL((void *)0))
252 err(1, NULL((void *)0));
253 if (fread(mbuf, sizeof(*mbuf), mbufsize, fp) != mbufsize)
254 err(1, "can't read temporary file");
255 (void)close(fd);
256}
257
258void
259addgroup(struct group *grp, char *name)
260{
261 int i;
262 struct wallgroup *g;
263
264 for (i = 0; grp->gr_mem[i]; i++)
265 ;
266
267 g = malloc(sizeof *g);
268 if (g == NULL((void *)0))
269 err(1, NULL((void *)0));
270 g->gid = grp->gr_gid;
271 g->name = name;
272 g->mem = calloc(i + 1, sizeof(char *));
273 if (g->mem == NULL((void *)0))
274 err(1, NULL((void *)0));
275 for (i = 0; grp->gr_mem[i] != NULL((void *)0); i++) {
276 g->mem[i] = strdup(grp->gr_mem[i]);
277 if (g->mem[i] == NULL((void *)0))
278 err(1, NULL((void *)0));
279 }
280 g->mem[i] = NULL((void *)0);
281 g->next = grouplist;
282 grouplist = g;
283}
284
285void
286usage(void)
287{
288 extern char *__progname;
289
290 (void)fprintf(stderr(&__sF[2]), "usage: %s [-g group] [file]\n", __progname);
291 exit(1);
292}
293
294static int
295isu8cont(unsigned char c)
296{
297 return (c & (0x80 | 0x40)) == 0x80;
298}