Bug Summary

File:src/usr.bin/htpasswd/htpasswd.c
Warning:line 195, column 12
Although the value stored to 'linelen' is used in the enclosing expression, the value is never actually read from 'linelen'

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 htpasswd.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/htpasswd/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/htpasswd/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/htpasswd/htpasswd.c
1/* $OpenBSD: htpasswd.c,v 1.18 2021/07/12 15:09:19 beck Exp $ */
2/*
3 * Copyright (c) 2014 Florian Obser <florian@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/stat.h>
19
20#include <err.h>
21#include <errno(*__errno()).h>
22#include <fcntl.h>
23#include <limits.h>
24#include <pwd.h>
25#include <readpassphrase.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <unistd.h>
30
31__dead__attribute__((__noreturn__)) void usage(void);
32void nag(char*);
33
34extern char *__progname;
35
36__dead__attribute__((__noreturn__)) void
37usage(void)
38{
39 fprintf(stderr(&__sF[2]), "usage:\t%s [file] login\n", __progname);
40 fprintf(stderr(&__sF[2]), "\t%s -I [file]\n", __progname);
41 exit(1);
42}
43
44#define MAXNAG5 5
45int nagcount;
46
47int
48main(int argc, char** argv)
49{
50 char tmpl[sizeof("/tmp/htpasswd-XXXXXXXXXX")];
51 char hash[_PASSWORD_LEN128], pass[1024], pass2[1024];
52 char *line = NULL((void *)0), *login = NULL((void *)0), *tok;
53 int c, fd, loginlen, batch = 0;
54 FILE *in = NULL((void *)0), *out = NULL((void *)0);
55 const char *file = NULL((void *)0);
56 size_t linesize = 0;
57 ssize_t linelen;
58 mode_t old_umask;
59
60 while ((c = getopt(argc, argv, "I")) != -1) {
61 switch (c) {
62 case 'I':
63 batch = 1;
64 break;
65 default:
66 usage();
67 /* NOT REACHED */
68 break;
69 }
70 }
71
72 argc -= optind;
73 argv += optind;
74
75 if ((batch && argc == 1) || (!batch && argc == 2)) {
76 if (unveil(argv[0], "rwc") == -1)
77 err(1, "unveil %s", argv[0]);
78 if (unveil("/tmp", "rwc") == -1)
79 err(1, "unveil /tmp");
80 }
81 if (pledge("stdio rpath wpath cpath flock tmppath tty", NULL((void *)0)) == -1)
82 err(1, "pledge");
83
84 if (batch) {
85 if (argc == 1)
86 file = argv[0];
87 else if (argc > 1)
88 usage();
89 else if (pledge("stdio", NULL((void *)0)) == -1)
90 err(1, "pledge");
91
92 if ((linelen = getline(&line, &linesize, stdin(&__sF[0]))) == -1)
93 err(1, "cannot read login:password from stdin");
94 line[linelen-1] = '\0';
95
96 if ((tok = strstr(line, ":")) == NULL((void *)0))
97 errx(1, "cannot find ':' in input");
98 *tok++ = '\0';
99
100 if ((loginlen = asprintf(&login, "%s:", line)) == -1)
101 err(1, "asprintf");
102
103 if (strlcpy(pass, tok, sizeof(pass)) >= sizeof(pass))
104 errx(1, "password too long");
105 } else {
106
107 switch (argc) {
108 case 1:
109 if (pledge("stdio tty", NULL((void *)0)) == -1)
110 err(1, "pledge");
111 if ((loginlen = asprintf(&login, "%s:", argv[0])) == -1)
112 err(1, "asprintf");
113 break;
114 case 2:
115 file = argv[0];
116 if ((loginlen = asprintf(&login, "%s:", argv[1])) == -1)
117 err(1, "asprintf");
118 break;
119 default:
120 usage();
121 /* NOT REACHED */
122 break;
123 }
124
125 if (!readpassphrase("Password: ", pass, sizeof(pass),
126 RPP_ECHO_OFF0x00))
127 err(1, "unable to read password");
128 if (!readpassphrase("Retype Password: ", pass2, sizeof(pass2),
129 RPP_ECHO_OFF0x00)) {
130 explicit_bzero(pass, sizeof(pass));
131 err(1, "unable to read password");
132 }
133 if (strcmp(pass, pass2) != 0) {
134 explicit_bzero(pass, sizeof(pass));
135 explicit_bzero(pass2, sizeof(pass2));
136 errx(1, "passwords don't match");
137 }
138
139 explicit_bzero(pass2, sizeof(pass2));
140 }
141
142 if (crypt_newhash(pass, "bcrypt,a", hash, sizeof(hash)) != 0)
143 err(1, "can't generate hash");
144 explicit_bzero(pass, sizeof(pass));
145
146 if (file == NULL((void *)0))
147 printf("%s%s\n", login, hash);
148 else {
149 if ((in = fopen(file, "r+")) == NULL((void *)0)) {
150 if (errno(*__errno()) == ENOENT2) {
151 old_umask = umask(S_IXUSR0000100|
152 S_IWGRP0000020|S_IRGRP0000040|S_IXGRP0000010|
153 S_IWOTH0000002|S_IROTH0000004|S_IXOTH0000001);
154 if ((out = fopen(file, "w")) == NULL((void *)0))
155 err(1, "cannot open password file for"
156 " reading or writing");
157 umask(old_umask);
158 } else
159 err(1, "cannot open password file for"
160 " reading or writing");
161 } else
162 if (flock(fileno(in)(!__isthreaded ? ((in)->_file) : (fileno)(in)), LOCK_EX0x02|LOCK_NB0x04) == -1)
163 errx(1, "cannot lock password file");
164
165 /* file already exits, copy content and filter login out */
166 if (out == NULL((void *)0)) {
167 strlcpy(tmpl, "/tmp/htpasswd-XXXXXXXXXX", sizeof(tmpl));
168 if ((fd = mkstemp(tmpl)) == -1)
169 err(1, "mkstemp");
170
171 if ((out = fdopen(fd, "w+")) == NULL((void *)0))
172 err(1, "cannot open tempfile");
173
174 while ((linelen = getline(&line, &linesize, in))
175 != -1) {
176 if (strncmp(line, login, loginlen) != 0) {
177 if (fprintf(out, "%s", line) == -1)
178 errx(1, "cannot write to temp "
179 "file");
180 nag(line);
181 }
182 }
183 }
184 if (fprintf(out, "%s%s\n", login, hash) == -1)
185 errx(1, "cannot write new password hash");
186
187 /* file already exists, overwrite it */
188 if (in != NULL((void *)0)) {
189 if (fseek(in, 0, SEEK_SET0) == -1)
190 err(1, "cannot seek in password file");
191 if (fseek(out, 0, SEEK_SET0) == -1)
192 err(1, "cannot seek in temp file");
193 if (ftruncate(fileno(in)(!__isthreaded ? ((in)->_file) : (fileno)(in)), 0) == -1)
194 err(1, "cannot truncate password file");
195 while ((linelen = getline(&line, &linesize, out))
Although the value stored to 'linelen' is used in the enclosing expression, the value is never actually read from 'linelen'
196 != -1)
197 if (fprintf(in, "%s", line) == -1)
198 errx(1, "cannot write to password "
199 "file");
200 if (fclose(in) == EOF(-1))
201 err(1, "cannot close password file");
202 }
203 if (fclose(out) == EOF(-1)) {
204 if (in != NULL((void *)0))
205 err(1, "cannot close temp file");
206 else
207 err(1, "cannot close password file");
208 }
209 if (in != NULL((void *)0) && unlink(tmpl) == -1)
210 err(1, "cannot delete temp file (%s)", tmpl);
211 }
212 if (nagcount >= MAXNAG5)
213 warnx("%d more logins not using bcryt.", nagcount - MAXNAG5);
214 exit(0);
215}
216
217void
218nag(char* line)
219{
220 const char *tok;
221 if (strtok(line, ":") != NULL((void *)0))
222 if ((tok = strtok(NULL((void *)0), ":")) != NULL((void *)0))
223 if (strncmp(tok, "$2a$", 4) != 0 &&
224 strncmp(tok, "$2b$", 4) != 0) {
225 nagcount++;
226 if (nagcount <= MAXNAG5)
227 warnx("%s doesn't use bcrypt."
228 " Update the password.", line);
229 }
230}