From 140738e4e8c6afa2b2b8c20ff07d69de6562314f Mon Sep 17 00:00:00 2001 From: thematdev Date: Tue, 4 Jul 2023 18:39:14 +0300 Subject: [PATCH] Added redis auth module In order to use hiredis it is needed to move from ANSI C to C99 or gnu89. For now redis auth code is untested it's just first compiling prototype --- Makefile | 5 +- src/auth.h | 12 ++++ src/config.h | 18 ++++++ src/main.c | 19 ++----- src/redis_auth.c | 131 ++++++++++++++++++++++++++++++++++++++++++++ src/string_buffer.c | 2 +- 6 files changed, 171 insertions(+), 16 deletions(-) create mode 100644 src/auth.h create mode 100644 src/config.h create mode 100644 src/redis_auth.c diff --git a/Makefile b/Makefile index fadd28f..6f13331 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ SD ?= src LIBS_DIR ?= libs LIBS ?= $(wildcard $(LIBS_DIR)/*) -LDFLAGS ?= -L$(BD)/ -lcgic -lqments +LDFLAGS ?= -L$(BD)/ -lcgic -lqments -lhiredis -ltomcrypt -luuid SRC_FILES := $(shell find $(SD) -name *.c) OBJ_FILES := $(patsubst $(SD)/%.c,$(BD)/%.o,$(SRC_FILES)) @@ -13,10 +13,11 @@ OBJ_FILES := $(patsubst $(SD)/%.c,$(BD)/%.o,$(SRC_FILES)) INCLUDE_DIRS = -Ilibs/qments/include -Ilibs/cgic LIB_TARGETS = $(patsubst $(LIBS_DIR)/%,$(BD)/lib%.a,$(LIBS)) -CFLAGS ?= -O2 -ansi -pedantic +CFLAGS ?= -O2 -std=c99 -pedantic $(BD)/libcgic.a: $(MAKE) -C $(LIBS_DIR)/cgic + mkdir -p $(@D) cp $(LIBS_DIR)/cgic/libcgic.a $(BD) $(BD)/libqments.a: diff --git a/src/auth.h b/src/auth.h new file mode 100644 index 0000000..1e02bbc --- /dev/null +++ b/src/auth.h @@ -0,0 +1,12 @@ +#ifndef AUTH_H +#define AUTH_H + +/* return 0 on success, negative number on error + * if session_id is not associated with any user, empty + * string is put in username */ +int user_by_session_id(const char *session_id, char *username); + +/* return 0 on failure, 1 on success, negative number on error */ +int authenticate(const char *username, const char *password, char *sid); + +#endif /* AUTH_H */ diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..1838a90 --- /dev/null +++ b/src/config.h @@ -0,0 +1,18 @@ +#define RA_USER_MAX_LENGTH 64 +#define RA_PASSWORD_MAX_LENGTH 256 + +#define RA_SESSION_EXPIRY 86400 + +#define RA_CONNECTION_TIMEOUT {1, 500000} +#define RA_HOSTNAME "ra-socket" + +#define RA_MAGIC "$z$" + +#define COMMENTS_PER_PAGE 20 +#define MAX_NAME_SIZE 1024 +#define MAX_COMMENT_SIZE 4096 + +#define QMENTS_PATH "qments-storage" +#define DRIVER_DATA { QMENTS_PATH } +#define DRIVER unix_fs_driver + diff --git a/src/main.c b/src/main.c index bf89d06..41e514f 100644 --- a/src/main.c +++ b/src/main.c @@ -4,19 +4,12 @@ #include "cgic.h" #include "driver.h" #include "drivers/unix_fs/unix_fs_driver.h" +#include "config.h" #include #include #include -#define COMMENTS_PER_PAGE 20 -#define MAX_NAME 1024 -#define MAX_COMMENT_SIZE 4096 - -#define QMENTS_PATH "qments-storage" -#define DRIVER_DATA { QMENTS_PATH } -#define DRIVER unix_fs_driver - int page_by_id(int id) { @@ -51,8 +44,8 @@ render_comment(const Comment *comment) void allocate_header(CommentHeader *header) { - header->user_sid = malloc(MAX_NAME); - header->user_displayname = malloc(MAX_NAME); + header->user_sid = malloc(MAX_NAME_SIZE); + header->user_displayname = malloc(MAX_NAME_SIZE); } void @@ -163,7 +156,7 @@ print_page() int handle_submitted_comment() { - char displayname[MAX_NAME], text[MAX_COMMENT_SIZE], *sanitized_text; + char displayname[MAX_NAME_SIZE], text[MAX_COMMENT_SIZE], *sanitized_text; int rid, retval; cgiFormResultType err; CommentHeader header; @@ -180,9 +173,9 @@ handle_submitted_comment() goto defer; } - err = cgiFormString("displayname", displayname, MAX_NAME); + err = cgiFormString("displayname", displayname, MAX_NAME_SIZE); if (err == cgiFormTruncated) { - fprintf(cgiOut, "Name too long (max %d bytes)\n", MAX_NAME); + fprintf(cgiOut, "Name too long (max %d bytes)\n", MAX_NAME_SIZE); goto defer; } if (err == cgiFormNotFound) { diff --git a/src/redis_auth.c b/src/redis_auth.c new file mode 100644 index 0000000..c62c797 --- /dev/null +++ b/src/redis_auth.c @@ -0,0 +1,131 @@ +#include "auth.h" +#include "config.h" + +#include +#include +#include +#include +#include + +#define UUID_SIZE 36 +#define SHA512_DIGEST_SIZE 64 + +#define FAIL_AUTH() do { \ + strcpy(session_id, ""); \ + retval = 0; \ + goto defer; \ +} while (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; + reply = NULL; + + if (uuid_parse(session_id, uu_tmp) < 0) { + retval = -EINVAL; + goto defer; + } + + ctx = redisConnectUnixWithTimeout(RA_HOSTNAME, timeout); + + if (!ctx || ctx->err) { + retval = -EIO; + goto defer; + } + + reply = redisCommand(ctx, "GET session:%s", session_id); + if (reply->type != REDIS_REPLY_STRING) { + strcpy(username, ""); + } else { + strcpy(username, reply->str); + } + +defer: + redisFree(ctx); + freeReplyObject(reply); + return retval; +} + +int +authenticate(const char *username, const char *password, char *session_id) +{ + redisContext *ctx; + redisReply *salt_reply, *hash_reply, *sid_reply; + char *sid; + 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) { + retval = -EINVAL; + goto defer; + } + + ctx = redisConnectUnixWithTimeout(RA_HOSTNAME, timeout); + + if (!ctx || ctx->err) { + retval = -EIO; + goto defer; + } + + salt_reply = redisCommand(ctx, "GET salt:%s", username); + + if (salt_reply->type != REDIS_REPLY_STRING) { + FAIL_AUTH(); + } + + hash_reply = redisCommand(ctx, "GET hash:%s", username); + + if (hash_reply->type != REDIS_REPLY_STRING || hash_reply->len != SHA512_DIGEST_SIZE) { + FAIL_AUTH(); + } + + memcpy(db_hash, hash_reply->str, SHA512_DIGEST_SIZE); + + sha512_init(&md); + + sha512_process(&md, RA_MAGIC, strlen(RA_MAGIC)); + sha512_process(&md, password, strlen(password)); + sha512_process(&md, salt_reply->str, strlen(salt_reply->str)); + + sha512_done(&md, got_hash); + + if (memcmp(db_hash, got_hash, SHA512_DIGEST_SIZE)) { + FAIL_AUTH(); + } else { + redisCommand(ctx, "GET session:%s", username); + if (sid_reply->type != REDIS_REPLY_STRING) { + /* assuming there is no session for this user */ + freeReplyObject(sid_reply); + + uuid_generate(uu_tmp); + uuid_unparse(uu_tmp, 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); + } + retval = 1; + } + +defer: + redisFree(ctx); + freeReplyObject(salt_reply); + freeReplyObject(hash_reply); + freeReplyObject(sid_reply); +} diff --git a/src/string_buffer.c b/src/string_buffer.c index 0aa16a4..25da5ac 100644 --- a/src/string_buffer.c +++ b/src/string_buffer.c @@ -50,5 +50,5 @@ sb_init_empty(StringBuffer *buffer) void sb_free(StringBuffer *buffer) { - free(buffer->buf); + if (buffer) free(buffer->buf); }