Bug Summary

File:src/usr.bin/wall/wall.c
Warning:line 229, 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.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name wall.c -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 -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -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/llvm16/lib/clang/16 -internal-isystem /usr/local/llvm16/lib/clang/16/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 -fcf-protection=branch -fno-jump-tables -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/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.bin/wall/wall.c
1/* $OpenBSD: wall.c,v 1.37 2023/03/08 04:43:12 guenther 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
77int
78main(int argc, char **argv)
79{
80 FILE *fp;
81 int ch, ingroup;
82 struct iovec iov;
83 struct utmp utmp;
84 char *p, **mem;
85 char line[sizeof(utmp.ut_line) + 1];
86 char username[sizeof(utmp.ut_name) + 1];
87 struct passwd *pw;
88 struct group *grp;
89 struct wallgroup *g;
90 struct utmptty *un;
91 SLIST_HEAD(,utmptty)struct { struct utmptty *slh_first; } utmphead;
92 SLIST_INIT(&utmphead){ ((&utmphead)->slh_first) = ((void *)0); };
93
94 while ((ch = getopt(argc, argv, "ng:")) != -1)
95 switch (ch) {
96 case 'n':
97 /* undoc option for shutdown: suppress banner */
98 pw = getpwnam("nobody");
99 if (geteuid() == 0 || (pw && getuid() == pw->pw_uid))
100 nobanner = 1;
101 break;
102 case 'g':
103 if ((grp = getgrnam(optarg)) == NULL((void *)0))
104 errx(1, "unknown group `%s'", optarg);
105 addgroup(grp, optarg);
106 break;
107 default:
108 usage();
109 }
110 argc -= optind;
111 argv += optind;
112 if (argc > 1)
113 usage();
114
115 makemsg(*argv);
116
117 if (unveil(_PATH_UTMP"/var/run/utmp", "r") == -1)
118 err(1, "unveil %s", _PATH_UTMP"/var/run/utmp");
119 if (unveil(_PATH_DEV"/dev/", "w") == -1)
120 err(1, "unveil %s", _PATH_DEV"/dev/");
121 if (unveil(_PATH_DEVDB"/var/run/dev.db", "r") == -1)
122 err(1, "unveil %s", _PATH_DEVDB"/var/run/dev.db");
123 if (pledge("stdio rpath wpath getpw proc", NULL((void *)0)) == -1)
124 err(1, "pledge");
125
126 if (!(fp = fopen(_PATH_UTMP"/var/run/utmp", "r")))
127 err(1, "cannot read %s", _PATH_UTMP"/var/run/utmp");
128 iov.iov_base = mbuf;
129 iov.iov_len = mbufsize;
130
131 while (fread(&utmp, sizeof(utmp), 1, fp) == 1) {
132 if (!utmp.ut_name[0])
133 continue;
134 if (grouplist) {
135 ingroup = 0;
136 strncpy(username, utmp.ut_name, sizeof(utmp.ut_name));
137 username[sizeof(utmp.ut_name)] = '\0';
138 pw = getpwnam(username);
139 if (!pw)
140 continue;
141 for (g = grouplist; g && ingroup == 0; g = g->next) {
142 if (g->gid == pw->pw_gid)
143 ingroup = 1;
144 for (mem = g->mem; *mem && ingroup == 0; mem++)
145 if (strcmp(username, *mem) == 0)
146 ingroup = 1;
147 }
148 if (ingroup == 0)
149 continue;
150 }
151 strncpy(line, utmp.ut_line, sizeof(utmp.ut_line));
152 line[sizeof(utmp.ut_line)] = '\0';
153 un = malloc(sizeof(struct utmptty));
154 if (un == NULL((void *)0))
155 err(1, "malloc");
156 un->tty = strndup(line, sizeof(utmp.ut_line));
157 if (un->tty == NULL((void *)0))
158 err(1, "strndup");
159 SLIST_INSERT_HEAD(&utmphead, un, next)do { (un)->next.sle_next = (&utmphead)->slh_first; (
&utmphead)->slh_first = (un); } while (0)
;
160 }
161 fclose(fp);
162
163 if (pledge("stdio rpath wpath proc", NULL((void *)0)) == -1)
164 err(1, "pledge");
165
166 SLIST_FOREACH(un, &utmphead, next)for((un) = ((&utmphead)->slh_first); (un) != ((void *)
0); (un) = ((un)->next.sle_next))
{
167 if ((p = ttymsg(&iov, 1, un->tty, 60*5)) != NULL((void *)0))
168 warnx("%s", p);
169 }
170
171 exit(0);
172}
173
174void
175makemsg(char *fname)
176{
177 int cnt;
178 struct tm *lt;
179 struct passwd *pw;
180 struct stat sbuf;
181 time_t now;
182 FILE *fp;
183 int fd;
184 char *p, *whom, hostname[HOST_NAME_MAX255+1], lbuf[100], tmpname[PATH_MAX1024];
185 char *ttynam;
186 unsigned char ch;
187
188 snprintf(tmpname, sizeof(tmpname), "%s/wall.XXXXXXXXXX", _PATH_TMP"/tmp/");
189 if ((fd = mkstemp(tmpname)) >= 0) {
190 (void)unlink(tmpname);
191 fp = fdopen(fd, "r+");
192 }
193 if (fd == -1 || fp == NULL((void *)0))
194 err(1, "can't open temporary file");
195
196 if (!nobanner) {
197 if (!(whom = getlogin()))
198 whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
199 (void)gethostname(hostname, sizeof(hostname));
200 (void)time(&now);
201 lt = localtime(&now);
202 if ((ttynam = ttyname(STDERR_FILENO2)) == NULL((void *)0))
203 ttynam = "(not a tty)";
204
205 /*
206 * all this stuff is to blank out a square for the message;
207 * we wrap message lines at column 79, not 80, because some
208 * terminals wrap after 79, some do not, and we can't tell.
209 * Which means that we may leave a non-blank character
210 * in column 80, but that can't be helped.
211 */
212 (void)fprintf(fp, "\r%79s\r\n", " ");
213 (void)snprintf(lbuf, sizeof lbuf,
214 "Broadcast Message from %s@%s", whom, hostname);
215 (void)fprintf(fp, "%-79.79s\007\007\r\n", lbuf);
216 (void)snprintf(lbuf, sizeof lbuf,
217 " (%s) at %d:%02d ...", ttynam,
218 lt->tm_hour, lt->tm_min);
219 (void)fprintf(fp, "%-79.79s\r\n", lbuf);
220 }
221 (void)fprintf(fp, "%79s\r\n", " ");
222
223 if (fname) {
224 gid_t egid = getegid();
225
226 setegid(getgid());
227 if (freopen(fname, "r", stdin(&__sF[0])) == NULL((void *)0))
228 err(1, "can't read %s", fname);
229 setegid(egid);
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
230 }
231 cnt = 0;
232 while (fgets(lbuf, sizeof(lbuf), stdin(&__sF[0])))
233 for (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 if (ch != '\n')
241 p--;
242 } else if (!isu8cont(ch))
243 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))
244 ch : '?', fp)(!__isthreaded ? __sputc(isprint(ch) || isspace(ch) || ch == '\a'
? ch : '?', fp) : (putc)(isprint(ch) || isspace(ch) || ch ==
'\a' ? ch : '?', fp))
;
245 }
246 (void)fprintf(fp, "%79s\r\n", " ");
247 rewind(fp);
248
249 if (fstat(fd, &sbuf))
250 err(1, "can't stat temporary file");
251 mbufsize = sbuf.st_size;
252 mbuf = malloc((u_int)mbufsize);
253 if (mbuf == NULL((void *)0))
254 err(1, NULL((void *)0));
255 if (fread(mbuf, sizeof(*mbuf), mbufsize, fp) != mbufsize)
256 err(1, "can't read temporary file");
257 (void)close(fd);
258}
259
260void
261addgroup(struct group *grp, char *name)
262{
263 int i;
264 struct wallgroup *g;
265
266 for (i = 0; grp->gr_mem[i]; i++)
267 ;
268
269 g = malloc(sizeof *g);
270 if (g == NULL((void *)0))
271 err(1, NULL((void *)0));
272 g->gid = grp->gr_gid;
273 g->name = name;
274 g->mem = calloc(i + 1, sizeof(char *));
275 if (g->mem == NULL((void *)0))
276 err(1, NULL((void *)0));
277 for (i = 0; grp->gr_mem[i] != NULL((void *)0); i++) {
278 g->mem[i] = strdup(grp->gr_mem[i]);
279 if (g->mem[i] == NULL((void *)0))
280 err(1, NULL((void *)0));
281 }
282 g->mem[i] = NULL((void *)0);
283 g->next = grouplist;
284 grouplist = g;
285}
286
287void
288usage(void)
289{
290 extern char *__progname;
291
292 (void)fprintf(stderr(&__sF[2]), "usage: %s [-g group] [file]\n", __progname);
293 exit(1);
294}
295
296static int
297isu8cont(unsigned char c)
298{
299 return (c & (0x80 | 0x40)) == 0x80;
300}