Bug Summary

File:lwan-http-authorize.c
Location:line 55, column 12
Description:Potential leak of memory pointed to by 'username'

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