Bug Summary

File:common/lwan-http-authorize.c
Location:line 65, column 12
Description:Potential leak of memory pointed to by 'password'

Annotated Source Code

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_t base;
32 struct hash *entries;
33};
34
35static struct cache_t *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 free(str);
44 }
45}
46
47static struct cache_entry_t *_create_realm_file(
48 const char *key,
49 void *context __attribute__((unused)))
50{
51 struct realm_password_file_t *rpf = malloc(sizeof(*rpf));
52 config_t f;
53 config_line_t l;
54
55 if (UNLIKELY(!rpf)__builtin_expect(((!rpf)), (0)))
1
Taking false branch
56 return NULL((void*)0);
57
58 rpf->entries = hash_str_new(fourty_two_and_free, fourty_two_and_free);
59 if (UNLIKELY(!rpf->entries)__builtin_expect(((!rpf->entries)), (0)))
2
Taking false branch
60 goto error_no_close;
61
62 if (!config_open(&f, key))
3
Taking false branch
63 goto error_no_close;
64
65 while (config_read_line(&f, &l)) {
4
Loop condition is true. Entering loop body
13
Potential leak of memory pointed to by 'password'
66 /* FIXME: Storing plain-text passwords in memory isn't a good idea. */
67 switch (l.type) {
5
Control jumps to 'case CONFIG_LINE_TYPE_LINE:' at line 68
68 case CONFIG_LINE_TYPE_LINE: {
69 char *username = strdup(l.line.key);
70 if (!username)
6
Assuming 'username' is non-null
7
Taking false branch
71 goto error;
72
73 char *password = strdup(l.line.value);
8
Memory is allocated
74 if (!password) {
9
Assuming 'password' is non-null
10
Taking false branch
75 free(username);
76 goto error;
77 }
78
79 int err = hash_add_unique(rpf->entries, username, password);
80 if (LIKELY(!err)__builtin_expect((!!(!err)), (1)))
11
Taking true branch
81 continue;
12
Execution continues on line 65
82
83 free(username);
84 free(password);
85
86 if (err == -EEXIST17) {
87 lwan_status_warning(lwan_status_warning_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-http-authorize.c"
, 89, __FUNCTION__, "Username entry already exists, ignoring: \"%s\""
, l.line.key)
88 "Username entry already exists, ignoring: \"%s\"",lwan_status_warning_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-http-authorize.c"
, 89, __FUNCTION__, "Username entry already exists, ignoring: \"%s\""
, l.line.key)
89 l.line.key)lwan_status_warning_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-http-authorize.c"
, 89, __FUNCTION__, "Username entry already exists, ignoring: \"%s\""
, l.line.key)
;
90 continue;
91 }
92
93 goto error;
94 }
95 default:
96 config_error(&f, "Expected username = password");
97 break;
98 }
99 }
100
101 if (f.error_message) {
102 lwan_status_error("Error on password file \"%s\", line %d: %s",lwan_status_error_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-http-authorize.c"
, 103, __FUNCTION__, "Error on password file \"%s\", line %d: %s"
, key, f.line, f.error_message)
103 key, f.line, f.error_message)lwan_status_error_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-http-authorize.c"
, 103, __FUNCTION__, "Error on password file \"%s\", line %d: %s"
, key, f.line, f.error_message)
;
104 goto error;
105 }
106
107 config_close(&f);
108 return (struct cache_entry_t *)rpf;
109
110error:
111 config_close(&f);
112error_no_close:
113 hash_free(rpf->entries);
114 free(rpf);
115 return NULL((void*)0);
116}
117
118static void destroy_realm_file(struct cache_entry_t *entry,
119 void *context __attribute__((unused)))
120{
121 struct realm_password_file_t *rpf = (struct realm_password_file_t *)entry;
122 hash_free(rpf->entries);
123 free(rpf);
124}
125
126bool_Bool
127lwan_http_authorize_init(void)
128{
129 realm_password_cache = cache_create(_create_realm_file,
130 destroy_realm_file, NULL((void*)0), 60);
131
132 return !!realm_password_cache;
133}
134
135void
136lwan_http_authorize_shutdown(void)
137{
138 cache_destroy(realm_password_cache);
139}
140
141static bool_Bool
142authorize(coro_t *coro,
143 lwan_value_t *authorization,
144 const char *password_file)
145{
146 struct realm_password_file_t *rpf;
147 unsigned char *decoded;
148 char *colon;
149 char *password;
150 char *looked_password;
151 size_t decoded_len;
152 bool_Bool password_ok = false0;
153
154 rpf = (struct realm_password_file_t *)cache_coro_get_and_ref_entry(
155 realm_password_cache, coro, password_file);
156 if (UNLIKELY(!rpf)__builtin_expect(((!rpf)), (0)))
157 return false0;
158
159 decoded = base64_decode((unsigned char *)authorization->value,
160 authorization->len, &decoded_len);
161 if (UNLIKELY(!decoded)__builtin_expect(((!decoded)), (0)))
162 return false0;
163
164 if (UNLIKELY(decoded_len >= sizeof(((config_line_t *)0)->buffer))__builtin_expect(((decoded_len >= sizeof(((config_line_t *
)0)->buffer))), (0))
)
165 goto out;
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 = !strcmp(password, looked_password);
177
178out:
179 free(decoded);
180 return password_ok;
181}
182
183bool_Bool
184lwan_http_authorize(lwan_request_t *request,
185 lwan_value_t *authorization,
186 const char *realm,
187 const char *password_file)
188{
189 static const char authenticate_tmpl[] = "Basic realm=\"%s\"";
190 static const size_t basic_len = sizeof("Basic ") - 1;
191 lwan_key_value_t *headers;
192
193 if (!authorization->value)
194 goto unauthorized;
195
196 if (UNLIKELY(strncmp(authorization->value, "Basic ", basic_len))__builtin_expect(((strncmp(authorization->value, "Basic ",
basic_len))), (0))
)
197 goto unauthorized;
198
199 authorization->value += basic_len;
200 authorization->len -= basic_len;
201
202 if (authorize(request->conn->coro, authorization, password_file))
203 return true1;
204
205unauthorized:
206 headers = coro_malloc(request->conn->coro, 2 * sizeof(*headers));
207 headers[0].key = "WWW-Authenticate";
208 headers[0].value = coro_printf(request->conn->coro,
209 authenticate_tmpl, realm);
210 headers[1].key = headers[1].value = NULL((void*)0);
211
212 request->response.headers = headers;
213 return false0;
214}