AmendHub

Download

jcs

/

subtext

/

user.c

 

(View History)

jcs   user: Add user_first_sysop_id() Latest amendment: 562 on 2023-11-27

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 unsigned long
303 user_first_sysop_id(void)
304 {
305 struct user user;
306 struct bile_object *o;
307 size_t nuser;
308
309 nuser = 0;
310 while ((o = bile_get_nth_of_type(db->bile, nuser, DB_USER_RTYPE))) {
311 bile_read(db->bile, DB_USER_RTYPE, o->id, (char *)&user,
312 sizeof(user));
313 xfree(&o);
314 if (user.is_sysop)
315 return user.id;
316 nuser++;
317 }
318
319 return 0;
320 }
321
322 void
323 user_change_password(struct session *s, struct user *user)
324 {
325 char *password = NULL, *password_confirm = NULL;
326
327 if (!user) {
328 session_printf(s, "{{B}}Error{{/B}}: Guest accounts "
329 "cannot change passwords\r\n");
330 return;
331 }
332
333 while (!s->ending) {
334 session_printf(s, "{{B}}New Password:{{/B}} ");
335 session_flush(s);
336 password = session_field_input(s, 64, 64, NULL, false, '*');
337 session_output(s, "\r\n", 2);
338 session_flush(s);
339
340 if (password == NULL || s->ending)
341 break;
342
343 if (password[0] == '\0') {
344 session_printf(s, "{{B}}Error:{{/B}} "
345 "Password cannot be blank\r\n");
346 xfree(&password);
347 password = NULL;
348 continue;
349 }
350
351 session_printf(s, "{{B}}New Password (again):{{/B}} ");
352 session_flush(s);
353 password_confirm = session_field_input(s, 64, 64, NULL, false, '*');
354 session_output(s, "\r\n", 2);
355 session_flush(s);
356
357 if (password_confirm == NULL || s->ending)
358 break;
359
360 if (strcmp(password_confirm, password) != 0) {
361 session_printf(s, "{{B}}Error:{{/B}} "
362 "Passwords do not match\r\n");
363 memset(password, 0, strlen(password));
364 xfree(&password);
365 memset(password_confirm, 0, strlen(password_confirm));
366 xfree(&password_confirm);
367 continue;
368 }
369
370 if (!user_set_password(user, password)) {
371 session_logf(s, "Failed saving new password");
372 session_printf(s, "{{B}}Error:{{/B}} "
373 "Failed saving new password\r\n");
374 break;
375 }
376
377 if (!user_save(user)) {
378 session_logf(s, "Failed saving user account");
379 session_printf(s, "{{B}}Error:{{/B}} "
380 "Failed saving user account\r\n");
381 break;
382 }
383
384 if (strcmp(s->user->username, user->username) == 0) {
385 session_logf(s, "User changed password");
386 session_printf(s, "{{B}}Your password has been "
387 "changed{{/B}}\r\n");
388 } else {
389 session_logf(s, "User %s changed password for %s",
390 s->user->username, user->username);
391 session_printf(s, "{{B}}Password has been "
392 "changed{{/B}}\r\n");
393 }
394
395 break;
396 }
397
398 if (password != NULL) {
399 memset(password, 0, strlen(password));
400 xfree(&password);
401 }
402 if (password_confirm != NULL) {
403 memset(password_confirm, 0, strlen(password_confirm));
404 xfree(&password_confirm);
405 }
406 }
407
408 void
409 user_delete(struct user *user)
410 {
411 struct bile_object *o;
412 unsigned long msg_user_id, id;
413 size_t n;
414
415 /* delete the user's mail */
416 n = 0;
417 while ((o = bile_get_nth_of_type(db->mail_bile, n,
418 MAIL_SPOOL_MESSAGE_RTYPE))) {
419 id = o->id;
420 bile_read(db->mail_bile, MAIL_SPOOL_MESSAGE_RTYPE, id,
421 (char *)&msg_user_id, sizeof(msg_user_id));
422 xfree(&o);
423
424 if (msg_user_id != user->id) {
425 n++;
426 continue;
427 }
428
429 bile_delete(db->mail_bile, MAIL_SPOOL_MESSAGE_RTYPE, id,
430 BILE_DELETE_FLAG_ZERO | BILE_DELETE_FLAG_PURGE);
431
432 /* don't increase n, the remaining messages will shift down */
433 }
434
435 /* delete the user */
436 bile_delete(db->bile, DB_USER_RTYPE, user->id,
437 BILE_DELETE_FLAG_ZERO | BILE_DELETE_FLAG_PURGE);
438 }