File: | lwan-http-authorize.c |
Warning: | line 75, column 17 Potential leak of memory pointed to by 'username' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * lwan - simple web server | |||
3 | * Copyright (c) 2014 L. A. F. Pereira <l@tia.mat.br> | |||
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-private.h" | |||
28 | #include "lwan-cache.h" | |||
29 | #include "lwan-config.h" | |||
30 | #include "lwan-http-authorize.h" | |||
31 | ||||
32 | struct realm_password_file_t { | |||
33 | struct cache_entry base; | |||
34 | struct hash *entries; | |||
35 | }; | |||
36 | ||||
37 | static struct cache *realm_password_cache = NULL((void*)0); | |||
38 | ||||
39 | static void fourty_two_and_free(void *str) | |||
40 | { | |||
41 | if (LIKELY(str)__builtin_expect((!!(str)), (1))) { | |||
42 | char *s = str; | |||
43 | while (*s) | |||
44 | *s++ = 42; | |||
45 | LWAN_NO_DISCARD(str)do { __typeof__(str) no_discard_ = str; __asm__ __volatile__( "" ::"g"(no_discard_) : "memory"); } while (0); | |||
46 | free(str); | |||
47 | } | |||
48 | } | |||
49 | ||||
50 | static struct cache_entry * | |||
51 | create_realm_file(const char *key, void *context __attribute__((unused))) | |||
52 | { | |||
53 | struct realm_password_file_t *rpf = malloc(sizeof(*rpf)); | |||
54 | const struct config_line *l; | |||
55 | struct config *f; | |||
56 | ||||
57 | if (UNLIKELY(!rpf)__builtin_expect(((!rpf)), (0))) | |||
| ||||
58 | return NULL((void*)0); | |||
59 | ||||
60 | rpf->entries = hash_str_new(fourty_two_and_free, fourty_two_and_free); | |||
61 | if (UNLIKELY(!rpf->entries)__builtin_expect(((!rpf->entries)), (0))) | |||
62 | goto error_no_close; | |||
63 | ||||
64 | #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) | |||
65 | static const uint8_t hardcoded_user_config[] = "user=password\n" | |||
66 | "root=hunter2\n"; | |||
67 | f = config_open_for_fuzzing(hardcoded_user_config, | |||
68 | sizeof(hardcoded_user_config)); | |||
69 | #else | |||
70 | f = config_open(key); | |||
71 | #endif | |||
72 | if (!f) | |||
73 | goto error_no_close; | |||
74 | ||||
75 | while ((l = config_read_line(f))) { | |||
| ||||
76 | /* FIXME: Storing plain-text passwords in memory isn't a good idea. */ | |||
77 | switch (l->type) { | |||
78 | case CONFIG_LINE_TYPE_LINE: { | |||
79 | char *username = strdup(l->key); | |||
80 | if (!username) | |||
81 | goto error; | |||
82 | ||||
83 | char *password = strdup(l->value); | |||
84 | if (!password) { | |||
85 | free(username); | |||
86 | goto error; | |||
87 | } | |||
88 | ||||
89 | int err = hash_add_unique(rpf->entries, username, password); | |||
90 | if (LIKELY(!err)__builtin_expect((!!(!err)), (1))) | |||
91 | continue; | |||
92 | ||||
93 | free(username); | |||
94 | free(password); | |||
95 | ||||
96 | if (err == -EEXIST17) { | |||
97 | lwan_status_warning(lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-http-authorize.c" , 98, __FUNCTION__, "Username entry already exists, ignoring: \"%s\"" , l->key) | |||
98 | "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" , 98, __FUNCTION__, "Username entry already exists, ignoring: \"%s\"" , l->key); | |||
99 | continue; | |||
100 | } | |||
101 | ||||
102 | goto error; | |||
103 | } | |||
104 | default: | |||
105 | config_error(f, "Expected username = password"); | |||
106 | break; | |||
107 | } | |||
108 | } | |||
109 | ||||
110 | if (config_last_error(f)) { | |||
111 | 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" , 112, __FUNCTION__, "Error on password file \"%s\", line %d: %s" , key, config_cur_line(f), config_last_error(f)) | |||
112 | 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" , 112, __FUNCTION__, "Error on password file \"%s\", line %d: %s" , key, config_cur_line(f), config_last_error(f)); | |||
113 | goto error; | |||
114 | } | |||
115 | ||||
116 | config_close(f); | |||
117 | return (struct cache_entry *)rpf; | |||
118 | ||||
119 | error: | |||
120 | config_close(f); | |||
121 | error_no_close: | |||
122 | hash_free(rpf->entries); | |||
123 | free(rpf); | |||
124 | return NULL((void*)0); | |||
125 | } | |||
126 | ||||
127 | static void destroy_realm_file(struct cache_entry *entry, | |||
128 | void *context __attribute__((unused))) | |||
129 | { | |||
130 | struct realm_password_file_t *rpf = (struct realm_password_file_t *)entry; | |||
131 | hash_free(rpf->entries); | |||
132 | free(rpf); | |||
133 | } | |||
134 | ||||
135 | bool_Bool lwan_http_authorize_init(void) | |||
136 | { | |||
137 | realm_password_cache = | |||
138 | cache_create(create_realm_file, destroy_realm_file, NULL((void*)0), 60); | |||
139 | ||||
140 | return !!realm_password_cache; | |||
141 | } | |||
142 | ||||
143 | void lwan_http_authorize_shutdown(void) { cache_destroy(realm_password_cache); } | |||
144 | ||||
145 | static bool_Bool authorize(struct coro *coro, | |||
146 | const char *header, | |||
147 | size_t header_len, | |||
148 | const char *password_file) | |||
149 | { | |||
150 | struct realm_password_file_t *rpf; | |||
151 | unsigned char *decoded; | |||
152 | char *colon; | |||
153 | char *password; | |||
154 | char *looked_password; | |||
155 | size_t decoded_len; | |||
156 | bool_Bool password_ok = false0; | |||
157 | ||||
158 | rpf = (struct realm_password_file_t *)cache_coro_get_and_ref_entry( | |||
159 | realm_password_cache, coro, password_file); | |||
160 | if (UNLIKELY(!rpf)__builtin_expect(((!rpf)), (0))) | |||
161 | return false0; | |||
162 | ||||
163 | decoded = base64_decode((unsigned char *)header, header_len, &decoded_len); | |||
164 | if (UNLIKELY(!decoded)__builtin_expect(((!decoded)), (0))) | |||
165 | return false0; | |||
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 = streq(password, looked_password); | |||
177 | ||||
178 | out: | |||
179 | free(decoded); | |||
180 | return password_ok; | |||
181 | } | |||
182 | ||||
183 | bool_Bool lwan_http_authorize(struct lwan_request *request, | |||
184 | const char *realm, | |||
185 | const char *password_file) | |||
186 | { | |||
187 | static const char authenticate_tmpl[] = "Basic realm=\"%s\""; | |||
188 | static const size_t basic_len = sizeof("Basic ") - 1; | |||
189 | const char *authorization = | |||
190 | lwan_request_get_header(request, "Authorization"); | |||
191 | ||||
192 | if (LIKELY(authorization && !strncmp(authorization, "Basic ", basic_len))__builtin_expect((!!(authorization && !strncmp(authorization , "Basic ", basic_len))), (1))) { | |||
193 | const char *header = authorization + basic_len; | |||
194 | size_t header_len = strlen(authorization) - basic_len; | |||
195 | ||||
196 | if (authorize(request->conn->coro, header, header_len, password_file)) | |||
197 | return true1; | |||
198 | } | |||
199 | ||||
200 | const struct lwan_key_value headers[] = { | |||
201 | {"WWW-Authenticate", | |||
202 | coro_printf(request->conn->coro, authenticate_tmpl, realm)}, | |||
203 | {}, | |||
204 | }; | |||
205 | request->response.headers = | |||
206 | coro_memdup(request->conn->coro, headers, sizeof(headers)); | |||
207 | ||||
208 | return false0; | |||
209 | } |