diff options
| author | Franklin Wei <git@fwei.tk> | 2015-12-03 21:25:33 -0500 |
|---|---|---|
| committer | Franklin Wei <git@fwei.tk> | 2015-12-03 21:25:33 -0500 |
| commit | b7541dfc74d2d549210aa0ad73e0536ca4818909 (patch) | |
| tree | fbc2083fe99dffe5085f5861c79a284d48a72171 /src | |
| parent | 1c795424fde9839ab6ebfd49ce7a84ac946be3c0 (diff) | |
| download | netcosm-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.c | 203 | ||||
| -rw-r--r-- | src/client.c | 63 | ||||
| -rw-r--r-- | src/netcosm.h | 8 | ||||
| -rw-r--r-- | src/server.c | 18 |
4 files changed, 264 insertions, 28 deletions
@@ -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 |