Bug Summary

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

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -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 -ffp-contract=on -fno-rounding-math -mconstructor-aliases -fno-plt -funwind-tables=2 -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/14.0.6 -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.1 -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/14.0.6/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../x86_64-pc-linux-gnu/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-free-nonheap-object -std=gnu11 -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/2023-01-03-043944-1058096-1 -x c /home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-http-authorize.c
1/*
2 * lwan - 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 zero_and_free(void *str)
40{
41 if (LIKELY(str)__builtin_expect((!!(str)), (1))) {
42 lwan_always_bzero(str, strlen(str));
43 free(str);
44 }
45}
46
47static struct cache_entry *
48create_realm_file(const void *key, void *context __attribute__((unused)))
49{
50 struct realm_password_file_t *rpf = malloc(sizeof(*rpf));
51 const struct config_line *l;
52 struct config *f;
53
54 if (UNLIKELY(!rpf)__builtin_expect(((!rpf)), (0)))
1
Assuming 'rpf' is non-null
2
Taking false branch
55 return NULL((void*)0);
56
57 rpf->entries = hash_str_new(zero_and_free, zero_and_free);
58 if (UNLIKELY(!rpf->entries)__builtin_expect(((!rpf->entries)), (0)))
3
Assuming field 'entries' is non-null
4
Taking false branch
59 goto error_no_close;
60
61#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
62 static const uint8_t hardcoded_user_config[] = "user=password\n"
63 "root=hunter2\n";
64 f = config_open_for_fuzzing(hardcoded_user_config,
65 sizeof(hardcoded_user_config));
66#else
67 f = config_open(key);
68#endif
69 if (!f)
5
Assuming 'f' is non-null
6
Taking false branch
70 goto error_no_close;
71
72 while ((l = config_read_line(f))) {
7
Loop condition is true. Entering loop body
16
Execution continues on line 72
17
Potential leak of memory pointed to by 'username'
73 /* FIXME: Storing plain-text passwords in memory isn't a good idea. */
74 switch (l->type) {
8
Control jumps to 'case CONFIG_LINE_TYPE_LINE:' at line 75
75 case CONFIG_LINE_TYPE_LINE: {
76 char *username = strdup(l->key);
9
Memory is allocated
77 if (!username)
10
Assuming 'username' is non-null
11
Taking false branch
78 goto error;
79
80 char *password = strdup(l->value);
81 if (!password) {
12
Assuming 'password' is non-null
13
Taking false branch
82 free(username);
83 goto error;
84 }
85
86 int err = hash_add_unique(rpf->entries, username, password);
87 if (LIKELY(!err)__builtin_expect((!!(!err)), (1)))
14
Assuming 'err' is 0
15
Taking true branch
88 continue;
89
90 zero_and_free(username);
91 zero_and_free(password);
92
93 if (err == -EEXIST17) {
94 lwan_status_warning(lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-http-authorize.c"
, 95, __FUNCTION__, "Username entry already exists, ignoring: \"%s\""
, l->key)
95 "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"
, 95, __FUNCTION__, "Username entry already exists, ignoring: \"%s\""
, l->key)
;
96 continue;
97 }
98
99 goto error;
100 }
101 default:
102 config_error(f, "Expected username = password");
103 break;
104 }
105 }
106
107 if (config_last_error(f)) {
108 lwan_status_error("Error on password file \"%s\", line %d: %s", (char *)key,lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-http-authorize.c"
, 109, __FUNCTION__, "Error on password file \"%s\", line %d: %s"
, (char *)key, config_cur_line(f), config_last_error(f))
109 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"
, 109, __FUNCTION__, "Error on password file \"%s\", line %d: %s"
, (char *)key, config_cur_line(f), config_last_error(f))
;
110 goto error;
111 }
112
113 config_close(f);
114 return (struct cache_entry *)rpf;
115
116error:
117 config_close(f);
118error_no_close:
119 hash_free(rpf->entries);
120 free(rpf);
121 return NULL((void*)0);
122}
123
124static void destroy_realm_file(struct cache_entry *entry,
125 void *context __attribute__((unused)))
126{
127 struct realm_password_file_t *rpf = (struct realm_password_file_t *)entry;
128 hash_free(rpf->entries);
129 free(rpf);
130}
131
132bool_Bool lwan_http_authorize_init(void)
133{
134 realm_password_cache =
135 cache_create(create_realm_file, destroy_realm_file, NULL((void*)0), 60);
136
137 return !!realm_password_cache;
138}
139
140void lwan_http_authorize_shutdown(void) { cache_destroy(realm_password_cache); }
141
142static bool_Bool authorize(struct coro *coro,
143 const char *header,
144 size_t header_len,
145 const char *password_file)
146{
147 struct realm_password_file_t *rpf;
148 unsigned char *decoded;
149 char *colon;
150 char *password;
151 char *looked_password;
152 size_t decoded_len;
153 bool_Bool password_ok = false0;
154
155 rpf = (struct realm_password_file_t *)cache_coro_get_and_ref_entry(
156 realm_password_cache, coro, password_file);
157 if (UNLIKELY(!rpf)__builtin_expect(((!rpf)), (0)))
158 return false0;
159
160 decoded = base64_decode((unsigned char *)header, header_len, &decoded_len);
161 if (UNLIKELY(!decoded)__builtin_expect(((!decoded)), (0)))
162 return false0;
163
164 colon = memchr(decoded, ':', decoded_len);
165 if (UNLIKELY(!colon)__builtin_expect(((!colon)), (0)))
166 goto out;
167
168 *colon = '\0';
169 password = colon + 1;
170
171 looked_password = hash_find(rpf->entries, decoded);
172 if (looked_password)
173 password_ok = streq(password, looked_password);
174
175out:
176 free(decoded);
177 return password_ok;
178}
179
180bool_Bool lwan_http_authorize(struct lwan_request *request,
181 const char *realm,
182 const char *password_file)
183{
184 static const char authenticate_tmpl[] = "Basic realm=\"%s\"";
185 static const size_t basic_len = sizeof("Basic ") - 1;
186 const char *authorization =
187 lwan_request_get_header(request, "Authorization");
188
189 if (LIKELY(authorization && !strncmp(authorization, "Basic ", basic_len))__builtin_expect((!!(authorization && !strncmp(authorization
, "Basic ", basic_len))), (1))
) {
190 const char *header = authorization + basic_len;
191 size_t header_len = strlen(authorization) - basic_len;
192
193 if (authorize(request->conn->coro, header, header_len, password_file))
194 return true1;
195 }
196
197 const struct lwan_key_value headers[] = {
198 {"WWW-Authenticate",
199 coro_printf(request->conn->coro, authenticate_tmpl, realm)},
200 {},
201 };
202 request->response.headers =
203 coro_memdup(request->conn->coro, headers, sizeof(headers));
204
205 return false0;
206}