Bug Summary

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