Bug Summary

File:lwan-http-authorize.c
Warning:line 67, column 12
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 -mrelocation-model pic -pic-level 2 -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -fno-plt -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -resource-dir /usr/lib/clang/8.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/8.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -std=gnu99 -fdebug-compilation-dir /home/buildbot/lwan-worker/clang-analyze/build/src/lib -ferror-limit 19 -fmessage-length 0 -stack-protector 2 -fobjc-runtime=gcc -fdiagnostics-show-option -analyzer-output=html -o /home/buildbot/lwan-worker/clang-analyze/CLANG/2019-04-17-045736-12850-1 -x c /home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-http-authorize.c -faddrsig
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, USA.
18 */
19
20#include <errno(*__errno_location ()).h>
21#include <stdbool.h>
22#include <stdlib.h>
23#include <string.h>
24
25#include "base64.h"
26#include "lwan-cache.h"
27#include "lwan-config.h"
28#include "lwan-http-authorize.h"
29
30struct realm_password_file_t {
31 struct cache_entry base;
32 struct hash *entries;
33};
34
35static struct cache *realm_password_cache = NULL((void*)0);
36
37static void fourty_two_and_free(void *str)
38{
39 if (LIKELY(str)__builtin_expect((!!(str)), (1))) {
40 char *s = str;
41 while (*s)
42 *s++ = 42;
43 __asm__ __volatile__("" :: "g"(s) : "memory");
44 free(str);
45 }
46}
47
48static struct cache_entry *create_realm_file(
49 const char *key,
50 void *context __attribute__((unused)))
51{
52 struct realm_password_file_t *rpf = malloc(sizeof(*rpf));
53 struct config *f;
54 struct config_line l;
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 the condition is false
4
Taking false branch
61 goto error_no_close;
62
63 f = config_open(key);
64 if (!f)
5
Assuming 'f' is non-null
6
Taking false branch
65 goto error_no_close;
66
67 while (config_read_line(f, &l)) {
7
Loop condition is true. Entering loop body
17
Potential leak of memory pointed to by 'username'
68 /* FIXME: Storing plain-text passwords in memory isn't a good idea. */
69 switch (l.type) {
8
Control jumps to 'case CONFIG_LINE_TYPE_LINE:' at line 70
70 case CONFIG_LINE_TYPE_LINE: {
71 char *username = strdup(l.key);
9
Memory is allocated
72 if (!username)
10
Assuming 'username' is non-null
11
Taking false branch
73 goto error;
74
75 char *password = strdup(l.value);
76 if (!password) {
12
Assuming 'password' is non-null
13
Taking false branch
77 free(username);
78 goto error;
79 }
80
81 int err = hash_add_unique(rpf->entries, username, password);
82 if (LIKELY(!err)__builtin_expect((!!(!err)), (1)))
14
Assuming 'err' is 0
15
Taking true branch
83 continue;
16
Execution continues on line 67
84
85 free(username);
86 free(password);
87
88 if (err == -EEXIST17) {
89 lwan_status_warning(lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-http-authorize.c"
, 91, __FUNCTION__, "Username entry already exists, ignoring: \"%s\""
, l.key)
90 "Username entry already exists, ignoring: \"%s\"",lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-http-authorize.c"
, 91, __FUNCTION__, "Username entry already exists, ignoring: \"%s\""
, l.key)
91 l.key)lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-http-authorize.c"
, 91, __FUNCTION__, "Username entry already exists, ignoring: \"%s\""
, l.key)
;
92 continue;
93 }
94
95 goto error;
96 }
97 default:
98 config_error(f, "Expected username = password");
99 break;
100 }
101 }
102
103 if (config_last_error(f)) {
104 lwan_status_error("Error on password file \"%s\", line %d: %s",lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-http-authorize.c"
, 105, __FUNCTION__, "Error on password file \"%s\", line %d: %s"
, key, config_cur_line(f), config_last_error(f))
105 key, 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"
, 105, __FUNCTION__, "Error on password file \"%s\", line %d: %s"
, key, config_cur_line(f), config_last_error(f))
;
106 goto error;
107 }
108
109 config_close(f);
110 return (struct cache_entry *)rpf;
111
112error:
113 config_close(f);
114error_no_close:
115 hash_free(rpf->entries);
116 free(rpf);
117 return NULL((void*)0);
118}
119
120static void destroy_realm_file(struct cache_entry *entry,
121 void *context __attribute__((unused)))
122{
123 struct realm_password_file_t *rpf = (struct realm_password_file_t *)entry;
124 hash_free(rpf->entries);
125 free(rpf);
126}
127
128bool_Bool
129lwan_http_authorize_init(void)
130{
131 realm_password_cache = cache_create(create_realm_file,
132 destroy_realm_file, NULL((void*)0), 60);
133
134 return !!realm_password_cache;
135}
136
137void
138lwan_http_authorize_shutdown(void)
139{
140 cache_destroy(realm_password_cache);
141}
142
143static bool_Bool
144authorize(struct coro *coro,
145 struct lwan_value *authorization,
146 const char *password_file)
147{
148 struct realm_password_file_t *rpf;
149 unsigned char *decoded;
150 char *colon;
151 char *password;
152 char *looked_password;
153 size_t decoded_len;
154 bool_Bool password_ok = false0;
155
156 rpf = (struct realm_password_file_t *)cache_coro_get_and_ref_entry(
157 realm_password_cache, coro, password_file);
158 if (UNLIKELY(!rpf)__builtin_expect(((!rpf)), (0)))
159 return false0;
160
161 decoded = base64_decode((unsigned char *)authorization->value,
162 authorization->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
183lwan_http_authorize(struct lwan_request *request,
184 struct lwan_value *authorization,
185 const char *realm,
186 const char *password_file)
187{
188 static const char authenticate_tmpl[] = "Basic realm=\"%s\"";
189 static const size_t basic_len = sizeof("Basic ") - 1;
190 struct lwan_key_value *headers;
191
192 if (!authorization->value)
193 goto unauthorized;
194
195 if (UNLIKELY(strncmp(authorization->value, "Basic ", basic_len))__builtin_expect(((strncmp(authorization->value, "Basic ",
basic_len))), (0))
)
196 goto unauthorized;
197
198 authorization->value += basic_len;
199 authorization->len -= basic_len;
200
201 if (authorize(request->conn->coro, authorization, password_file))
202 return true1;
203
204unauthorized:
205 headers = coro_malloc(request->conn->coro, 2 * sizeof(*headers));
206 if (UNLIKELY(!headers)__builtin_expect(((!headers)), (0)))
207 return false0;
208
209 headers[0].key = "WWW-Authenticate";
210 headers[0].value = coro_printf(request->conn->coro,
211 authenticate_tmpl, realm);
212 headers[1].key = headers[1].value = NULL((void*)0);
213
214 request->response.headers = headers;
215 return false0;
216}