master uint/parados / server / users.c
  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