aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFranklin Wei <git@fwei.tk>2016-01-02 18:40:29 -0500
committerFranklin Wei <git@fwei.tk>2016-01-02 18:40:29 -0500
commit2819d11ceeb1ac739ed5f17ccb0abab63f494299 (patch)
tree9041b1aa1212df0208f8f49b78101933ce7d4a3a /src
parent66cdb3d4f427a1978dad56a66c1bf1085939601c (diff)
downloadnetcosm-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.c41
-rw-r--r--src/auth.h40
-rw-r--r--src/client.c18
-rw-r--r--src/hash.h2
-rw-r--r--src/netcosm.h75
-rw-r--r--src/server.c17
-rw-r--r--src/telnet.h5
-rw-r--r--src/test.c18
-rw-r--r--src/userdb.c111
-rw-r--r--src/userdb.h43
10 files changed, 289 insertions, 81 deletions
diff --git a/src/auth.c b/src/auth.c
index 5027e5f..c0a6a46 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -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");
diff --git a/src/hash.h b/src/hash.h
index 28a5352..5eb8135 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -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);
diff --git a/src/test.c b/src/test.c
index 95129e5..b194352 100644
--- a/src/test.c
+++ b/src/test.c
@@ -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*);