AmendHub

Download

jcs

/

subtext

/

user.c

 

(View History)

jcs   user: Update username cache in user_find_by_username if no users Latest amendment: 360 on 2023-03-05

1 /*
2 * Copyright (c) 2021-2022 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <ctype.h>
18 #include <string.h>
19
20 #include "bile.h"
21 #include "db.h"
22 #include "sha2.h"
23 #include "subtext.h"
24 #include "user.h"
25 #include "util.h"
26
27 static const char *BANNED_USERNAMES[] = {
28 "admin", "administrator", "\"admin", "admin1",
29 "client", "contact",
30 "daemon", "default",
31 "enable",
32 "fraud",
33 "guest",
34 "help", "hostmaster",
35 "mailer-daemon",
36 "moderator", "moderators",
37 "nobody", "notme",
38 "postmaster",
39 "root", "\"root",
40 "security", "server", "support", "sventek", "sysop", "system",
41 "test",
42 "ubnt", "unknown",
43 "webmaster",
44 };
45
46 void
47 user_cache_usernames(void)
48 {
49 struct user user;
50 struct username_cache *muser, *new_username_cache;
51 struct bile_object *o;
52 size_t new_nusers, nuser, len;
53
54 new_nusers = bile_count_by_type(db->bile, DB_USER_RTYPE);
55 new_username_cache = xreallocarray(db->username_cache,
56 sizeof(struct username_cache), new_nusers);
57 if (new_username_cache == NULL)
58 return;
59
60 db->nusers = new_nusers;
61 db->username_cache = new_username_cache;
62
63 nuser = 0;
64 while ((o = bile_get_nth_of_type(db->bile, nuser, DB_USER_RTYPE))) {
65 muser = &db->username_cache[nuser];
66 muser->id = o->id;
67
68 len = bile_read(db->bile, DB_USER_RTYPE, o->id, (char *)&user,
69 sizeof(user));
70 if (len == sizeof(user))
71 strncpy(muser->username, user.username, sizeof(muser->username));
72 else {
73 warn("user_update_cache_map: user %lu read size %lu != %lu (%d)",
74 o->id, len, sizeof(user), bile_error(db->bile));
75 memset(muser->username, 0, sizeof(muser->username));
76 }
77
78 xfree(&o);
79 nuser++;
80 }
81 }
82
83 bool
84 user_save(struct user *user)
85 {
86 size_t len;
87
88 if (!user->id) {
89 user->id = bile_next_id(db->bile, DB_USER_RTYPE);
90 if (!user->id)
91 return false;
92 user->created_at = Time;
93 }
94
95 len = bile_write(db->bile, DB_USER_RTYPE, user->id,
96 user, sizeof(struct user));
97 if (len != sizeof(struct user))
98 panic("user_save: failed to write: %d", bile_error(db->bile));
99
100 bile_flush(db->bile, true);
101 return true;
102 }
103
104 struct user *
105 user_find(unsigned long id)
106 {
107 struct user *user;
108 char *data;
109 size_t len;
110
111 len = bile_read_alloc(db->bile, DB_USER_RTYPE, id, &data);
112 if (len == 0 || data == NULL)
113 return NULL;
114 if (len != sizeof(struct user))
115 panic("user_find: bad user record size %lu != %lu", len,
116 sizeof(struct user));
117 user = xmalloczero(sizeof(struct user));
118 if (user != NULL)
119 memcpy(user, data, sizeof(struct user));
120 xfree(&data);
121 return user;
122 }
123
124 struct user *
125 user_find_by_username(const char *username)
126 {
127 struct user suser;
128 struct username_cache *muser;
129 short n;
130 size_t len;
131
132 len = strlen(username);
133 if (len > sizeof(suser.username))
134 return NULL;
135
136 if (db->nusers == 0)
137 user_cache_usernames();
138
139 for (n = 0; n < db->nusers; n++) {
140 muser = &db->username_cache[n];
141
142 if (strcasecmp(muser->username, username) == 0)
143 return user_find(muser->id);
144 }
145
146 return NULL;
147 }
148
149 struct username_cache *
150 user_username(unsigned long id)
151 {
152 struct username_cache *muser;
153 size_t n;
154
155 for (n = 0; n < db->nusers; n++) {
156 muser = &db->username_cache[n];
157 if (muser->id == id)
158 return muser;
159 }
160
161 return NULL;
162 }
163
164 short
165 user_authenticate(struct user *user, const char *password)
166 {
167 char hash[SHA256_DIGEST_STRING_LENGTH];
168 char *salted;
169 size_t plen, slen;
170 short n;
171 unsigned char res;
172
173 plen = strlen(password);
174 slen = SHA256_DIGEST_STRING_LENGTH - 1 + plen;
175 salted = xmalloc(slen);
176 if (salted == NULL)
177 return AUTH_USER_FAILED;
178 memcpy(salted, user->password_salt, SHA256_DIGEST_STRING_LENGTH - 1);
179 memcpy(salted + SHA256_DIGEST_STRING_LENGTH - 1, password, plen);
180
181 SHA256Data((const u_int8_t *)salted, slen, hash);
182
183 /* timing-safe comparison */
184 for (res = 0, n = 0; n < SHA256_DIGEST_STRING_LENGTH; n++)
185 res |= (hash[n] ^ user->password_hash[n]);
186
187 memset(&hash, 0, sizeof(hash));
188 memset(salted, 0, slen);
189 xfree(&salted);
190
191 if (res == 0)
192 return AUTH_USER_OK;
193
194 return AUTH_USER_FAILED;
195 }
196
197 bool
198 user_set_password(struct user *user, const char *password)
199 {
200 char hash[SHA256_DIGEST_STRING_LENGTH];
201 char salt[SHA256_DIGEST_STRING_LENGTH];
202 char *salted;
203 unsigned long tmp[8];
204 size_t plen, slen;
205 short n;
206
207 /* make a random salt out of a sha256 of some random longs */
208 for (n = 0; n < nitems(tmp) - 1; n++)
209 tmp[n] = xorshift32();
210
211 SHA256Data((const u_int8_t *)tmp, sizeof(tmp), salt);
212 memcpy(&user->password_salt, &salt, sizeof(salt));
213
214 plen = strlen(password);
215 slen = plen + sizeof(salt) - 1;
216 salted = xmalloc(slen);
217 if (salted == NULL)
218 return false;
219 memcpy(salted, salt, sizeof(salt) - 1); /* exclude null */
220 memcpy(salted + sizeof(salt) - 1, password, plen);
221 SHA256Data((const u_int8_t *)salted, slen, hash);
222 memset(salted, 0, slen);
223 xfree(&salted);
224
225 memcpy(&user->password_hash, &hash, sizeof(user->password_hash));
226
227 memset(&hash, 0, sizeof(hash));
228 memset(&salt, 0, sizeof(salt));
229 return true;
230 }
231
232 bool
233 user_valid_username(struct user *user, char *username, char **error)
234 {
235 struct user *ouser;
236 unsigned long ouser_id;
237 size_t len, n;
238 char c;
239
240 if (username == NULL) {
241 *error = xstrdup("username cannot be empty");
242 return false;
243 }
244
245 len = strlen(username);
246 if (len > DB_USERNAME_LENGTH) {
247 *error = xmalloc(61);
248 if (*error)
249 snprintf(*error, 60, "username cannot be more than %d "
250 "characters", DB_USERNAME_LENGTH);
251 return false;
252 }
253
254 for (n = 0; n < len; n++) {
255 c = username[n];
256
257 if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') ||
258 (c >= 'a' && c <= 'z') || c == '_')) {
259 *error = xstrdup("username cannot contain non-alphanumeric "
260 "characters");
261 return false;
262 }
263 }
264
265 if (strcasecmp(username, GUEST_USERNAME) == 0) {
266 *error = xstrdup("username is already in use");
267 return false;
268 }
269
270 if (!(user != NULL && user->is_sysop) &&
271 user_username_is_banned(username)) {
272 *error = xstrdup("username is not permitted");
273 return false;
274 }
275
276 if ((ouser = user_find_by_username(username))) {
277 ouser_id = ouser->id;
278 xfree(&ouser);
279
280 if (user == NULL || ouser_id != user->id) {
281 *error = xstrdup("username is already in use");
282 return false;
283 }
284 }
285
286 return true;
287 }
288
289 bool
290 user_username_is_banned(char *username)
291 {
292 size_t n;
293
294 for (n = 0; n < nitems(BANNED_USERNAMES); n++) {
295 if (strcasecmp(username, BANNED_USERNAMES[n]) == 0)
296 return true;
297 }
298
299 return false;
300 }
301
302 char *
303 user_first_sysop_username(void)
304 {
305 struct user user;
306 struct bile_object *o;
307 char *ret = NULL;
308 size_t nuser;
309
310 nuser = 0;
311 while ((o = bile_get_nth_of_type(db->bile, nuser, DB_USER_RTYPE))) {
312 bile_read(db->bile, DB_USER_RTYPE, o->id, (char *)&user,
313 sizeof(user));
314 xfree(&o);
315 if (user.is_sysop) {
316 ret = xstrdup(user.username);
317 break;
318 }
319 nuser++;
320 }
321
322 return ret;
323 }
324
325 void
326 user_change_password(struct session *s, struct user *user)
327 {
328 char *password = NULL, *password_confirm = NULL;
329
330 if (!user) {
331 session_printf(s, "{{B}}Error{{/B}}: Guest accounts "
332 "cannot change passwords\r\n");
333 return;
334 }
335
336 while (!s->ending) {
337 session_printf(s, "{{B}}New Password:{{/B}} ");
338 session_flush(s);
339 password = session_field_input(s, 64, 64, NULL, false, '*');
340 session_output(s, "\r\n", 2);
341 session_flush(s);
342
343 if (password == NULL || s->ending)
344 break;
345
346 if (password[0] == '\0') {
347 session_printf(s, "{{B}}Error:{{/B}} "
348 "Password cannot be blank\r\n");
349 xfree(&password);
350 password = NULL;
351 continue;
352 }
353
354 session_printf(s, "{{B}}New Password (again):{{/B}} ");
355 session_flush(s);
356 password_confirm = session_field_input(s, 64, 64, NULL, false, '*');
357 session_output(s, "\r\n", 2);
358 session_flush(s);
359
360 if (password_confirm == NULL || s->ending)
361 break;
362
363 if (strcmp(password_confirm, password) != 0) {
364 session_printf(s, "{{B}}Error:{{/B}} "
365 "Passwords do not match\r\n");
366 memset(password, 0, strlen(password));
367 xfree(&password);
368 memset(password_confirm, 0, strlen(password_confirm));
369 xfree(&password_confirm);
370 continue;
371 }
372
373 if (!user_set_password(user, password)) {
374 session_logf(s, "Failed saving new password");
375 session_printf(s, "{{B}}Error:{{/B}} "
376 "Failed saving new password\r\n");
377 break;
378 }
379
380 if (!user_save(user)) {
381 session_logf(s, "Failed saving user account");
382 session_printf(s, "{{B}}Error:{{/B}} "
383 "Failed saving user account\r\n");
384 break;
385 }
386
387 if (strcmp(s->user->username, user->username) == 0) {
388 session_logf(s, "User changed password");
389 session_printf(s, "{{B}}Your password has been "
390 "changed{{/B}}\r\n");
391 } else {
392 session_logf(s, "User %s changed password for %s",
393 s->user->username, user->username);
394 session_printf(s, "{{B}}Password has been "
395 "changed{{/B}}\r\n");
396 }
397
398 break;
399 }
400
401 if (password != NULL) {
402 memset(password, 0, strlen(password));
403 xfree(&password);
404 }
405 if (password_confirm != NULL) {
406 memset(password_confirm, 0, strlen(password_confirm));
407 xfree(&password_confirm);
408 }
409 }
410
411 void
412 user_delete(struct user *user)
413 {
414 struct bile_object *o;
415 unsigned long msg_user_id, id;
416 size_t n;
417
418 /* delete the user's mail */
419 n = 0;
420 while ((o = bile_get_nth_of_type(db->mail_bile, n,
421 MAIL_SPOOL_MESSAGE_RTYPE))) {
422 id = o->id;
423 bile_read(db->mail_bile, MAIL_SPOOL_MESSAGE_RTYPE, id,
424 (char *)&msg_user_id, sizeof(msg_user_id));
425 xfree(&o);
426
427 if (msg_user_id != user->id) {
428 n++;
429 continue;
430 }
431
432 bile_delete(db->mail_bile, MAIL_SPOOL_MESSAGE_RTYPE, id,
433 BILE_DELETE_FLAG_ZERO | BILE_DELETE_FLAG_PURGE);
434
435 /* don't increase n, the remaining messages will shift down */
436 }
437
438 /* delete the user */
439 bile_delete(db->bile, DB_USER_RTYPE, user->id,
440 BILE_DELETE_FLAG_ZERO | BILE_DELETE_FLAG_PURGE);
441 }