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