From 8d3718c9aef08ff918b2d8446ac5713b20de3596 Mon Sep 17 00:00:00 2001 From: thematdev Date: Wed, 5 Jul 2023 14:43:56 +0300 Subject: [PATCH] Added login page and fixed some bugs --- Makefile | 5 ++- src/config.h | 4 +- src/new_client.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++ src/redis_auth.c | 6 +-- src/utils.c | 35 +++++++++++++++-- src/utils.h | 3 ++ 6 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 src/new_client.c diff --git a/Makefile b/Makefile index d8d1461..782a8d9 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ SD ?= src LIBS_DIR ?= libs LIBS ?= $(wildcard $(LIBS_DIR)/*) -CGI_EXECS ?= view_comments.cgi +CGI_EXECS ?= view_comments.cgi new_client.cgi EXEC_TARGETS := $(patsubst %.cgi,$(BD)/%.cgi,$(CGI_EXECS)) LDFLAGS ?= -L$(BD)/ -lcgic -lqments -lhiredis -ltomcrypt -luuid @@ -27,13 +27,14 @@ $(BD)/libqments.a: cp $(LIBS_DIR)/qments/$(BD)/libqments.a $(BD) $(BD)/%.cgi: $(BD)/%.o $(OBJ_FILES) + mkdir -p $(@D) $(CC) $(INCLUDE_DIRS) $(CFLAGS) $^ -o $@ $(LDFLAGS) $(BD)/%.o: $(SD)/%.c $(LIB_TARGETS) mkdir -p $(@D) $(CC) $(INCLUDE_DIRS) $(CFLAGS) -c $< -o $@ $(LDFLAGS) -all: $(BD)/$(CGI_EXECS) +all: $(EXEC_TARGETS) clean: rm -rfI $(BD)/ diff --git a/src/config.h b/src/config.h index 1838a90..88d0e2c 100644 --- a/src/config.h +++ b/src/config.h @@ -1,7 +1,7 @@ #define RA_USER_MAX_LENGTH 64 #define RA_PASSWORD_MAX_LENGTH 256 -#define RA_SESSION_EXPIRY 86400 +#define RA_SESSION_EXPIRE 86400 #define RA_CONNECTION_TIMEOUT {1, 500000} #define RA_HOSTNAME "ra-socket" @@ -16,3 +16,5 @@ #define DRIVER_DATA { QMENTS_PATH } #define DRIVER unix_fs_driver +#define SESSION_COOKIE_NAME "thecookie" +#define HOSTNAME "localhost" diff --git a/src/new_client.c b/src/new_client.c new file mode 100644 index 0000000..2390a7b --- /dev/null +++ b/src/new_client.c @@ -0,0 +1,98 @@ +#include "cgic.h" +#include "auth.h" +#include "config.h" +#include "utils.h" + +#define UUID_SIZE 36 + +/* TODO: place cookie normally */ +void +validate_credentials() +{ + char username[RA_USER_MAX_LENGTH + 1], password[RA_PASSWORD_MAX_LENGTH + 1]; + char session_id[UUID_SIZE]; + cgiFormResultType err; + int auth; + + err = cgiFormString("username", username, RA_USER_MAX_LENGTH + 1); + if (err == cgiFormTruncated) { + fprintf(cgiOut, "Username too long(max %d chars)\n", RA_USER_MAX_LENGTH); + return; + } + if (err == cgiFormNotFound) { + fprintf(cgiOut, "Username not provided\n"); + return; + } + + err = cgiFormString("password", password, RA_PASSWORD_MAX_LENGTH + 1); + if (err == cgiFormTruncated) { + fprintf(cgiOut, "Password too long(max %d+1(NUL) bytes)\n", RA_PASSWORD_MAX_LENGTH); + return; + } + if (err == cgiFormNotFound) { + fprintf(cgiOut, "Password not provided\n"); + return; + } + + if (!is_valid_username(username)) { + fprintf(cgiOut, "Username must be [A-Za-z0-9_]\n"); + return; + } + + if (!is_valid_password(password)) { + fprintf(cgiOut, "Password must be a sequence of bytes in range 32-255\n"); + return; + } + + auth = authenticate(username, password, session_id); + if (auth < 0) { + fprintf(cgiOut, "Some error occured, contact system administrator\n"); + return; + } + + if (auth) { + cgiHeaderCookieSet(SESSION_COOKIE_NAME, session_id, RA_SESSION_EXPIRE, "/", HOSTNAME, 0); + fprintf(cgiOut, "You've successfully logged in as %s\n", username); + } else { + fprintf(cgiOut, "Failed to log in, check credentials\n"); + } +} + +void +print_login_form() +{ + fputs("
\n" + "\n" + "
\n" + "\n" + "
\n" + "\n" + "
\n", cgiOut); +} + +int +cgiMain() +{ + cgiHeaderContentType("text/html"); + + fprintf(cgiOut, "\n"); + + fprintf(cgiOut, "\n"); + fprintf(cgiOut, " Simple discuss powered by qments \n"); + fprintf(cgiOut, "\n"); + + fprintf(cgiOut, "\n"); + + if (cgiFormSubmitClicked("login") == cgiFormSuccess) { + validate_credentials(); + } + + print_login_form(); + + fprintf(cgiOut, "\n"); + + fprintf(cgiOut, "\n"); + + return 0; + +} diff --git a/src/redis_auth.c b/src/redis_auth.c index c62c797..6e640a1 100644 --- a/src/redis_auth.c +++ b/src/redis_auth.c @@ -105,7 +105,7 @@ authenticate(const char *username, const char *password, char *session_id) if (memcmp(db_hash, got_hash, SHA512_DIGEST_SIZE)) { FAIL_AUTH(); } else { - redisCommand(ctx, "GET session:%s", username); + sid_reply = redisCommand(ctx, "GET session:%s", username); if (sid_reply->type != REDIS_REPLY_STRING) { /* assuming there is no session for this user */ freeReplyObject(sid_reply); @@ -115,11 +115,10 @@ authenticate(const char *username, const char *password, char *session_id) sid_reply = redisCommand(ctx, "SET session:%s %s", username, session_id); freeReplyObject(sid_reply); - - sid_reply = redisCommand(ctx, "EXPIRY session:%s %d", username, RA_SESSION_EXPIRY); } else { strcpy(session_id, sid_reply->str); } + sid_reply = redisCommand(ctx, "EXPIRE session:%s %d", username, RA_SESSION_EXPIRE); retval = 1; } @@ -128,4 +127,5 @@ defer: freeReplyObject(salt_reply); freeReplyObject(hash_reply); freeReplyObject(sid_reply); + return retval; } diff --git a/src/utils.c b/src/utils.c index 93285f6..1f03cf9 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,15 +1,44 @@ -#include -#include - #include "string_buffer.h" #include "utils.h" +#include +#include +#include + +#define VALID_FOR_USERNAME(c) (isalpha(c) || c == '_') + int contain_special(const char *s) { return strchr(s, '&') || strchr(s, '"') || strchr(s, '\'') || strchr(s, '<') || strchr(s, '>'); } +int +is_valid_username(const char *s) +{ + char c; + while ((c = *s++)) { + if (!VALID_FOR_USERNAME(c)) { + return 0; + } + } + return 1; +} + +int +is_valid_password(const char *s) +{ + char c; + unsigned char b; + while ((c = *s++)) { + b = (unsigned char) c; + if (b < 32) { + return 0; + } + } + return 1; +} + char * mk_specialchars(const char *input) { diff --git a/src/utils.h b/src/utils.h index 9fa5502..d6d1329 100644 --- a/src/utils.h +++ b/src/utils.h @@ -3,6 +3,9 @@ int contain_special(const char *s); +int is_valid_username(const char *s); +int is_valid_password(const char *s); + char *mk_specialchars(const char *input); #endif /* UTILS_H */