Bug Summary

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

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -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 -mthread-model posix -mframe-pointer=all -fmath-errno -fno-rounding-math -masm-verbose -mconstructor-aliases -fno-plt -munwind-tables -target-cpu x86-64 -dwarf-column-info -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib/clang/10.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/local/include -internal-isystem /usr/lib/clang/10.0.0/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 -fmessage-length 0 -stack-protector 2 -fgnuc-version=4.2.1 -fobjc-runtime=gcc -fdiagnostics-show-option -analyzer-output=html -faddrsig -o /home/buildbot/lwan-worker/clang-analyze/CLANG/2020-07-18-163813-1474451-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 Leandro A. F. Pereira <leandro@hardinfo.org>
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-cache.h"
28#include "lwan-config.h"
29#include "lwan-http-authorize.h"
30
31struct realm_password_file_t {
32 struct cache_entry base;
33 struct hash *entries;
34};
35
36static struct cache *realm_password_cache = NULL((void*)0);
37
38static void fourty_two_and_free(void *str)
39{
40 if (LIKELY(str)__builtin_expect((!!(str)), (1))) {
41 char *s = str;
42 while (*s)
43 *s++ = 42;
44 LWAN_NO_DISCARD(str)do { __typeof__(str) no_discard_ = str; __asm__ __volatile__(
"" ::"g"(no_discard_) : "memory"); } while (0)
;
45 free(str);
46 }
47}
48
49static struct cache_entry *
50create_realm_file(const char *key, void *context __attribute__((unused)))
51{
52 struct realm_password_file_t *rpf = malloc(sizeof(*rpf));
53 const struct config_line *l;
54 struct config *f;
55
56 if (UNLIKELY(!rpf)__builtin_expect(((!rpf)), (0)))
1
Assuming 'rpf' is non-null
2
Taking false branch
57 return NULL((void*)0);
58
59 rpf->entries = hash_str_new(fourty_two_and_free, fourty_two_and_free);
60 if (UNLIKELY(!rpf->entries)__builtin_expect(((!rpf->entries)), (0)))
3
Assuming field 'entries' is non-null
4
Taking false branch
61 goto error_no_close;
62
63#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
64 static const uint8_t hardcoded_user_config[] = "user=password\n"
65 "root=hunter2\n";
66 f = config_open_for_fuzzing(hardcoded_user_config,
67 sizeof(hardcoded_user_config));
68#else
69 f = config_open(key);
70#endif
71 if (!f)
5
Assuming 'f' is non-null
6
Taking false branch
72 goto error_no_close;
73
74 while ((l = config_read_line(f))) {
7
Loop condition is true. Entering loop body
17
Potential leak of memory pointed to by 'username'
75 /* FIXME: Storing plain-text passwords in memory isn't a good idea. */
76 switch (l->type) {
8
Control jumps to 'case CONFIG_LINE_TYPE_LINE:' at line 77
77 case CONFIG_LINE_TYPE_LINE: {
78 char *username = strdup(l->key);
9
Memory is allocated
79 if (!username)
10
Assuming 'username' is non-null
11
Taking false branch
80 goto error;
81
82 char *password = strdup(l->value);
83 if (!password) {
12
Assuming 'password' is non-null
13
Taking false branch
84 free(username);
85 goto error;
86 }
87
88 int err = hash_add_unique(rpf->entries, username, password);
89 if (LIKELY(!err)__builtin_expect((!!(!err)), (1)))
14
Assuming 'err' is 0
15
Taking true branch
90 continue;
16
Execution continues on line 74
91
92 free(username);
93 free(password);
94
95 if (err == -EEXIST17) {
96 lwan_status_warning(lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-http-authorize.c"
, 97, __FUNCTION__, "Username entry already exists, ignoring: \"%s\""
, l->key)
97 "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"
, 97, __FUNCTION__, "Username entry already exists, ignoring: \"%s\""
, l->key)
;
98 continue;
99 }
100
101 goto error;
102 }
103 default:
104 config_error(f, "Expected username = password");
105 break;
106 }
107 }
108
109 if (config_last_error(f)) {
110 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"
, 111, __FUNCTION__, "Error on password file \"%s\", line %d: %s"
, key, config_cur_line(f), config_last_error(f))
111 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"
, 111, __FUNCTION__, "Error on password file \"%s\", line %d: %s"
, key, config_cur_line(f), config_last_error(f))
;
112 goto error;
113 }
114
115 config_close(f);
116 return (struct cache_entry *)rpf;
117
118error:
119 config_close(f);
120error_no_close:
121 hash_free(rpf->entries);
122 free(rpf);
123 return NULL((void*)0);
124}
125
126static void destroy_realm_file(struct cache_entry *entry,
127 void *context __attribute__((unused)))
128{
129 struct realm_password_file_t *rpf = (struct realm_password_file_t *)entry;
130 hash_free(rpf->entries);
131 free(rpf);
132}
133
134bool_Bool lwan_http_authorize_init(void)
135{
136 realm_password_cache =
137 cache_create(create_realm_file, destroy_realm_file, NULL((void*)0), 60);
138
139 return !!realm_password_cache;
140}
141
142void lwan_http_authorize_shutdown(void) { cache_destroy(realm_password_cache); }
143
144static bool_Bool authorize(struct coro *coro,
145 const char *header,
146 size_t header_len,
147 const char *password_file)
148{
149 struct realm_password_file_t *rpf;
150 unsigned char *decoded;
151 char *colon;
152 char *password;
153 char *looked_password;
154 size_t decoded_len;
155 bool_Bool password_ok = false0;
156
157 rpf = (struct realm_password_file_t *)cache_coro_get_and_ref_entry(
158 realm_password_cache, coro, password_file);
159 if (UNLIKELY(!rpf)__builtin_expect(((!rpf)), (0)))
160 return false0;
161
162 decoded = base64_decode((unsigned char *)header, header_len, &decoded_len);
163 if (UNLIKELY(!decoded)__builtin_expect(((!decoded)), (0)))
164 return false0;
165
166 colon = memchr(decoded, ':', decoded_len);
167 if (UNLIKELY(!colon)__builtin_expect(((!colon)), (0)))
168 goto out;
169
170 *colon = '\0';
171 password = colon + 1;
172
173 looked_password = hash_find(rpf->entries, decoded);
174 if (looked_password)
175 password_ok = streq(password, looked_password);
176
177out:
178 free(decoded);
179 return password_ok;
180}
181
182bool_Bool lwan_http_authorize(struct lwan_request *request,
183 const char *realm,
184 const char *password_file)
185{
186 static const char authenticate_tmpl[] = "Basic realm=\"%s\"";
187 static const size_t basic_len = sizeof("Basic ") - 1;
188 const char *authorization =
189 lwan_request_get_header(request, "Authorization");
190
191 if (LIKELY(authorization && !strncmp(authorization, "Basic ", basic_len))__builtin_expect((!!(authorization && !strncmp(authorization
, "Basic ", basic_len))), (1))
) {
192 const char *header = authorization + basic_len;
193 size_t header_len = strlen(authorization) - basic_len;
194
195 if (authorize(request->conn->coro, header, header_len, password_file))
196 return true1;
197 }
198
199 const struct lwan_key_value headers[] = {
200 {"WWW-Authenticate",
201 coro_printf(request->conn->coro, authenticate_tmpl, realm)},
202 {},
203 };
204 request->response.headers =
205 coro_memdup(request->conn->coro, headers, sizeof(headers));
206
207 return false0;
208}