From 9df8091c0a30276b181796993b883f7e3b8f609a Mon Sep 17 00:00:00 2001 From: Franklin Wei Date: Sat, 5 Dec 2015 11:42:14 -0500 Subject: fix auth again --- src/auth.c | 170 ++++++++++++++++++++++++++++++++++++++-------------------- src/client.c | 115 +++++++++++++++++++++++++++++++++++---- src/netcosm.h | 10 ++++ src/server.c | 2 + 4 files changed, 230 insertions(+), 67 deletions(-) (limited to 'src') diff --git a/src/auth.c b/src/auth.c index f47aee8..4bcb6b5 100644 --- a/src/auth.c +++ b/src/auth.c @@ -8,11 +8,15 @@ /* * format: - * [login]:[hash]\n + * { [username]:[salt]:[hash]:[authlevel]\n } [N] */ +static bool valid_login_name(const char *name); + static void add_user_append(int fd, const char *name, const char *pass, int authlevel) { + if(errno < 0) + perror("unknown"); size_t pass_len = strlen(pass); /* salt */ @@ -27,9 +31,7 @@ static void add_user_append(int fd, const char *name, const char *pass, int auth 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(ALGO); + unsigned int hash_len = gcry_md_get_algo_dlen(ALGO); unsigned char *hash = malloc(hash_len); gcry_md_hash_buffer(ALGO, hash, salted, pass_len + SALT_LEN); free(salted); @@ -37,83 +39,131 @@ static void add_user_append(int fd, const char *name, const char *pass, int auth /* convert to hex */ char *hex = malloc(hash_len * 2 + 1); char *ptr = hex; - for(int i = 0; i < hash_len; ++i, ptr += 2) + for(unsigned 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:%s:%d\n", name, salt, hex, authlevel); - - free(hex); - + if(dprintf(fd, "%s:%s:%s:%d\n", name, salt, hex, authlevel) < 0) + perror("dprintf"); + printf("writing %s:%s:%s:%d\n", name, salt, hex, authlevel); flock(fd, LOCK_UN); + close(fd); + free(hex); + perror("add_user_append"); } -void add_user(const char *name2, const char *pass2, int level) +/* writes the contents of USERFILE to a temp file, and return its path, which is statically allocated */ +static char *remove_user_internal(const char *user, int *found) { - 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, "a+"); + static char tmp[] = "userlist_tmp.XXXXXX"; + int out_fd = mkstemp(tmp); + if(found) + *found = 0; - 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) + char *junk; + 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) + { + free(line); 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 */ + char *old = strdup(line); + + char *user_on_line = strtok_r(line, ":\r\n", &junk); + + if(strcmp(user_on_line, user) != 0) + { + write(out_fd, old, len); + } + else + if(found) + (*found)++; + free(line); + free(old); + } - add_user_append(out_fd, name, pass, level); close(out_fd); + fclose(in_fd); - /* rename temp file -> user list */ - int fd = open(tmp, O_RDONLY); - int userfile = open(USERFILE, O_WRONLY | O_TRUNC | O_CREAT, 0600); + return tmp; +} - ssize_t nread; - char buf[1024]; - while (nread = read(fd, buf, sizeof buf), nread > 0) +bool auth_remove(const char *user2) +{ + char *user = strdup(user2); + strtok(user, "\r\n"); + if(valid_login_name(user)) + { + int found = 0; + char *tmp = remove_user_internal(user, &found); + free(user); + if(found) + { + rename(tmp, USERFILE); + return true; + } + else + { + remove(tmp); + return false; + } + } + else { - printf("writing %d bytes\n", nread); - char *out_ptr = buf; - ssize_t nwritten; + free(user); + return false; + } +} - do { - nwritten = write(userfile, out_ptr, nread); +bool add_change_user(const char *user2, const char *pass2, int level) +{ + char *user = strdup(user2); + strtok(user, "\r\n"); + char *pass = strdup(pass2); + strtok(pass, "\r\n"); - if (nwritten >= 0) - { - nread -= nwritten; - out_ptr += nwritten; - } - else - break; - } while (nread > 0); + printf("Add user '%s'\n", user); + + if(!valid_login_name(user)) + { + free(user); + free(pass); + return false; } - close(userfile); - remove(tmp); + /* remove any instances of the user in the file, write to temp file */ + char *tmp = remove_user_internal(user, NULL); + + printf("point 0\n"); + + /* add user to end of temp file */ + int out_fd = open(tmp, O_WRONLY | O_APPEND); + add_user_append(out_fd, user, pass, level); + printf("point 1\n"); + close(out_fd); + printf("point 2\n"); + + /* rename temp file -> user list */ + rename(tmp, USERFILE); + + return true; } -bool valid_login_name(const char *name) +static bool valid_login_name(const char *name) { while(*name) { @@ -154,7 +204,9 @@ void first_run_setup(void) len = 0; getline(&admin_pass, &len, stdin); strtok(admin_pass, "\r\n"); - add_user(admin_name, admin_pass, 0); + + if(!add_change_user(admin_name, admin_pass, PRIV_ADMIN)) + error("Unknown error"); /* zero the memory */ memset(admin_name, 0, strlen(admin_name)); @@ -165,6 +217,8 @@ void first_run_setup(void) struct authinfo_t auth_check(const char *name2, const char *pass2) { + sleep(1); + /* get our own copy to remove newlines */ char *name = strdup(name2); char *pass = strdup(pass2); @@ -179,7 +233,7 @@ struct authinfo_t auth_check(const char *name2, const char *pass2) struct authinfo_t ret; ret.success = false; - ret.authlevel = -1; + ret.authlevel = PRIV_NONE; while(1) { @@ -202,7 +256,7 @@ struct authinfo_t auth_check(const char *name2, const char *pass2) free(line); - int hash_len = gcry_md_get_algo_dlen(ALGO); + unsigned 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); @@ -224,7 +278,7 @@ struct authinfo_t auth_check(const char *name2, const char *pass2) char *hex = malloc(hash_len * 2 + 1); char *ptr = hex; - for(int i = 0; i < hash_len; ++i, ptr += 2) + for(unsigned int i = 0; i < hash_len; ++i, ptr += 2) snprintf(ptr, 3, "%02x", newhash[i]); free(newhash); @@ -249,7 +303,7 @@ good: printf("Successful authentication.\n"); return ret; bad: + sleep(2); printf("Failed authentication.\n"); - sleep(1); return ret; } diff --git a/src/client.c b/src/client.c index b49498f..d836467 100644 --- a/src/client.c +++ b/src/client.c @@ -17,7 +17,9 @@ void __attribute__((format(printf,1,2))) out(const char *fmt, ...) char *client_read(void) { - char *buf = malloc(BUFSZ); + char *buf; +tryagain: + buf = malloc(BUFSZ); memset(buf, 0, BUFSZ); if(read(client_fd, buf, BUFSZ - 1) < 0) error("lost connection"); @@ -27,9 +29,27 @@ char *client_read(void) if(!memcmp(buf, ctrlc, sizeof(ctrlc))) exit(0); + printf("Read '%s'\n", buf); + if(buf[0] & 0x80) + { + free(buf); + goto tryagain; + } + return buf; } +void all_upper(char *s) +{ + while(*s) + { + *s = toupper(*s); + s++; + } +} + +#define WSPACE " \t\r\n" + void client_main(int fd, struct sockaddr_in *addr, int total) { client_fd = fd; @@ -50,17 +70,19 @@ void client_main(int fd, struct sockaddr_in *addr, int total) int authlevel; + char *current_user; + /* auth loop */ while(1) { out("login: "); - char *user = client_read(); + current_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); + struct authinfo_t auth = auth_check(current_user, pass); + memset(pass, 0, strlen(pass)); free(pass); + authlevel = auth.authlevel; if(auth.success) { @@ -69,26 +91,101 @@ void client_main(int fd, struct sockaddr_in *addr, int total) } else { + free(current_user); out("Access Denied.\n\n"); if(++failures >= MAX_FAILURES) return; } } + /* something has gone wrong, but we are here for some reason */ + if(authlevel == PRIV_NONE) + return; + + bool admin = (authlevel == PRIV_ADMIN); + /* authenticated */ while(1) { out(">> "); char *cmd = client_read(); - char *tok = strtok(cmd, " \t\r\n"); + char *save = NULL; + + char *tok = strtok_r(cmd, WSPACE, &save); + + all_upper(tok); - if(!strcmp(tok, "USER")) + if(admin) { - void change_user(const char *name2, const char *pass2, int level); - add_user("admin", "test", 0); + if(!strcmp(tok, "USER")) + { + char *what = strtok_r(NULL, WSPACE, &save); + all_upper(what); + + if(!strcmp(what, "DEL")) + { + char *user = strtok_r(NULL, WSPACE, &save); + if(user) + { + if(strcmp(user, current_user) && auth_remove(user)) + out("Success.\n"); + else + out("Failure.\n"); + } + else + { + out("Usage: USER DEL \n"); + } + } + else if(!strcmp(what, "ADD") || !strcmp(what, "PASS")) + { + char *user = strtok_r(NULL, WSPACE, &save); + if(user) + { + if(!strcmp(user, current_user)) + { + out("Do not modify your own password using USER. User CHPASS instead.\n"); + goto next_cmd; + } + + out("Editing user '%s'\n", user); + + out("New Password (_DO_NOT_USE_A_VALUABLE_PASSWORD_): "); + + /* BAD BAD BAD BAD BAD BAD BAD CLEARTEXT PASSWORDS!!! */ + char *pass = client_read(); + + out("Admin privileges [y/N]? "); + char *allow_admin = client_read(); + int priv = PRIV_USER; + if(toupper(allow_admin[0]) == 'Y') + priv = PRIV_ADMIN; + + if(add_change_user(user, pass, priv)) + out("Success.\n"); + else + out("Failure.\n"); + memset(pass, 0, strlen(pass)); + free(pass); + } + else + out("Usage: USER ADD|CHANGE \n"); + } + } } + if(!strcmp(tok, "QUIT")) + { + free(cmd); + goto done; + } + + next_cmd: + free(cmd); } + +done: + free(current_user); } diff --git a/src/netcosm.h b/src/netcosm.h index d7251ad..4f04e5b 100644 --- a/src/netcosm.h +++ b/src/netcosm.h @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -14,12 +15,17 @@ #include #include #include +#include #include #define USERFILE "users.dat" #define MAX_FAILURES 3 #define NETCOSM_VERSION "v0.1" +#define PRIV_NONE -1 +#define PRIV_USER 0 +#define PRIV_ADMIN 1337 + struct authinfo_t { bool success; const char *user; @@ -30,3 +36,7 @@ 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*); + +/* add or change a user */ +bool add_change_user(const char *user2, const char *pass2, int level); +bool auth_remove(const char*); diff --git a/src/server.c b/src/server.c index e717cfe..39719eb 100644 --- a/src/server.c +++ b/src/server.c @@ -37,6 +37,7 @@ int num_clients = 0; void sigchld_handler(int s) { + (void) s; printf("Client disconnect.\n"); // waitpid() might overwrite errno, so we save and restore it: int saved_errno = errno; @@ -67,6 +68,7 @@ void serv_cleanup(void) void sigint_handler(int sig) { + (void) sig; serv_cleanup(); } -- cgit v1.1