1#include <stdlib.h>
2#include <string.h>
3#include <strings.h>
4
5#include "config.h"
6#include "log.h"
7#include "users.h"
8#include "util.h"
9
10struct users users;
11
12static int allow_push(struct user* u, const char* s);
13static int b64_decode(unsigned char* out, size_t outsz, const char* in);
14static int b64_val(int c);
15static int ct_equal(const char* a, const char* b);
16static const struct user* find_user(const char* name);
17
18/**
19 * @brief Append allow prefix to user entry
20 *
21 * @param u User entry
22 * @param s Allow prefix string
23 *
24 * @return 0=Success, -1=Failure
25 */
26static int allow_push(struct user* u, const char* s)
27{
28 if (u->allow_len == u->allow_cap) {
29 size_t ncap = u->allow_cap ? (u->allow_cap * 2) : 8;
30 char** nv = realloc(u->allow, ncap * sizeof(*nv));
31 if (!nv)
32 return -1;
33
34 u->allow = nv;
35 u->allow_cap = ncap;
36 }
37
38 char* p = strdup(s);
39 if (!p)
40 return -1;
41
42 u->allow[u->allow_len++] = p;
43 return 0;
44}
45
46/**
47 * @brief Decode base64 string
48 *
49 * @param out Output buffer
50 * @param outsz Output buffer size
51 * @param in Input base64 string
52 *
53 * @return 0=Success, -1=Failure
54 */
55static int b64_decode(unsigned char* out, size_t outsz, const char* in)
56{
57 size_t olen = 0;
58 int buf = 0;
59 int bits = 0;
60
61 for (; *in; in++) {
62 int v = b64_val((unsigned char)*in);
63 if (v == -1)
64 continue; /* skip whitespace/junk */
65
66 if (v == -2)
67 break; /* padding */
68
69 buf = (buf << 6) | v; /* append 6 bits */
70 bits += 6;
71
72 if (bits >= 8) {
73 bits -= 8;
74 if (olen + 1 >= outsz)
75 return -1; /* overflow */
76
77 out[olen++] = (unsigned char)((buf >> bits) & 0xff);
78 }
79 }
80
81 if (olen >= outsz)
82 return -1;
83
84 out[olen] = '\0';
85 return 0;
86}
87
88/**
89 * @brief Map base64 byte to sextet value
90 *
91 * @param c Input byte
92 *
93 * @return 0..63=Value, -2=Padding, -1=Invalid
94 */
95static int b64_val(int c)
96{
97 if (c >= 'A' && c <= 'Z')
98 return c - 'A';
99 if (c >= 'a' && c <= 'z')
100 return c - 'a' + 26;
101 if (c >= '0' && c <= '9')
102 return c - '0' + 52;
103 if (c == '+')
104 return 62;
105 if (c == '/')
106 return 63;
107 if (c == '=')
108 return -2;
109
110 return -1;
111}
112/**
113 * @brief Compare two strings in constant time
114 *
115 * @param a Left string
116 * @param b Right string
117 *
118 * @return 1=Equal, 0=Not equal
119 */
120static int ct_equal(const char* a, const char* b)
121{
122 size_t la = strlen(a);
123 size_t lb = strlen(b);
124 size_t n = (la > lb) ? la : lb;
125
126 unsigned char diff = 0;
127
128 for (size_t i = 0; i < n; i++) {
129 unsigned char ca = (i < la) ? (unsigned char)a[i] : 0;
130 unsigned char cb = (i < lb) ? (unsigned char)b[i] : 0;
131 diff |= (unsigned char)(ca ^ cb);
132 }
133
134 return (diff == 0) && (la == lb);
135}
136
137/**
138 * @brief Find user by name
139 *
140 * @param name Username
141 *
142 * @return User Ptr=Found, NULL=Not found
143 */
144static const struct user* find_user(const char* name)
145{
146 for (size_t i = 0; i < users.len; i++)
147 if (strcmp(users.v[i].name, name) == 0)
148 return &users.v[i];
149
150 return NULL;
151}
152
153/**
154 * @brief Check whether path matches allow prefix
155 *
156 * @param path Relative media path
157 * @param pre Allow prefix
158 *
159 * @return 1=Match, 0=No match
160 */
161static int prefix_ok(const char* path, const char* pre)
162{
163 size_t n = strlen(pre);
164 if (n == 0)
165 return 0;
166
167 if (strcmp(pre, "*") == 0)
168 return 1;
169
170 if (strncmp(path, pre, n) != 0)
171 return 0;
172
173 /* allow match or dir boundary */
174 if (path[n] == '\0')
175 return 1;
176 if (pre[n - 1] == '/')
177 return 1;
178 if (path[n] == '/')
179 return 1;
180
181 return 0;
182}
183
184bool user_allows_path(const struct user* u, const char* relpath)
185{
186 if (!u || !relpath)
187 return false;
188
189 /* no allow list -> allow nothing */
190 if (u->allow_len == 0)
191 return false;
192
193 for (size_t i = 0; i < u->allow_len; i++)
194 if (prefix_ok(relpath, u->allow[i]))
195 return true;
196
197 return false;
198}
199
200int users_add_allow(const char* prefix)
201{
202 if (users.len == 0 || !prefix) {
203 LOG(true, "AUTH", "AddAllow FAILED No user or prefix");
204 return -1;
205 }
206
207 LOG(verbose_log, "AUTH", "Allow directories %s %s", users.v[users.len - 1].name, prefix);
208
209 return allow_push(&users.v[users.len - 1], prefix);
210}
211
212const struct user* users_auth_from_hdr(const char* hdr)
213{
214 if (users.len == 0)
215 return NULL;
216
217 char auth[512];
218 if (hdr_get_value(auth, hdr, "authorization") < 0) {
219 LOG(true, "AUTH", "Authorization Not found");
220 return NULL;
221 }
222
223 /* given "Basic XXXX" */
224 const char* p = auth;
225 while (*p == ' ' || *p == '\t')
226 p++;
227
228 if (strncasecmp(p, "Basic", 5) != 0) {
229 LOG(verbose_log, "AUTH", "Authorization Not Basic");
230 return NULL;
231 }
232
233 p += 5;
234 while (*p == ' ' || *p == '\t')
235 p++;
236
237 if (*p == '\0') {
238 LOG(verbose_log, "AUTH", "Basic Auth Missing token");
239 return NULL;
240 }
241
242 unsigned char dec[512];
243 if (b64_decode(dec, sizeof(dec), p) < 0) {
244 LOG(verbose_log, "AUTH", "Basic Auth B64 decode failed");
245 secure_bzero(dec, sizeof(dec));
246 return NULL;
247 }
248
249 char* sep = strchr((char*)dec, ':');
250 if (!sep) {
251 LOG(verbose_log, "AUTH", "Basic Auth Decoded but missing ':'");
252 secure_bzero(dec, sizeof(dec));
253 return NULL;
254 }
255
256 *sep = '\0';
257 const char* user = (const char*)dec;
258 const char* pass = (const char*)(sep + 1);
259
260 const struct user* u = find_user(user);
261 if (!u) {
262 LOG(true, "AUTH", "Login FAILED Unknown user '%s'", user);
263 secure_bzero(dec, sizeof(dec));
264 return NULL;
265 }
266
267 /* empty password -> passwordless account */
268 if (u->pass[0] == '\0') {
269 if (pass[0] == '\0') {
270 LOG(true, "AUTH", "Login OK %s", user);
271 secure_bzero(dec, sizeof(dec));
272 return u;
273 }
274 LOG(true, "AUTH", "Login FAILED %s", user);
275 secure_bzero(dec, sizeof(dec));
276 return NULL;
277 }
278
279 if (!ct_equal(u->pass, pass)) {
280 LOG(true, "AUTH", "Login FAILED Bad password %s", user);
281 secure_bzero(dec, sizeof(dec));
282 return NULL;
283 }
284
285 LOG(verbose_log, "AUTH", "Login OK %s", user);
286 secure_bzero(dec, sizeof(dec));
287 return u;
288}
289
290void users_free(void)
291{
292 for (size_t i = 0; i < users.len; i++) {
293 for (size_t j = 0; j < users.v[i].allow_len; j++)
294 free(users.v[i].allow[j]);
295 free(users.v[i].allow);
296
297 users.v[i].allow = NULL;
298 users.v[i].allow_len = 0;
299 users.v[i].allow_cap = 0;
300 }
301
302 free(users.v);
303 users.v = NULL;
304 users.len = 0;
305 users.cap = 0;
306}
307
308int users_push(const char* name)
309{
310 if (!name || name[0] == '\0') {
311 LOG(true, "AUTH", "UserAdd FAILED Empty username");
312 return -1;
313 }
314
315 if (users.len == users.cap) {
316 size_t ncap = users.cap ? (users.cap * 2) : 8;
317 struct user* nv = realloc(users.v, ncap * sizeof(*nv));
318 if (!nv) {
319 LOG(true, "AUTH", "realloc FAILED (cap=%zu -> %zu)", users.cap, ncap);
320 return -1;
321 }
322 users.v = nv;
323 users.cap = ncap;
324 }
325
326 memset(&users.v[users.len], 0, sizeof(users.v[users.len]));
327 snprintf(users.v[users.len].name, sizeof(users.v[users.len].name), "%s", name);
328
329 LOG(verbose_log, "AUTH", "UserAdd SUCCESS %s", users.v[users.len].name);
330
331 users.len++;
332 return 0;
333}
334
335int users_set_pass(const char* pass)
336{
337 if (users.len == 0) {
338 LOG(true, "AUTH", "SetPass FAILED No users");
339 return -1;
340 }
341
342 if (!pass)
343 pass = "";
344
345 /* copy pass->user.pass */
346 snprintf(users.v[users.len - 1].pass, sizeof(users.v[users.len - 1].pass), "%s", pass);
347
348 LOG(
349 verbose_log, "AUTH", "Password set for %s (%s)",
350 users.v[users.len - 1].name, (pass[0] == '\0') ? "none" : "set"
351 );
352 return 0;
353}
354