aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFranklin Wei <git@fwei.tk>2015-12-03 21:25:33 -0500
committerFranklin Wei <git@fwei.tk>2015-12-03 21:25:33 -0500
commitb7541dfc74d2d549210aa0ad73e0536ca4818909 (patch)
treefbc2083fe99dffe5085f5861c79a284d48a72171 /src
parent1c795424fde9839ab6ebfd49ce7a84ac946be3c0 (diff)
downloadnetcosm-b7541dfc74d2d549210aa0ad73e0536ca4818909.zip
netcosm-b7541dfc74d2d549210aa0ad73e0536ca4818909.tar.gz
netcosm-b7541dfc74d2d549210aa0ad73e0536ca4818909.tar.bz2
netcosm-b7541dfc74d2d549210aa0ad73e0536ca4818909.tar.xz
better auth
Diffstat (limited to 'src')
-rw-r--r--src/auth.c203
-rw-r--r--src/client.c63
-rw-r--r--src/netcosm.h8
-rw-r--r--src/server.c18
4 files changed, 264 insertions, 28 deletions
diff --git a/src/auth.c b/src/auth.c
index 55fe083..53ae4f6 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -1,5 +1,9 @@
#include "netcosm.h"
+#define SALT_LEN 8
+
+#define ALGO GCRY_MD_SHA1
+
/* add a user to the on-disk database */
/*
@@ -7,23 +11,106 @@
* [login]:[hash]\n
*/
-void add_user(const char *name, const char *pass)
+static void add_user_append(int fd, const char *name, const char *pass, int authlevel)
{
- int fd = open(USERFILE, O_WRONLY | O_CREAT | O_APPEND, 0600);
+ size_t pass_len = strlen(pass);
+
+ /* salt */
+ char *salted = malloc(pass_len + SALT_LEN + 1);
+ char salt[SALT_LEN + 1];
+ for(int i = 0; i < SALT_LEN; ++i)
+ {
+ salted[i] = salt[i] = 'A' + rand()%26;
+ }
+ salt[SALT_LEN] = '\0';
+
+ memcpy(salted + SALT_LEN, pass, pass_len);
+ salted[pass_len + SALT_LEN] = '\0';
+
+ printf("hashing %s\n", salted);
- int hash_len = gcry_md_get_algo_dlen(GCRY_MD_SHA512);
+ int hash_len = gcry_md_get_algo_dlen(ALGO);
unsigned char *hash = malloc(hash_len);
- gcry_md_hash_buffer(GCRY_MD_SHA512, hash, pass, strlen(pass));
+ gcry_md_hash_buffer(ALGO, hash, salted, pass_len + SALT_LEN);
+ free(salted);
+
+ /* convert to hex */
char *hex = malloc(hash_len * 2 + 1);
char *ptr = hex;
- for(int i = 0; i < hash_len; ++i, ++ptr)
+ for(int i = 0; i < hash_len; ++i, ptr += 2)
snprintf(ptr, 3, "%02x", hash[i]);
+ free(hash);
+
+ /* write */
+
flock(fd, LOCK_EX);
- dprintf(fd, "%s:%s\n", name, hex);
+ dprintf(fd, "%s:%s:%s:%d\n", name, salt, hex, authlevel);
+
+ free(hex);
flock(fd, LOCK_UN);
+ close(fd);
+}
+
+void add_user(const char *name2, const char *pass2, int level)
+{
+ char *name = strdup(name2);
+ strtok(name, "\r\n");
+ char *pass = strdup(pass2);
+ strtok(pass, "\r\n");
+
+ /* remove any instances of the user in the file, write to temp file */
+
+ FILE *in_fd = fopen(USERFILE, "w+");
+ flock(fileno(in_fd), LOCK_SH);
+ char *tmp = tmpnam(NULL);
+ int out_fd = open(tmp, O_CREAT | O_WRONLY, 0600);
+ while(1)
+ {
+ char *line = NULL;
+ size_t len = 0;
+ if(getline(&line, &len, in_fd) < 0)
+ break;
+ if(strcmp(strtok(line, ":\r\n"), name) != 0)
+ write(out_fd, line, len);
+ }
+ flock(fileno(in_fd), LOCK_UN);
+ fclose(in_fd);
+
+ /* add user to end of temp file */
+
+ add_user_append(out_fd, name, pass, level);
+ close(out_fd);
+
+ /* rename temp file -> user list */
+ int fd = open(tmp, O_RDONLY);
+ int userfile = open(USERFILE, O_WRONLY | O_TRUNC | O_CREAT, 0600);
+
+ ssize_t nread;
+ char buf[1024];
+ while (nread = read(fd, buf, sizeof buf), nread > 0)
+ {
+ printf("writing %d bytes\n", nread);
+ char *out_ptr = buf;
+ ssize_t nwritten;
+
+ do {
+ nwritten = write(userfile, out_ptr, nread);
+
+ if (nwritten >= 0)
+ {
+ nread -= nwritten;
+ out_ptr += nwritten;
+ }
+ else
+ break;
+ } while (nread > 0);
+ }
+
+ close(userfile);
+ remove(tmp);
}
bool valid_login_name(const char *name)
@@ -33,7 +120,11 @@ bool valid_login_name(const char *name)
char c = *name++;
switch(c)
{
+ case ' ':
case ':':
+ case '\n':
+ case '\r':
+ case '\t':
return false;
default:
break;
@@ -54,15 +145,109 @@ void first_run_setup(void)
printf("Admin account name: ");
fflush(stdout);
getline(&admin_name, &len, stdin);
+ strtok(admin_name, "\r\n");
} while(!valid_login_name(admin_name));
- printf("Admin password (DO NOT USE A VALUABLE PASSWORD): ");
+ printf("Admin password (_DO_NOT_ USE A VALUABLE PASSWORD): ");
fflush(stdout);
char *admin_pass = NULL;
len = 0;
getline(&admin_pass, &len, stdin);
+ strtok(admin_pass, "\r\n");
+ add_user(admin_name, admin_pass, 0);
+
+ /* zero the memory */
+ memset(admin_name, 0, strlen(admin_name));
+ memset(admin_pass, 0, strlen(admin_pass));
+ free(admin_name);
+ free(admin_pass);
+}
+
+struct authinfo_t auth_check(const char *name2, const char *pass2)
+{
+ /* get our own copy to remove newlines */
+ char *name = strdup(name2);
+ char *pass = strdup(pass2);
+ strtok(name, "\r\n");
+ strtok(pass, "\r\n");
+
+ /* find it in the user list */
+
+ FILE *f = fopen(USERFILE, "r");
+
+ flock(fileno(f), LOCK_SH);
+
+ struct authinfo_t ret;
+
+ while(1)
+ {
+ char *line = NULL;
+ size_t len = 0;
+ if(getline(&line, &len, f) < 0)
+ {
+ free(line);
+ goto bad;
+ }
+ if(!strcmp(strtok(line, ":\r\n"), name))
+ {
+ free(name);
+ size_t pass_len = strlen(pass);
+
+ char *salt = strdup(strtok(NULL, ":\r\n"));
+ char *hash = strdup(strtok(NULL, ":\r\n"));
+
+ ret.authlevel = strtol(strtok(NULL, ":\r\n"), NULL, 10);
+
+ free(line);
+
+ int hash_len = gcry_md_get_algo_dlen(ALGO);
+
+ if(strlen(hash) != hash_len * 2)
+ error("hash corrupt %d %d", strlen(hash), hash_len * 2);
+ if(strlen(salt) != SALT_LEN)
+ error("salt corrupt");
+
+ char *buf = malloc(pass_len + SALT_LEN + 1);
+ memcpy(buf, salt, strlen(salt));
+ memcpy(buf + SALT_LEN, pass, pass_len);
+ buf[pass_len + SALT_LEN] = '\0';
- /* hash and store */
+ free(salt);
+ free(pass);
- add_user(admin_name, admin_pass);
+ unsigned char *newhash = malloc(hash_len);
+
+ gcry_md_hash_buffer(ALGO, newhash, buf, pass_len + SALT_LEN);
+ free(buf);
+ char *hex = malloc(hash_len * 2 + 1);
+
+ char *ptr = hex;
+ for(int i = 0; i < hash_len; ++i, ptr += 2)
+ snprintf(ptr, 3, "%02x", newhash[i]);
+
+ free(newhash);
+
+ if(!memcmp(hex, hash, hash_len * 2))
+ {
+ free(hex);
+ free(hash);
+ ret.success = true;
+ goto good;
+ }
+ else
+ {
+ free(hex);
+ free(hash);
+ ret.success = false;
+ goto bad;
+ }
+ }
+ }
+good:
+ printf("Successful authentication.\n");
+ return ret;
+bad:
+ printf("Failed authentication.\n");
+ sleep(1);
+ return ret;
}
diff --git a/src/client.c b/src/client.c
index e907172..a3cc191 100644
--- a/src/client.c
+++ b/src/client.c
@@ -13,15 +13,15 @@ void __attribute__((format(printf,1,2))) out(const char *fmt, ...)
write(client_fd, buf, sizeof(buf));
}
+#define BUFSZ 128
+
char *client_read(void)
{
- static char buf[128];
- if(read(client_fd, buf, sizeof(buf) - 1) < 0)
- {
+ char *buf = malloc(BUFSZ);
+ memset(buf, 0, BUFSZ);
+ if(read(client_fd, buf, BUFSZ - 1) < 0)
error("lost connection");
- exit(EXIT_FAILURE);
- }
- buf[sizeof(buf) - 1] = '\0';
+ buf[BUFSZ - 1] = '\0';
const unsigned char ctrlc[] = { 0xff, 0xf4, 0xff, 0xfd, 0x06 };
if(!memcmp(buf, ctrlc, sizeof(ctrlc)))
@@ -34,14 +34,55 @@ void client_main(int fd, struct sockaddr_in *addr, int total)
{
client_fd = fd;
- bool admin = false;
-
char *ip = inet_ntoa(addr->sin_addr);
printf("New client %s\n", ip);
printf("Total clients: %d\n", total);
out("Hello %s.\n", ip);
- out("Please authenticate to continue.\n");
- out("login: ");
- char *input = client_read();
+ out("Please authenticate to continue.\n\n");
+
+ int failures = 0;
+
+ int authlevel;
+
+ /* auth loop */
+ while(1)
+ {
+ out("NetCosm login: ");
+ char *user = client_read();
+ out("Password: ");
+ char *pass = client_read();
+ printf("pass is %s\n", pass);
+ struct authinfo_t auth = auth_check(user, pass);
+ free(user);
+ free(pass);
+ authlevel = auth.authlevel;
+ if(auth.success)
+ {
+ out("Access Granted.\n\n");
+ break;
+ }
+ else
+ {
+ out("Access Denied.\n\n");
+ if(++failures >= MAX_FAILURES)
+ return;
+ }
+ }
+
+ /* authenticated */
+ while(1)
+ {
+ out(">> ");
+ char *cmd = client_read();
+ char *tok = strtok(cmd, " \t\r\n");
+
+ if(!strcmp(tok, "USER"))
+ {
+ void change_user(const char *name2, const char *pass2, int level);
+ add_user("admin", "test", 0);
+ }
+
+ free(cmd);
+ }
}
diff --git a/src/netcosm.h b/src/netcosm.h
index e8a1384..86d38b9 100644
--- a/src/netcosm.h
+++ b/src/netcosm.h
@@ -17,7 +17,15 @@
#include <unistd.h>
#define USERFILE "users.dat"
+#define MAX_FAILURES 3
+
+struct authinfo_t {
+ bool success;
+ const char *user;
+ int authlevel; /* 0 = highest */
+};
void client_main(int fd, struct sockaddr_in *addr, int);
void __attribute__((noreturn)) error(const char *fmt, ...);
void first_run_setup(void);
+struct authinfo_t auth_check(const char*, const char*);
diff --git a/src/server.c b/src/server.c
index e727d19..6089317 100644
--- a/src/server.c
+++ b/src/server.c
@@ -18,7 +18,7 @@
#include "netcosm.h"
-#define PORT 1234
+#define PORT 1333
#define BACKLOG 16
void __attribute__((noreturn)) error(const char *fmt, ...)
@@ -33,6 +33,8 @@ void __attribute__((noreturn)) error(const char *fmt, ...)
exit(EXIT_FAILURE);
}
+int num_clients = 0;
+
void sigchld_handler(int s)
{
// waitpid() might overwrite errno, so we save and restore it:
@@ -41,6 +43,8 @@ void sigchld_handler(int s)
while(waitpid(-1, NULL, WNOHANG) > 0);
errno = saved_errno;
+
+ --num_clients;
}
int port;
@@ -68,6 +72,7 @@ void sigint_handler(int sig)
int main(int argc, char *argv[])
{
port = PORT;
+ srand(time(0));
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock<0)
@@ -104,8 +109,6 @@ int main(int argc, char *argv[])
signal(SIGINT, sigint_handler);
- int num_clients = 0;
-
if(access(USERFILE, F_OK) < 0)
first_run_setup();
@@ -114,26 +117,25 @@ int main(int argc, char *argv[])
while(1)
{
struct sockaddr_in client;
- socklen_t client_len;
+ socklen_t client_len = sizeof(client);
int new_sock = accept(sock, (struct sockaddr*) &client, &client_len);
if(new_sock < 0)
error("accept");
+ ++num_clients;
+
pid_t pid = fork();
if(pid < 0)
error("fork");
+
if(!pid)
{
close(sock);
server_socket = new_sock;
- ++num_clients;
-
handle_client(new_sock, &client, num_clients);
- --num_clients;
-
exit(0);
}
else