diff options
| author | Franklin Wei <git@fwei.tk> | 2016-01-02 18:40:29 -0500 |
|---|---|---|
| committer | Franklin Wei <git@fwei.tk> | 2016-01-02 18:40:29 -0500 |
| commit | 2819d11ceeb1ac739ed5f17ccb0abab63f494299 (patch) | |
| tree | 9041b1aa1212df0208f8f49b78101933ce7d4a3a /src | |
| parent | 66cdb3d4f427a1978dad56a66c1bf1085939601c (diff) | |
| download | netcosm-2819d11ceeb1ac739ed5f17ccb0abab63f494299.zip netcosm-2819d11ceeb1ac739ed5f17ccb0abab63f494299.tar.gz netcosm-2819d11ceeb1ac739ed5f17ccb0abab63f494299.tar.bz2 netcosm-2819d11ceeb1ac739ed5f17ccb0abab63f494299.tar.xz | |
preliminary refactor of user data management
Diffstat (limited to 'src')
| -rw-r--r-- | src/auth.c | 41 | ||||
| -rw-r--r-- | src/auth.h | 40 | ||||
| -rw-r--r-- | src/client.c | 18 | ||||
| -rw-r--r-- | src/hash.h | 2 | ||||
| -rw-r--r-- | src/netcosm.h | 75 | ||||
| -rw-r--r-- | src/server.c | 17 | ||||
| -rw-r--r-- | src/telnet.h | 5 | ||||
| -rw-r--r-- | src/test.c | 18 | ||||
| -rw-r--r-- | src/userdb.c | 111 | ||||
| -rw-r--r-- | src/userdb.h | 43 |
10 files changed, 289 insertions, 81 deletions
@@ -18,11 +18,6 @@ #include "netcosm.h" -#define SALT_LEN 12 -#define ALGO GCRY_MD_SHA512 -//#define HASH_ITERS 500000 -#define HASH_ITERS 1 - static bool valid_login_name(const char *name); /* returns a pointer to a malloc-allocated buffer containing the salted hex hash of pass */ @@ -65,7 +60,7 @@ static char *hash_pass_hex(const char *pass, const char *salt) return hex; } -static void add_user_append(int fd, const char *name, const char *pass, int authlevel) +static void add_user_internal(const char *name, const char *pass, int authlevel) { char salt[SALT_LEN + 1]; for(int i = 0; i < SALT_LEN; ++i) @@ -74,16 +69,22 @@ static void add_user_append(int fd, const char *name, const char *pass, int auth } salt[SALT_LEN] = '\0'; - char *hex = hash_pass_hex(pass, salt); + char *hexhash = hash_pass_hex(pass, salt); + + /* doesn't need to be malloc'd */ + struct userdata_t userdata; + + userdata.username = (char*)name; + + memcpy(userdata.passhash, hexhash, sizeof(userdata.passhash)); + + free(hexhash); - /* write */ - flock(fd, LOCK_EX); - if(dprintf(fd, "%s:%s:%s:%d\n", name, salt, hex, authlevel) < 0) - perror("dprintf"); - flock(fd, LOCK_UN); + userdata.priv = authlevel; - close(fd); - free(hex); + memcpy(userdata.salt, salt, sizeof(salt)); + + userdb_add(&userdata); } /* writes the contents of USERFILE to a temp file, and return its path, which is statically allocated */ @@ -108,7 +109,6 @@ static int remove_user_internal(const char *user, int *found, char **filename) size_t buflen = 0; ssize_t len = getline(&line, &buflen, in_fd); - /* getline's return value is the actual length of the line read */ /* it's second argument in fact stores the length of the /buffer/, not the line */ if(len < 0) @@ -181,20 +181,13 @@ bool auth_user_add(const char *user2, const char *pass2, int level) return false; } - /* remove any instances of the user in the file, write to temp file */ - char *tmp; - int out_fd = remove_user_internal(user, NULL, &tmp); - /* add user to end of temp file */ - add_user_append(out_fd, user, pass, level); - close(out_fd); + add_user_internal(user, pass, level); + free(user); memset(pass, 0, strlen(pass)); free(pass); - /* rename temp file -> user list */ - rename(tmp, USERFILE); - return true; } diff --git a/src/auth.h b/src/auth.h new file mode 100644 index 0000000..cd2fc1a --- /dev/null +++ b/src/auth.h @@ -0,0 +1,40 @@ +/* + * NetCosm - a MUD server + * Copyright (C) 2015 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define SALT_LEN 12 +#define ALGO GCRY_MD_SHA512 +#define AUTH_HASHLEN 64 +//#define HASH_ITERS 500000 +#define HASH_ITERS 1 + +struct authinfo_t { + bool success; + const char *user; + int authlevel; +}; + +/* makes admin account */ +void first_run_setup(void); + +struct authinfo_t auth_check(const char *user, const char *pass); + +bool auth_user_add(const char *user, const char *pass, int authlevel); +bool auth_user_del(const char *user); + +/* lists users through out() */ +void auth_user_list(void); diff --git a/src/client.c b/src/client.c index 2784987..8591858 100644 --- a/src/client.c +++ b/src/client.c @@ -172,7 +172,9 @@ void sig_rt_0_handler(int s, siginfo_t *info, void *v) } unsigned char cmd; + /* drop this command */ read(from_parent, &cmd, 1); + switch(cmd) { case REQ_BCASTMSG: @@ -211,6 +213,18 @@ void sig_rt_0_handler(int s, siginfo_t *info, void *v) sigqueue(getppid(), SIGRTMIN+1, junk); } +static void sigpipe_handler(int s) +{ + (void) s; + union sigval junk; + /* + * necessary in case we get SIGPIPE in our SIGRTMIN+1 handler, + * the master expects a response from us + */ + sigqueue(getppid(), SIGRTMIN+1, junk); + _exit(0); +} + static void client_change_state(int state) { send_master(REQ_CHANGESTATE, &state, sizeof(state)); @@ -289,7 +303,9 @@ void client_main(int fd, struct sockaddr_in *addr, int total, int to, int from) output_locked = 0; struct sigaction sa; - sa.sa_handler = SIG_DFL; + sigemptyset(&sa.sa_mask); + sa.sa_handler = sigpipe_handler; + sa.sa_flags = SA_RESTART; if(sigaction(SIGPIPE, &sa, NULL) < 0) error("sigaction"); @@ -39,7 +39,7 @@ void hash_setfreekey_cb(void*, void (*cb)(void *key)); void hash_free(void*); /* - * insert a pair, returns null if not already found, otherwise returns + * insert a pair, returns NULL if not already found, otherwise returns * the existing data pointer */ void *hash_insert(void*, const void *key, const void *data); diff --git a/src/netcosm.h b/src/netcosm.h index 3e056d2..c6acf44 100644 --- a/src/netcosm.h +++ b/src/netcosm.h @@ -18,6 +18,9 @@ #ifndef _NC_H_ #define _NC_H_ + +#define _GNU_SOURCE /* for pipe2() */ + #include <arpa/inet.h> #include <ctype.h> #include <errno.h> @@ -44,6 +47,7 @@ #include "auth.h" #include "hash.h" #include "telnet.h" +#include "userdb.h" #define USERFILE "users.dat" #define WORLDFILE "world.dat" @@ -96,8 +100,6 @@ #define MSG_MAX 512 -#define ROOM_NONE -1 - #define ARRAYLEN(x) (sizeof(x)/sizeof(x[0])) #define MAX(a,b) ((a>b)?(a):(b)) #define MIN(a,b) ((a<b)?(a):(b)) @@ -110,40 +112,12 @@ #define sig_debugf debugf #endif -typedef int room_id; +#define ROOM_NONE -1 -/* used by the room module to keep track of users in rooms */ -struct user_t { - struct child_data *data; - struct user_t *next; -}; +typedef int room_id; enum direction_t { DIR_N = 0, DIR_NE, DIR_E, DIR_SE, DIR_S, DIR_SW, DIR_W, DIR_NW, DIR_UP, DIR_DN, DIR_IN, DIR_OT, NUM_DIRECTIONS }; -struct object_t { - const char *class; - -}; - -struct verb_t { - const char *name; - - /* toks is strtok_r's pointer */ - void (*execute)(const char *toks); -}; - -struct child_data { - pid_t pid; - int readpipe[2]; - int outpipe[2]; - - int state; - room_id room; - char *user; - - struct in_addr addr; -}; - /* the data we get from a world module */ struct roomdata_t { /* the non-const pointers can be modified by the world module */ @@ -179,6 +153,36 @@ struct room_t { int num_users; }; +/* used by the room module to keep track of users in rooms */ +struct user_t { + struct child_data *data; + struct user_t *next; +}; + +struct object_t { + const char *class; + +}; + +struct verb_t { + const char *name; + + /* toks is strtok_r's pointer */ + void (*execute)(const char *toks); +}; + +struct child_data { + pid_t pid; + int readpipe[2]; + int outpipe[2]; + + int state; + room_id room; + char *user; + + struct in_addr addr; +}; + extern const struct roomdata_t netcosm_world[]; extern const size_t netcosm_world_sz; extern const char *netcosm_world_name; @@ -189,11 +193,7 @@ void client_main(int sock, struct sockaddr_in *addr, int, int to_parent, int fro void out(const char *fmt, ...) __attribute__((format(printf,1,2))); void out_raw(const unsigned char*, size_t); -void telnet_init(void); -void telnet_handle_command(const unsigned char*); -void telnet_echo_on(void); -void telnet_echo_off(void); - +/* room/world */ void world_init(const struct roomdata_t *data, size_t sz, const char *name); bool world_load(const char *fname, const struct roomdata_t *data, size_t data_sz, const char *world_name); void world_save(const char *fname); @@ -208,4 +208,5 @@ void world_free(void); void __attribute__((noreturn,format(printf,1,2))) error(const char *fmt, ...); void debugf_real(const char *fmt, ...); void remove_cruft(char*); + #endif diff --git a/src/server.c b/src/server.c index da26279..e24c9f7 100644 --- a/src/server.c +++ b/src/server.c @@ -309,12 +309,6 @@ static void reqmap_init(void) hash_insert(request_map, &requests[i].code, requests + i); } -static void empty_pipe(int fd) -{ - char buf[4096]; - read(fd, buf, sizeof(buf)); -} - /** * Here's how child-parent requests work * 1. Child writes its PID and length of request to the parent's pipe, followed @@ -471,7 +465,6 @@ finish: return true; fail: - empty_pipe(in_fd); return true; } @@ -519,10 +512,12 @@ void init_signals(void) if(sigaction(SIGPIPE, &sa, NULL) < 0) error("sigaction"); - /* set this now so there's no race condition later */ + void sig_rt_0_handler(int s, siginfo_t *info, void *v); + + /* we set this now so there's no race condition after a fork() */ sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGRTMIN); - void sig_rt_0_handler(int s, siginfo_t *info, void *v); + sigaddset(&sa.sa_mask, SIGPIPE); sa.sa_sigaction = sig_rt_0_handler; sa.sa_flags = SA_RESTART | SA_SIGINFO; if(sigaction(SIGRTMIN, &sa, NULL) < 0) @@ -540,7 +535,7 @@ static void check_userfile(void) static void load_worldfile(void) { - if(access(WORLDFILE, F_OK) < 0) + if(access(WORLDFILE, F_OK) < 0 || userdb_size() == 0) { world_init(netcosm_world, netcosm_world_sz, netcosm_world_name); @@ -598,6 +593,8 @@ int main(int argc, char *argv[]) /* SIGRTMIN+0 is used for broadcast signaling */ init_signals(); + userdb_init(USERFILE); + check_userfile(); load_worldfile(); diff --git a/src/telnet.h b/src/telnet.h index 91364dc..1ca7b4c 100644 --- a/src/telnet.h +++ b/src/telnet.h @@ -37,3 +37,8 @@ #define STATUS 5 #define NAWS 31 #define LINEMODE 34 + +void telnet_init(void); +void telnet_handle_command(const unsigned char*); +void telnet_echo_on(void); +void telnet_echo_off(void); @@ -7,13 +7,15 @@ int main() void *map = hash_init(10000, hash_djb, compare_strings); hash_insert(map, "a",1); hash_insert(map, "b",2); - void *ptr = map; - void *data = NULL; - void *save; - do { - char *key; - data = hash_iterate(ptr, &save, &key); + hash_resize(map, 2); + void *ptr = map, *save, *key; + while(1) + { + void *data = hash_iterate(ptr, &save, &key); ptr = NULL; - printf("%d %s\n", data, key); - } while(data); + if(data) + printf("%s %d\n", key, data); + else + break; + } } diff --git a/src/userdb.c b/src/userdb.c new file mode 100644 index 0000000..4315da4 --- /dev/null +++ b/src/userdb.c @@ -0,0 +1,111 @@ +#include "netcosm.h" + +static void *map = NULL; +static char *db_file = NULL; +static size_t entries = 0; + +#define DELIM ":" + +/* + * the user DB is stored on disk as an ASCII database + * + * this is then loaded into variable-sized hash map at init + * TODO: implement with B-tree + */ +void userdb_init(const char *file) +{ + db_file = strdup(file); + entries = 0; + + /* FILE* for getline() */ + FILE *f = fopen(file, "r"); + map = hash_init(256, hash_djb, compare_strings); + hash_setfreekey_cb(map, free); + hash_setfreedata_cb(map, free); + + char *format; + asprintf(&format, "%%ms:%%%d%%s:%%%d%%s:%%d\n", + SALT_LEN, AUTH_HASHLEN * 2); + + if(f) + { + while(1) + { + struct userdata_t *data = calloc(1, sizeof(*data)); + + if(fscanf(f, format, + &data->username, + data->salt, + data->passhash, + &data->priv) != 6) + break; + + hash_insert(map, data->username, data); + ++entries; + } + + fclose(f); + } + + free(format); +} + +void userdb_write(const char *file) +{ + int fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0600); + void *save, *ptr = map; + while(1) + { + struct userdata_t *user = hash_iterate(ptr, &save, NULL); + ptr = NULL; + if(!user) + break; + dprintf(fd, "%s:%*s:%*s:%d\n", user->username, + SALT_LEN, user->salt, + AUTH_HASHLEN*2, user->passhash, + user->priv); + } + close(fd); +} + +struct userdata_t *userdb_lookup(const char *key) +{ + return hash_lookup(map, key); +} + +void userdb_remove(const char *key) +{ + if(hash_remove(map, key)) + --entries; +} + +struct userdata_t *userdb_add(struct userdata_t *data) +{ + struct userdata_t *new = calloc(1, sizeof(*new)); /* only in C! */ + memcpy(new, data, sizeof(*new)); + new->username = strdup(data->username); + struct userdata_t *ret; + if((ret = hash_insert(map, new->username, new))) /* failure */ + { + hash_remove(map, new->username); + ret = hash_insert(map, new->username, new); + } + + userdb_write(db_file); + if(!ret) + --entries; + + return ret; +} + +void userdb_shutdown(void) +{ + hash_free(map); + if(db_file) + free(db_file); +} + +size_t userdb_size(void) +{ + return entries; +} diff --git a/src/userdb.h b/src/userdb.h new file mode 100644 index 0000000..89e2566 --- /dev/null +++ b/src/userdb.h @@ -0,0 +1,43 @@ +#include "netcosm.h" + +/* + * on-disk database for storing user data + * + * child processes MUST go through the master to use this + */ + +struct userdata_t { + char *username; + + char salt[SALT_LEN + 1]; + + /* in hex + NULL terminator */ + char passhash[AUTH_HASHLEN * 2 + 1]; + + int priv; +}; + +/* call before using anything else */ +void userdb_init(const char *dbfile); + +/* looks up a username in the DB, returns NULL upon failure */ +struct userdata_t *userdb_lookup(const char *username); + +void userdb_remove(const char *username); + +/* is it empty? */ +size_t userdb_size(void); + +/* + * adds an entry to the DB + * if it already exists, OVERWRITE + * returns a pointer to the added entry, NULL on failure + * + * a DUPLICATE of the entry will be inserted + */ +struct userdata_t *userdb_add(struct userdata_t*); + +void userdb_shutdown(void); + +/* save the DB to disk */ +void userdb_write(const char*); |