Bug Summary

File: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;
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
12
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 char *password = strdup(l.line.value);
6
Memory is allocated
71 int err;
72
73 if (!username || !password) {
7
Assuming 'username' is non-null
8
Assuming 'password' is non-null
9
Taking false branch
74 free(username);
75 free(password);
76 goto error;
77 }
78
79 err = hash_add_unique(rpf->entries, username, password);
80 if (LIKELY(!err)__builtin_expect((!!(!err)), (1)))
10
Taking true branch
81 continue;
11
Execution continues on line 65
82
83 if (err == -EEXIST17)
84 lwan_status_warning(lwan_status_warning_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-http-authorize.c"
, 86, __FUNCTION__, "Username entry already exists, ignoring: \"%s\""
, username)
85 "Username entry already exists, ignoring: \"%s\"",lwan_status_warning_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-http-authorize.c"
, 86, __FUNCTION__, "Username entry already exists, ignoring: \"%s\""
, username)
86 username)lwan_status_warning_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-http-authorize.c"
, 86, __FUNCTION__, "Username entry already exists, ignoring: \"%s\""
, username)
;
87
88 free(username);
89 free(password);
90
91 if (err == -EEXIST17)
92 continue;
93
94 goto error;
95 }
96 default:
97 config_error(&f, "Expected username = password");
98 break;
99 }
100 }
101
102 if (f.error_message) {
103 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"
, 104, __FUNCTION__, "Error on password file \"%s\", line %d: %s"
, key, f.line, f.error_message)
104 key, f.line, f.error_message)lwan_status_error_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-http-authorize.c"
, 104, __FUNCTION__, "Error on password file \"%s\", line %d: %s"
, key, f.line, f.error_message)
;
105 goto error;
106 }
107
108 config_close(&f);
109 return (struct cache_entry_t *)rpf;
110
111error:
112 config_close(&f);
113error_no_close:
114 hash_free(rpf->entries);
115 free(rpf);
116 return NULL((void*)0);
117}
118
119static void _destroy_realm_file(struct cache_entry_t *entry,
120 void *context __attribute__((unused)))
121{
122 struct realm_password_file_t *rpf = (struct realm_password_file_t *)entry;
123 hash_free(rpf->entries);
124 free(rpf);
125}
126
127bool_Bool
128lwan_http_authorize_init(void)
129{
130 realm_password_cache = cache_create(_create_realm_file,
131 _destroy_realm_file, NULL((void*)0), 60);
132
133 return !!realm_password_cache;
134}
135
136void
137lwan_http_authorize_shutdown(void)
138{
139 cache_destroy(realm_password_cache);
140}
141
142static bool_Bool
143_authorize(coro_t *coro,
144 lwan_value_t *authorization,
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 *)authorization->value,
161 authorization->len, &decoded_len);
162 if (UNLIKELY(!decoded)__builtin_expect(((!decoded)), (0)))
163 return false0;
164
165 /* 1024 is the line buffer size for config_* */
166 if (UNLIKELY(decoded_len >= 1024)__builtin_expect(((decoded_len >= 1024)), (0)))
167 goto out;
168
169 colon = memchr(decoded, ':', decoded_len);
170 if (UNLIKELY(!colon)__builtin_expect(((!colon)), (0)))
171 goto out;
172
173 *colon = '\0';
174 password = colon + 1;
175
176 looked_password = hash_find(rpf->entries, decoded);
177 if (looked_password)
178 password_ok = !strcmp(password, looked_password);
179
180out:
181 free(decoded);
182 return password_ok;
183}
184
185bool_Bool
186lwan_http_authorize(lwan_request_t *request,
187 lwan_value_t *authorization,
188 const char *realm,
189 const char *password_file)
190{
191 static const char authenticate_tmpl[] = "Basic realm=\"%s\"";
192 static const size_t basic_len = sizeof("Basic ") - 1;
193 lwan_key_value_t *headers;
194
195 if (!authorization->value)
196 goto unauthorized;
197
198 if (UNLIKELY(strncmp(authorization->value, "Basic ", basic_len))__builtin_expect(((strncmp(authorization->value, "Basic ",
basic_len))), (0))
)
199 goto unauthorized;
200
201 authorization->value += basic_len;
202 authorization->len -= basic_len;
203
204 if (_authorize(request->conn->coro, authorization, password_file))
205 return true1;
206
207unauthorized:
208 headers = coro_malloc(request->conn->coro, 2 * sizeof(*headers));
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}