Bug Summary

File:lib/lwan-http-authorize.c
Warning:line 75, column 17
Potential leak of memory pointed to by 'password'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name lwan-http-authorize.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 2 -fhalf-no-semantic-interposition -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -fno-plt -munwind-tables -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/buildbot/lwan-worker/clang-analyze/build/src/lib -resource-dir /usr/lib/clang/13.0.0 -include /home/buildbot/lwan-worker/clang-analyze/build/lwan-build-config.h -D _FILE_OFFSET_BITS=64 -D _TIME_BITS=64 -I /home/buildbot/lwan-worker/clang-analyze/build/src/lib/missing -I /usr/include/luajit-2.0 -I /usr/include/valgrind -I /home/buildbot/lwan-worker/clang-analyze/build/src/lib -I /home/buildbot/lwan-worker/clang-analyze/build -internal-isystem /usr/lib/clang/13.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/11.1.0/../../../../x86_64-pc-linux-gnu/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-free-nonheap-object -std=gnu99 -fdebug-compilation-dir=/home/buildbot/lwan-worker/clang-analyze/build/src/lib -ferror-limit 19 -stack-protector 2 -fgnuc-version=4.2.1 -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/buildbot/lwan-worker/clang-analyze/CLANG/2022-01-16-230328-3592891-1 -x c /home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-http-authorize.c
1/*
2 * lwan - simple web server
3 * Copyright (c) 2014 L. A. F. Pereira <l@tia.mat.br>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
18 * USA.
19 */
20
21#include <errno(*__errno_location ()).h>
22#include <stdbool.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include "base64.h"
27#include "lwan-private.h"
28#include "lwan-cache.h"
29#include "lwan-config.h"
30#include "lwan-http-authorize.h"
31
32struct realm_password_file_t {
33 struct cache_entry base;
34 struct hash *entries;
35};
36
37static struct cache *realm_password_cache = NULL((void*)0);
38
39static void fourty_two_and_free(void *str)
40{
41 if (LIKELY(str)__builtin_expect((!!(str)), (1))) {
42 char *s = str;
43 while (*s)
44 *s++ = 42;
45 LWAN_NO_DISCARD(str)do { __typeof__(str) no_discard_ = str; __asm__ __volatile__(
"" ::"g"(no_discard_) : "memory"); } while (0)
;
46 free(str);
47 }
48}
49
50static struct cache_entry *
51create_realm_file(const char *key, void *context __attribute__((unused)))
52{
53 struct realm_password_file_t *rpf = malloc(sizeof(*rpf));
54 const struct config_line *l;
55 struct config *f;
56
57 if (UNLIKELY(!rpf)__builtin_expect(((!rpf)), (0)))
1
Assuming 'rpf' is non-null
2
Taking false branch
58 return NULL((void*)0);
59
60 rpf->entries = hash_str_new(fourty_two_and_free, fourty_two_and_free);
61 if (UNLIKELY(!rpf->entries)__builtin_expect(((!rpf->entries)), (0)))
3
Assuming field 'entries' is non-null
4
Taking false branch
62 goto error_no_close;
63
64#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
65 static const uint8_t hardcoded_user_config[] = "user=password\n"
66 "root=hunter2\n";
67 f = config_open_for_fuzzing(hardcoded_user_config,
68 sizeof(hardcoded_user_config));
69#else
70 f = config_open(key);
71#endif
72 if (!f)
5
Assuming 'f' is non-null
6
Taking false branch
73 goto error_no_close;
74
75 while ((l = config_read_line(f))) {
7
Loop condition is true. Entering loop body
17
Potential leak of memory pointed to by 'password'
76 /* FIXME: Storing plain-text passwords in memory isn't a good idea. */
77 switch (l->type) {
8
Control jumps to 'case CONFIG_LINE_TYPE_LINE:' at line 78
78 case CONFIG_LINE_TYPE_LINE: {
79 char *username = strdup(l->key);
80 if (!username)
9
Assuming 'username' is non-null
10
Taking false branch
81 goto error;
82
83 char *password = strdup(l->value);
11
Memory is allocated
84 if (!password) {
12
Assuming 'password' is non-null
13
Taking false branch
85 free(username);
86 goto error;
87 }
88
89 int err = hash_add_unique(rpf->entries, username, password);
90 if (LIKELY(!err)__builtin_expect((!!(!err)), (1)))
14
Assuming 'err' is 0
15
Taking true branch
91 continue;
16
Execution continues on line 75
92
93 free(username);
94 free(password);
95
96 if (err == -EEXIST17) {
97 lwan_status_warning(lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-http-authorize.c"
, 98, __FUNCTION__, "Username entry already exists, ignoring: \"%s\""
, l->key)
98 "Username entry already exists, ignoring: \"%s\"", l->key)lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-http-authorize.c"
, 98, __FUNCTION__, "Username entry already exists, ignoring: \"%s\""
, l->key)
;
99 continue;
100 }
101
102 goto error;
103 }
104 default:
105 config_error(f, "Expected username = password");
106 break;
107 }
108 }
109
110 if (config_last_error(f)) {
111 lwan_status_error("Error on password file \"%s\", line %d: %s", key,lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-http-authorize.c"
, 112, __FUNCTION__, "Error on password file \"%s\", line %d: %s"
, key, config_cur_line(f), config_last_error(f))
112 config_cur_line(f), config_last_error(f))lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-http-authorize.c"
, 112, __FUNCTION__, "Error on password file \"%s\", line %d: %s"
, key, config_cur_line(f), config_last_error(f))
;
113 goto error;
114 }
115
116 config_close(f);
117 return (struct cache_entry *)rpf;
118
119error:
120 config_close(f);
121error_no_close:
122 hash_free(rpf->entries);
123 free(rpf);
124 return NULL((void*)0);
125}
126
127static void destroy_realm_file(struct cache_entry *entry,
128 void *context __attribute__((unused)))
129{
130 struct realm_password_file_t *rpf = (struct realm_password_file_t *)entry;
131 hash_free(rpf->entries);
132 free(rpf);
133}
134
135bool_Bool lwan_http_authorize_init(void)
136{
137 realm_password_cache =
138 cache_create(create_realm_file, destroy_realm_file, NULL((void*)0), 60);
139
140 return !!realm_password_cache;
141}
142
143void lwan_http_authorize_shutdown(void) { cache_destroy(realm_password_cache); }
144
145static bool_Bool authorize(struct coro *coro,
146 const char *header,
147 size_t header_len,
148 const char *password_file)
149{
150 struct realm_password_file_t *rpf;
151 unsigned char *decoded;
152 char *colon;
153 char *password;
154 char *looked_password;
155 size_t decoded_len;
156 bool_Bool password_ok = false0;
157
158 rpf = (struct realm_password_file_t *)cache_coro_get_and_ref_entry(
159 realm_password_cache, coro, password_file);
160 if (UNLIKELY(!rpf)__builtin_expect(((!rpf)), (0)))
161 return false0;
162
163 decoded = base64_decode((unsigned char *)header, header_len, &decoded_len);
164 if (UNLIKELY(!decoded)__builtin_expect(((!decoded)), (0)))
165 return false0;
166
167 colon = memchr(decoded, ':', decoded_len);
168 if (UNLIKELY(!colon)__builtin_expect(((!colon)), (0)))
169 goto out;
170
171 *colon = '\0';
172 password = colon + 1;
173
174 looked_password = hash_find(rpf->entries, decoded);
175 if (looked_password)
176 password_ok = streq(password, looked_password);
177
178out:
179 free(decoded);
180 return password_ok;
181}
182
183bool_Bool lwan_http_authorize(struct lwan_request *request,
184 const char *realm,
185 const char *password_file)
186{
187 static const char authenticate_tmpl[] = "Basic realm=\"%s\"";
188 static const size_t basic_len = sizeof("Basic ") - 1;
189 const char *authorization =
190 lwan_request_get_header(request, "Authorization");
191
192 if (LIKELY(authorization && !strncmp(authorization, "Basic ", basic_len))__builtin_expect((!!(authorization && !strncmp(authorization
, "Basic ", basic_len))), (1))
) {
193 const char *header = authorization + basic_len;
194 size_t header_len = strlen(authorization) - basic_len;
195
196 if (authorize(request->conn->coro, header, header_len, password_file))
197 return true1;
198 }
199
200 const struct lwan_key_value headers[] = {
201 {"WWW-Authenticate",
202 coro_printf(request->conn->coro, authenticate_tmpl, realm)},
203 {},
204 };
205 request->response.headers =
206 coro_memdup(request->conn->coro, headers, sizeof(headers));
207
208 return false0;
209}