From c920d34e2361f924c1339a5591d57b026f7a6077 Mon Sep 17 00:00:00 2001 From: thematdev Date: Fri, 14 Jul 2023 21:44:39 +0300 Subject: [PATCH] Added config support via libconfig --- Makefile | 2 +- redis_create_user.sh | 22 ++++++++++++++ src/auth.h | 3 ++ src/config.c | 36 +++++++++++++++++++++++ src/config.h | 56 ++++++++++++++++++++++++++--------- src/new_client.c | 28 ++++++++++++++---- src/redis_auth.c | 39 +++++++++++++++++++----- src/utils.c | 2 +- src/view_comments.c | 70 +++++++++++++++++++++++++++++++------------- 9 files changed, 208 insertions(+), 50 deletions(-) create mode 100755 redis_create_user.sh create mode 100644 src/config.c diff --git a/Makefile b/Makefile index 782a8d9..f52e7b8 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ LIBS ?= $(wildcard $(LIBS_DIR)/*) CGI_EXECS ?= view_comments.cgi new_client.cgi EXEC_TARGETS := $(patsubst %.cgi,$(BD)/%.cgi,$(CGI_EXECS)) -LDFLAGS ?= -L$(BD)/ -lcgic -lqments -lhiredis -ltomcrypt -luuid +LDFLAGS ?= -L$(BD)/ -lconfig -lcgic -lqments -lhiredis -ltomcrypt -luuid SRC_FILES := $(shell find $(SD) -name *.c) EXEC_OBJS := $(patsubst %.cgi,$(BD)/%.o,$(CGI_EXECS)) diff --git a/redis_create_user.sh b/redis_create_user.sh new file mode 100755 index 0000000..e9a799a --- /dev/null +++ b/redis_create_user.sh @@ -0,0 +1,22 @@ +die() { + echo "$@" >&2 + exit 1 +} + +magic='$z$' +redis_socket=$1 +read -p "Username: " username +read -s -p "Password: " password +echo +read -s -p "Confirm your password: " password_conf +echo +if [[ $password != $password_conf ]]; then + die "Passwords don't match" +fi + +salt=$(tr -dc A-Za-z0-9 +#include + +config_t config; + +int +try_preload_config(const char *filename) +{ + if (access(filename, F_OK) != 0) { + return CFG_PLD_NOT_FOUND; + } + if (config_read_file(&config, filename) == CONFIG_FALSE) { + return CFG_PLD_INVALID; + } + return CFG_PLD_OK; +} + +int +preload_config(const char *filename) +{ + char **filename_ptr = config_preload_paths; + int err; + if (filename) { + err = try_preload_config(filename); + if (err != CFG_PLD_NOT_FOUND) { + return err; + } + } + while (filename_ptr && (err = try_preload_config(*filename_ptr++))) { + if (err != CFG_PLD_NOT_FOUND) { + return err; + } + } +} diff --git a/src/config.h b/src/config.h index 88d0e2c..fedd27d 100644 --- a/src/config.h +++ b/src/config.h @@ -1,20 +1,48 @@ -#define RA_USER_MAX_LENGTH 64 -#define RA_PASSWORD_MAX_LENGTH 256 +#ifndef CONFIG_H +#define CONFIG_H -#define RA_SESSION_EXPIRE 86400 +#include "drivers/unix_fs/unix_fs_driver.h" +#include "driver.h" -#define RA_CONNECTION_TIMEOUT {1, 500000} -#define RA_HOSTNAME "ra-socket" +#include +#include -#define RA_MAGIC "$z$" +enum ConfigPreloadError { + CFG_PLD_INVALID = -2, + CFG_PLD_NOT_FOUND, + CFG_PLD_OK +}; -#define COMMENTS_PER_PAGE 20 -#define MAX_NAME_SIZE 1024 -#define MAX_COMMENT_SIZE 4096 +static char *config_preload_paths[] = {"discuss.conf", "/etc/discuss.conf", NULL}; +extern config_t config; -#define QMENTS_PATH "qments-storage" -#define DRIVER_DATA { QMENTS_PATH } -#define DRIVER unix_fs_driver +/* first tries to load config `filename`, then tries preload_paths in order + * on first invalid config returns CFG_PLD_INVALID, + * if all paths do not exist returns CFG_PLD_NOT_FOUND + * if filename is NULL, checks only preload_paths + */ +int preload_config(const char *filename); -#define SESSION_COOKIE_NAME "thecookie" -#define HOSTNAME "localhost" +#pragma GCC diagnostic ignored "-Wincompatible-pointer-types" +#define CONF_ROOT_LOOKUP(type, name) config_lookup_##type(&config, #name, &name) + +/* defaults */ +static int comments_per_page = 20; + +static int max_name_size = 1024; +static int max_comment_size = 4096; + +static int user_max_length = 64; +static int password_max_length = 256; + +static int session_expire = 86400; + +static char *qments_path = "qments-storage"; +static UnixFsDriverData driver_data; +static Driver driver = unix_fs_driver; + +static char *session_cookie_name = "thecookie"; +static char *hostname = "localhost"; + + +#endif /* CONFIG_H */ diff --git a/src/new_client.c b/src/new_client.c index 3227777..1fd5576 100644 --- a/src/new_client.c +++ b/src/new_client.c @@ -4,6 +4,8 @@ #include "utils.h" #include +#include +#include #define UUID_SIZE 37 @@ -11,12 +13,12 @@ char * validate_credentials() { - char username[RA_USER_MAX_LENGTH + 1], password[RA_PASSWORD_MAX_LENGTH + 1]; + char username[user_max_length + 1], password[password_max_length + 1]; char session_id[UUID_SIZE]; cgiFormResultType err; int auth; - err = cgiFormString("username", username, RA_USER_MAX_LENGTH + 1); + err = cgiFormString("username", username, user_max_length + 1); if (err == cgiFormTruncated) { return "Username too long\n"; } @@ -24,7 +26,7 @@ validate_credentials() return "Username not provided\n"; } - err = cgiFormString("password", password, RA_PASSWORD_MAX_LENGTH + 1); + err = cgiFormString("password", password, password_max_length + 1); if (err == cgiFormTruncated) { return "Password too long\n"; } @@ -46,7 +48,7 @@ validate_credentials() } if (auth) { - cgiHeaderCookieSet(SESSION_COOKIE_NAME, session_id, RA_SESSION_EXPIRE, "/", HOSTNAME, 0); + cgiHeaderCookieSet(session_cookie_name, session_id, session_expire, "/", hostname, 0); return "You've successfully logged in!\n"; } else { return "Failed to log in, check credentials\n"; @@ -68,7 +70,24 @@ print_login_form() int cgiMain() { + int cfg_pld_err; char *message; + + config_init(&config); + cfg_pld_err = preload_config((cgiArgc > 1 ? cgiArgv[1] : NULL)); + + if (cfg_pld_err == CFG_PLD_INVALID) { + syslog(LOG_ERR, "Got invalid config\n"); + syslog(LOG_ERR, "%s\n", config_error_text(&config)); + exit(EXIT_FAILURE); + } else if (cfg_pld_err == CFG_PLD_NOT_FOUND) { + syslog(LOG_WARNING, "Config not found, loading defaults\n"); + } else { + load_auth_config(); + } + + CONF_ROOT_LOOKUP(int, session_expire); + if (cgiFormSubmitClicked("login") == cgiFormSuccess && !strcmp(cgiRequestMethod, "POST")) { message = validate_credentials(); @@ -76,7 +95,6 @@ cgiMain() message = ""; } - cgiHeaderContentType("text/html; charset=utf-8"); fprintf(cgiOut, "\n"); diff --git a/src/redis_auth.c b/src/redis_auth.c index 2c29a59..df2dbde 100644 --- a/src/redis_auth.c +++ b/src/redis_auth.c @@ -16,13 +16,33 @@ goto defer; \ } while (0) +/* redis auth defaults */ +static struct timeval ra_connection_timeout = {1, 500000}; /* 1.5 secs */ + +static char *ra_hostname = "ra-socket"; +static char *ra_magic = "$z$"; + + +int +load_auth_config() +{ + CONF_ROOT_LOOKUP(string, ra_hostname); + CONF_ROOT_LOOKUP(string, ra_magic); + + CONF_ROOT_LOOKUP(int, user_max_length); + CONF_ROOT_LOOKUP(int, password_max_length); + + CONF_ROOT_LOOKUP(int, session_expire); + + return 0; +} + int user_by_session_id(const char *session_id, char *username) { redisContext *ctx; redisReply *reply; uuid_t uu_tmp; - struct timeval timeout = RA_CONNECTION_TIMEOUT; int retval = 0; ctx = NULL; @@ -33,7 +53,7 @@ user_by_session_id(const char *session_id, char *username) goto defer; } - ctx = redisConnectUnixWithTimeout(RA_HOSTNAME, timeout); + ctx = redisConnectUnixWithTimeout(ra_hostname, ra_connection_timeout); if (!ctx || ctx->err) { retval = -EIO; @@ -44,6 +64,10 @@ user_by_session_id(const char *session_id, char *username) if (reply->type != REDIS_REPLY_STRING) { strcpy(username, ""); } else { + if (reply->len > user_max_length + 1) { + retval = -EINVAL; + goto defer; + } strcpy(username, reply->str); } @@ -62,18 +86,17 @@ authenticate(const char *username, const char *password, char *session_id) unsigned char db_hash[SHA512_DIGEST_SIZE], got_hash[SHA512_DIGEST_SIZE]; uuid_t uu_tmp; hash_state md; - struct timeval timeout = RA_CONNECTION_TIMEOUT; int retval = 0; ctx = NULL; salt_reply = hash_reply = sid_reply = NULL; - if (strlen(username) > RA_USER_MAX_LENGTH || strlen(password) > RA_PASSWORD_MAX_LENGTH) { + if (strlen(username) > user_max_length || strlen(password) > password_max_length) { retval = -EINVAL; goto defer; } - ctx = redisConnectUnixWithTimeout(RA_HOSTNAME, timeout); + ctx = redisConnectUnixWithTimeout(ra_hostname, ra_connection_timeout); if (!ctx || ctx->err) { retval = -EIO; @@ -96,7 +119,7 @@ authenticate(const char *username, const char *password, char *session_id) sha512_init(&md); - sha512_process(&md, RA_MAGIC, strlen(RA_MAGIC)); + sha512_process(&md, ra_magic, strlen(ra_magic)); sha512_process(&md, password, strlen(password)); sha512_process(&md, salt_reply->str, strlen(salt_reply->str)); @@ -120,9 +143,9 @@ authenticate(const char *username, const char *password, char *session_id) } else { strcpy(session_id, sid_reply->str); } - sid_reply = redisCommand(ctx, "EXPIRE session:%s %d", username, RA_SESSION_EXPIRE); + sid_reply = redisCommand(ctx, "EXPIRE session:%s %d", username, session_expire); freeReplyObject(sid_reply); - sid_reply = redisCommand(ctx, "EXPIRE username:%s %d", session_id, RA_SESSION_EXPIRE); + sid_reply = redisCommand(ctx, "EXPIRE username:%s %d", session_id, session_expire); retval = 1; } diff --git a/src/utils.c b/src/utils.c index 1f03cf9..015aaea 100644 --- a/src/utils.c +++ b/src/utils.c @@ -5,7 +5,7 @@ #include #include -#define VALID_FOR_USERNAME(c) (isalpha(c) || c == '_') +#define VALID_FOR_USERNAME(c) (isalpha(c) || isdigit(c) || c == '_') int contain_special(const char *s) diff --git a/src/view_comments.c b/src/view_comments.c index 93c298d..d4ac744 100644 --- a/src/view_comments.c +++ b/src/view_comments.c @@ -1,25 +1,26 @@ +#include "config.h" #include "string_buffer.h" #include "utils.h" #include "comment.h" #include "cgic.h" #include "driver.h" #include "drivers/unix_fs/unix_fs_driver.h" -#include "config.h" #include "auth.h" #include #include #include #include +#include #define UUID_SIZE 37 -char authorized_user_sid[RA_USER_MAX_LENGTH + 1], session_id[UUID_SIZE]; +char session_id[UUID_SIZE], *authorized_user_sid; int page_by_id(int id) { - return id / COMMENTS_PER_PAGE + 1; + return id / comments_per_page + 1; } void @@ -50,8 +51,8 @@ render_comment(const Comment *comment) void allocate_header(CommentHeader *header) { - header->user_sid = malloc(MAX_NAME_SIZE); - header->user_displayname = malloc(MAX_NAME_SIZE); + header->user_sid = malloc(max_name_size); + header->user_displayname = malloc(max_name_size); } void @@ -120,8 +121,6 @@ print_page() int page, max_id; int id_begin, id_end, i; Comment comment; - UnixFsDriverData driver_data = DRIVER_DATA; - Driver driver = DRIVER; cgiFormInteger("page", &page, 0); if ((max_id = unix_fs_driver_get_max_id(&driver_data)) < 0) { @@ -131,18 +130,18 @@ print_page() if (page <= 0) { id_end = max_id; - id_begin = (max_id > COMMENTS_PER_PAGE ? max_id - COMMENTS_PER_PAGE : 1); + id_begin = (max_id > comments_per_page ? max_id - comments_per_page : 1); } else { - /* max_id > COMMENTS_PER_PAGE * (page - 1) + 1 - * max_id - 1 > COMMENTS_PER_PAGE * (page - 1) + /* max_id > comments_per_page * (page - 1) + 1 + * max_id - 1 > comments_per_page * (page - 1) * perform this check without overflow */ - if ((max_id + COMMENTS_PER_PAGE - 1) / COMMENTS_PER_PAGE <= (page - 1)) { + if ((max_id + comments_per_page - 1) / comments_per_page <= (page - 1)) { fprintf(cgiOut, "There is no such page\n"); return; } - id_begin = COMMENTS_PER_PAGE * (page - 1) + 1; - id_end = id_begin + COMMENTS_PER_PAGE; + id_begin = comments_per_page * (page - 1) + 1; + id_end = id_begin + comments_per_page; if (id_end > max_id) { id_end = max_id; } @@ -162,16 +161,14 @@ print_page() int handle_submitted_comment() { - char displayname[MAX_NAME_SIZE], text[MAX_COMMENT_SIZE], *sanitized_text; + char displayname[max_name_size], text[max_comment_size], *sanitized_text; int rid, retval; cgiFormResultType err; CommentHeader header; - Driver driver = DRIVER; - UnixFsDriverData driver_data = DRIVER_DATA; - err = cgiFormString("text", text, MAX_COMMENT_SIZE); + err = cgiFormString("text", text, max_comment_size); if (err == cgiFormTruncated) { - fprintf(cgiOut, "Comment too long (max %d bytes)\n", MAX_COMMENT_SIZE); + fprintf(cgiOut, "Comment too long (max %d bytes)\n", max_comment_size); goto defer; } if (err == cgiFormNotFound) { @@ -179,9 +176,9 @@ handle_submitted_comment() goto defer; } - err = cgiFormString("displayname", displayname, MAX_NAME_SIZE); + err = cgiFormString("displayname", displayname, max_name_size); if (err == cgiFormTruncated) { - fprintf(cgiOut, "Name too long (max %d bytes)\n", MAX_NAME_SIZE); + fprintf(cgiOut, "Name too long (max %d bytes)\n", max_name_size); goto defer; } if (err == cgiFormNotFound) { @@ -224,7 +221,35 @@ defer: int cgiMain() { - if (cgiCookieString(SESSION_COOKIE_NAME, session_id, UUID_SIZE) == cgiFormSuccess) { + int cfg_pld_err; + config_init(&config); + cfg_pld_err = preload_config((cgiArgc > 1 ? cgiArgv[1] : NULL)); + + if (cfg_pld_err == CFG_PLD_INVALID) { + syslog(LOG_ERR, "Got invalid config\n"); + syslog(LOG_ERR, "%s\n", config_error_text(&config)); + exit(EXIT_FAILURE); + } else if (cfg_pld_err == CFG_PLD_NOT_FOUND) { + syslog(LOG_WARNING, "Config not found, loading defaults\n"); + } else { + load_auth_config(); + } + + CONF_ROOT_LOOKUP(int, comments_per_page); + CONF_ROOT_LOOKUP(int, max_name_size); + CONF_ROOT_LOOKUP(int, max_comment_size); + + CONF_ROOT_LOOKUP(int, user_max_length); + CONF_ROOT_LOOKUP(int, password_max_length); + + CONF_ROOT_LOOKUP(string, qments_path); + CONF_ROOT_LOOKUP(string, session_cookie_name); + CONF_ROOT_LOOKUP(string, hostname); + + driver_data.path = qments_path; + + authorized_user_sid = malloc(user_max_length + 1); + if (cgiCookieString(session_cookie_name, session_id, UUID_SIZE) == cgiFormSuccess) { user_by_session_id(session_id, authorized_user_sid); } else { authorized_user_sid[0] = '\0'; @@ -253,6 +278,9 @@ cgiMain() fprintf(cgiOut, "\n"); fprintf(cgiOut, "\n"); + + free(authorized_user_sid); + config_destroy(&config); return 0; }